Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,36 @@
project(qt_common)
QT6_ADD_RESOURCES(RESOURCES res/resources_common.qrc)
set_property(SOURCE qrc_resources_common.cpp PROPERTY SKIP_AUTOGEN ON)
set(SRC
helpers.cpp
helpers.hpp
map_widget.cpp
map_widget.hpp
proxy_style.cpp
proxy_style.hpp
qtoglcontext.cpp
qtoglcontext.hpp
qtoglcontextfactory.cpp
qtoglcontextfactory.hpp
scale_slider.cpp
scale_slider.hpp
spinner.cpp
spinner.hpp
text_dialog.cpp
text_dialog.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC} ${RESOURCES})
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOMOC ON)
target_link_libraries(${PROJECT_NAME}
map
Qt6::Gui
Qt6::Widgets
Qt6::OpenGL
Qt6::OpenGLWidgets
)

96
qt/qt_common/helpers.cpp Normal file
View file

@ -0,0 +1,96 @@
#include "qt/qt_common/helpers.hpp"
#include "geometry/mercator.hpp"
#include "base/logging.hpp"
#include <QtCore/QDateTime>
#include <QtGui/QSurfaceFormat>
namespace qt::common
{
bool IsLeftButton(Qt::MouseButtons buttons)
{
return buttons & Qt::LeftButton;
}
bool IsLeftButton(QMouseEvent const * const e)
{
return IsLeftButton(e->button()) || IsLeftButton(e->buttons());
}
bool IsRightButton(Qt::MouseButtons buttons)
{
return buttons & Qt::RightButton;
}
bool IsRightButton(QMouseEvent const * const e)
{
return IsRightButton(e->button()) || IsRightButton(e->buttons());
}
bool IsCommandModifier(QMouseEvent const * const e)
{
return e->modifiers() & Qt::ControlModifier;
}
bool IsShiftModifier(QMouseEvent const * const e)
{
return e->modifiers() & Qt::ShiftModifier;
}
bool IsAltModifier(QMouseEvent const * const e)
{
return e->modifiers() & Qt::AltModifier;
}
location::GpsInfo MakeGpsInfo(m2::PointD const & point)
{
location::GpsInfo info;
info.m_source = location::EUser;
info.m_latitude = mercator::YToLat(point.y);
info.m_longitude = mercator::XToLon(point.x);
info.m_horizontalAccuracy = 10;
info.m_timestamp = QDateTime::currentMSecsSinceEpoch() / 1000.0;
return info;
}
void SetDefaultSurfaceFormat(QString const & platformName)
{
QSurfaceFormat fmt;
fmt.setAlphaBufferSize(8);
fmt.setBlueBufferSize(8);
fmt.setGreenBufferSize(8);
fmt.setRedBufferSize(8);
fmt.setStencilBufferSize(0);
fmt.setSamples(0);
fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
fmt.setSwapInterval(1);
fmt.setDepthBufferSize(16);
#if defined(OMIM_OS_LINUX)
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
#endif
// Set proper OGL version now (needed for "cocoa" or "xcb"), but have troubles with "wayland" devices.
// It will be resolved later in MapWidget::initializeGL when OGL context is available.
if (platformName != QString("wayland"))
{
#if defined(OMIM_OS_LINUX)
LOG(LINFO, ("Set default OpenGL version to ES 3.0"));
fmt.setVersion(3, 0);
#else
LOG(LINFO, ("Set default OGL version to 3.2"));
fmt.setProfile(QSurfaceFormat::CoreProfile);
fmt.setVersion(3, 2);
#endif
}
#ifdef ENABLE_OPENGL_DIAGNOSTICS
fmt.setOption(QSurfaceFormat::DebugContext);
#endif
QSurfaceFormat::setDefaultFormat(fmt);
}
} // namespace qt::common

31
qt/qt_common/helpers.hpp Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "platform/location.hpp"
#include <QtGui/QMouseEvent>
namespace qt::common
{
bool IsLeftButton(Qt::MouseButtons buttons);
bool IsLeftButton(QMouseEvent const * const e);
bool IsRightButton(Qt::MouseButtons buttons);
bool IsRightButton(QMouseEvent const * const e);
bool IsCommandModifier(QMouseEvent const * const e);
bool IsShiftModifier(QMouseEvent const * const e);
bool IsAltModifier(QMouseEvent const * const e);
struct Hotkey
{
Hotkey() = default;
Hotkey(QKeySequence const & key, char const * slot) : m_key(key), m_slot(slot) {}
QKeySequence m_key = 0;
char const * m_slot = nullptr;
};
location::GpsInfo MakeGpsInfo(m2::PointD const & point);
void SetDefaultSurfaceFormat(QString const & platformName);
} // namespace qt::common

522
qt/qt_common/map_widget.cpp Normal file
View file

@ -0,0 +1,522 @@
#include "qt/qt_common/map_widget.hpp"
#include "qt/qt_common/helpers.hpp"
#include "qt/qt_common/scale_slider.hpp"
#include "map/framework.hpp"
#include "routing/maxspeeds.hpp"
#include "geometry/point2d.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include <functional>
#include <string>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QTouchEvent>
#include <QtGui/QAction>
#include <QtGui/QMouseEvent>
#include <QtGui/QOpenGLFunctions>
#include <QtWidgets/QMenu>
// Fraction of the viewport for a move event
static constexpr float kViewportFractionRoughMove = 0.2;
// Fraction of the viewport for a small move event
static constexpr float kViewportFractionSmoothMove = 0.1;
namespace qt::common
{
// #define ENABLE_AA_SWITCH
MapWidget::MapWidget(Framework & framework, bool isScreenshotMode, QWidget * parent)
: QOpenGLWidget(parent)
, m_framework(framework)
, m_screenshotMode(isScreenshotMode)
, m_slider(nullptr)
, m_sliderState(SliderState::Released)
, m_ratio(1.0f)
, m_contextFactory(nullptr)
{
setMouseTracking(true);
// Update widget's content frequency is 60 fps.
m_updateTimer = std::make_unique<QTimer>(this);
VERIFY(connect(m_updateTimer.get(), SIGNAL(timeout()), this, SLOT(update())), ());
m_updateTimer->setSingleShot(false);
m_updateTimer->start(1000 / 60);
setAttribute(Qt::WA_AcceptTouchEvents);
}
MapWidget::~MapWidget()
{
m_framework.EnterBackground();
m_framework.SetRenderingDisabled(true);
m_contextFactory->PrepareToShutdown();
m_framework.DestroyDrapeEngine();
m_contextFactory.reset();
}
void MapWidget::BindHotkeys(QWidget & parent)
{
Hotkey const hotkeys[] = {
{Qt::Key_Equal, SLOT(ScalePlus())},
{Qt::Key_Plus, SLOT(ScalePlus())},
{Qt::Key_Minus, SLOT(ScaleMinus())},
{Qt::Key_Right, SLOT(MoveRight())},
{Qt::Key_Left, SLOT(MoveLeft())},
{Qt::Key_Up, SLOT(MoveUp())},
{Qt::Key_Down, SLOT(MoveDown())},
{Qt::ALT | Qt::Key_Equal, SLOT(ScalePlusLight())},
{Qt::ALT | Qt::Key_Plus, SLOT(ScalePlusLight())},
{Qt::ALT | Qt::Key_Minus, SLOT(ScaleMinusLight())},
{Qt::ALT | Qt::Key_Right, SLOT(MoveRightSmooth())},
{Qt::ALT | Qt::Key_Left, SLOT(MoveLeftSmooth())},
{Qt::ALT | Qt::Key_Up, SLOT(MoveUpSmooth())},
{Qt::ALT | Qt::Key_Down, SLOT(MoveDownSmooth())},
#ifdef ENABLE_AA_SWITCH
{Qt::ALT | Qt::Key_A, SLOT(AntialiasingOn())},
{Qt::ALT | Qt::Key_S, SLOT(AntialiasingOff())},
#endif
};
for (auto const & hotkey : hotkeys)
{
auto action = std::make_unique<QAction>(&parent);
action->setShortcut(QKeySequence(hotkey.m_key));
connect(action.get(), SIGNAL(triggered()), this, hotkey.m_slot);
parent.addAction(action.release());
}
}
void MapWidget::BindSlider(ScaleSlider & slider)
{
m_slider = &slider;
connect(m_slider, &QAbstractSlider::actionTriggered, this, &MapWidget::ScaleChanged);
connect(m_slider, &QAbstractSlider::sliderPressed, this, &MapWidget::SliderPressed);
connect(m_slider, &QAbstractSlider::sliderReleased, this, &MapWidget::SliderReleased);
}
void MapWidget::CreateEngine()
{
Framework::DrapeCreationParams p;
p.m_apiVersion = dp::ApiVersion::OpenGLES3;
p.m_surfaceWidth = m_screenshotMode ? width() : static_cast<int>(m_ratio * width());
p.m_surfaceHeight = m_screenshotMode ? height() : static_cast<int>(m_ratio * height());
p.m_visualScale = static_cast<float>(m_ratio);
p.m_hints.m_screenshotMode = m_screenshotMode;
m_skin.reset(new gui::Skin(gui::ResolveGuiSkinFile("default"), m_ratio));
m_skin->Resize(p.m_surfaceWidth, p.m_surfaceHeight);
m_skin->ForEach([&p](gui::EWidget widget, gui::Position const & pos) { p.m_widgetsInitInfo[widget] = pos; });
p.m_widgetsInitInfo[gui::WIDGET_SCALE_FPS_LABEL] = gui::Position(dp::LeftTop);
m_framework.CreateDrapeEngine(make_ref(m_contextFactory), std::move(p));
m_framework.SetViewportListener(std::bind(&MapWidget::OnViewportChanged, this, std::placeholders::_1));
}
void MapWidget::ScalePlus()
{
m_framework.Scale(Framework::SCALE_MAG, true);
}
void MapWidget::ScaleMinus()
{
m_framework.Scale(Framework::SCALE_MIN, true);
}
void MapWidget::ScalePlusLight()
{
m_framework.Scale(Framework::SCALE_MAG_LIGHT, true);
}
void MapWidget::ScaleMinusLight()
{
m_framework.Scale(Framework::SCALE_MIN_LIGHT, true);
}
void MapWidget::MoveRight()
{
m_framework.Move(-kViewportFractionRoughMove, 0, true);
}
void MapWidget::MoveRightSmooth()
{
m_framework.Move(-kViewportFractionSmoothMove, 0, true);
}
void MapWidget::MoveLeft()
{
m_framework.Move(kViewportFractionRoughMove, 0, true);
}
void MapWidget::MoveLeftSmooth()
{
m_framework.Move(kViewportFractionSmoothMove, 0, true);
}
void MapWidget::MoveUp()
{
m_framework.Move(0, -kViewportFractionRoughMove, true);
}
void MapWidget::MoveUpSmooth()
{
m_framework.Move(0, -kViewportFractionSmoothMove, true);
}
void MapWidget::MoveDown()
{
m_framework.Move(0, kViewportFractionRoughMove, true);
}
void MapWidget::MoveDownSmooth()
{
m_framework.Move(0, kViewportFractionSmoothMove, true);
}
void MapWidget::AntialiasingOn()
{
auto engine = m_framework.GetDrapeEngine();
if (engine != nullptr)
engine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, true);
}
void MapWidget::AntialiasingOff()
{
auto engine = m_framework.GetDrapeEngine();
if (engine != nullptr)
engine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, false);
}
void MapWidget::ScaleChanged(int action)
{
if (!m_slider)
return;
if (action == QAbstractSlider::SliderNoAction)
return;
double const factor = m_slider->GetScaleFactor();
if (factor != 1.0)
m_framework.Scale(factor, false);
}
void MapWidget::SliderPressed()
{
m_sliderState = SliderState::Pressed;
}
void MapWidget::SliderReleased()
{
m_sliderState = SliderState::Released;
}
m2::PointD MapWidget::GetDevicePoint(QMouseEvent * e) const
{
return m2::PointD(L2D(e->position().x()), L2D(e->position().y()));
}
df::Touch MapWidget::GetDfTouchFromQMouseEvent(QMouseEvent * e) const
{
df::Touch touch;
touch.m_id = 0;
touch.m_location = GetDevicePoint(e);
return touch;
}
df::TouchEvent MapWidget::GetDfTouchEventFromQMouseEvent(QMouseEvent * e, df::TouchEvent::ETouchType type) const
{
df::TouchEvent event;
event.SetTouchType(type);
event.SetFirstTouch(GetDfTouchFromQMouseEvent(e));
if (IsCommandModifier(e))
event.SetSecondTouch(GetSymmetrical(event.GetFirstTouch()));
return event;
}
df::Touch MapWidget::GetSymmetrical(df::Touch const & touch) const
{
m2::PointD const pixelCenter = m_framework.GetVisiblePixelCenter();
m2::PointD const symmetricalLocation = pixelCenter + pixelCenter - m2::PointD(touch.m_location);
df::Touch result;
result.m_id = touch.m_id + 1;
result.m_location = symmetricalLocation;
return result;
}
void MapWidget::OnViewportChanged(ScreenBase const & screen)
{
UpdateScaleControl();
}
void MapWidget::UpdateScaleControl()
{
if (!m_slider || m_sliderState == SliderState::Pressed)
return;
m_slider->SetPosWithBlockedSignals(m_framework.GetDrawScale());
}
void MapWidget::Build()
{
std::string_view vertexSrc;
std::string_view fragmentSrc;
#if defined(OMIM_OS_LINUX)
vertexSrc = ":common/shaders/gles_300.vsh.glsl";
fragmentSrc = ":common/shaders/gles_300.fsh.glsl";
#else
vertexSrc = ":common/shaders/gl_150.vsh.glsl";
fragmentSrc = ":common/shaders/gl_150.fsh.glsl";
#endif
m_program = std::make_unique<QOpenGLShaderProgram>(this);
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexSrc.data());
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentSrc.data());
m_program->link();
m_vao = std::make_unique<QOpenGLVertexArrayObject>(this);
m_vao->create();
m_vao->bind();
m_vbo = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::VertexBuffer);
m_vbo->setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vbo->create();
m_vbo->bind();
QVector4D vertices[4] = {QVector4D(-1.0, 1.0, 0.0, 1.0), QVector4D(1.0, 1.0, 1.0, 1.0),
QVector4D(-1.0, -1.0, 0.0, 0.0), QVector4D(1.0, -1.0, 1.0, 0.0)};
m_vbo->allocate(static_cast<void *>(vertices), sizeof(vertices));
QOpenGLFunctions * f = QOpenGLContext::currentContext()->functions();
// 0-index of the buffer is linked to "a_position" attribute in vertex shader.
// Introduced in https://github.com/organicmaps/organicmaps/pull/9814
f->glEnableVertexAttribArray(0);
f->glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(QVector4D), nullptr);
m_program->release();
m_vao->release();
}
namespace
{
search::ReverseGeocoder::Address GetFeatureAddressInfo(Framework const & framework, FeatureType & ft)
{
search::ReverseGeocoder const coder(framework.GetDataSource());
search::ReverseGeocoder::Address address;
coder.GetExactAddress(ft, address);
return address;
}
} // namespace
void MapWidget::ShowInfoPopup(QMouseEvent * e, m2::PointD const & pt)
{
// show feature types
QMenu menu;
auto const addStringFn = [&menu](std::string const & s)
{
if (!s.empty())
menu.addAction(QString::fromUtf8(s.c_str()));
};
m_framework.ForEachFeatureAtPoint([&](FeatureType & ft)
{
// ID
addStringFn(DebugPrint(ft.GetID()));
// Types
std::string concat;
auto types = feature::TypesHolder(ft);
types.SortBySpec();
for (auto const & type : types.ToObjectNames())
concat += type + " ";
addStringFn(concat);
// Name
addStringFn(std::string(ft.GetReadableName()));
// Address
auto const info = GetFeatureAddressInfo(m_framework, ft);
addStringFn(info.FormatAddress());
if (ft.GetGeomType() == feature::GeomType::Line)
{
// Maxspeed
auto const & dataSource = m_framework.GetDataSource();
auto const handle = dataSource.GetMwmHandleById(ft.GetID().m_mwmId);
auto const speeds = routing::LoadMaxspeeds(handle);
if (speeds)
{
auto const speed = speeds->GetMaxspeed(ft.GetID().m_index);
if (speed.IsValid())
addStringFn(DebugPrint(speed));
}
}
int const layer = ft.GetLayer();
if (layer != feature::LAYER_EMPTY)
addStringFn("Layer = " + std::to_string(layer));
menu.addSeparator();
}, m_framework.PtoG(pt));
menu.exec(e->pos());
}
void MapWidget::initializeGL()
{
ASSERT(m_contextFactory == nullptr, ());
if (!m_screenshotMode)
m_ratio = devicePixelRatio();
#if defined(OMIM_OS_LINUX)
{
QOpenGLFunctions * funcs = context()->functions();
LOG(LINFO, ("Vendor:", funcs->glGetString(GL_VENDOR), "\nRenderer:", funcs->glGetString(GL_RENDERER),
"\nVersion:", funcs->glGetString(GL_VERSION), "\nShading language version:\n",
funcs->glGetString(GL_SHADING_LANGUAGE_VERSION), "\nExtensions:", funcs->glGetString(GL_EXTENSIONS)));
if (!context()->isOpenGLES())
LOG(LCRITICAL, ("Context is not LibGLES! This shouldn't have happened."));
auto fmt = context()->format();
if (context()->format().version() < qMakePair(3, 0))
{
CHECK(false, ("OpenGL ES2 is not supported"));
}
else
{
LOG(LINFO, ("OpenGL version is at least 3.0, enabling GLSL '#version 300 es'"));
fmt.setVersion(3, 0);
}
QSurfaceFormat::setDefaultFormat(fmt);
}
#endif
m_contextFactory.reset(new QtOGLContextFactory(context()));
emit BeforeEngineCreation();
CreateEngine();
m_framework.EnterForeground();
}
void MapWidget::paintGL()
{
if (m_program == nullptr)
Build();
if (m_contextFactory->AcquireFrame())
{
m_vao->bind();
m_program->bind();
QOpenGLFunctions * funcs = context()->functions();
funcs->glActiveTexture(GL_TEXTURE0);
GLuint const image = m_contextFactory->GetTextureHandle();
funcs->glBindTexture(GL_TEXTURE_2D, image);
int const samplerLocation = m_program->uniformLocation("u_sampler");
m_program->setUniformValue(samplerLocation, 0);
QRectF const & texRect = m_contextFactory->GetTexRect();
QVector2D const samplerSize(texRect.width(), texRect.height());
int const samplerSizeLocation = m_program->uniformLocation("u_samplerSize");
m_program->setUniformValue(samplerSizeLocation, samplerSize);
funcs->glClearColor(0.0, 0.0, 0.0, 1.0);
funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_program->release();
m_vao->release();
}
}
void MapWidget::resizeGL(int width, int height)
{
int const w = m_screenshotMode ? width : m_ratio * width;
int const h = m_screenshotMode ? height : m_ratio * height;
m_framework.OnSize(w, h);
if (m_skin)
{
m_skin->Resize(w, h);
gui::TWidgetsLayoutInfo layout;
m_skin->ForEach([&layout](gui::EWidget w, gui::Position const & pos) { layout[w] = pos.m_pixelPivot; });
m_framework.SetWidgetLayout(std::move(layout));
}
}
void MapWidget::mouseDoubleClickEvent(QMouseEvent * e)
{
if (m_screenshotMode)
return;
QOpenGLWidget::mouseDoubleClickEvent(e);
if (IsLeftButton(e))
m_framework.Scale(Framework::SCALE_MAG_LIGHT, GetDevicePoint(e), true);
}
void MapWidget::mousePressEvent(QMouseEvent * e)
{
if (m_screenshotMode)
return;
QOpenGLWidget::mousePressEvent(e);
if (IsLeftButton(e))
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_DOWN));
}
void MapWidget::mouseMoveEvent(QMouseEvent * e)
{
if (m_screenshotMode)
return;
QOpenGLWidget::mouseMoveEvent(e);
if (IsLeftButton(e))
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_MOVE));
}
void MapWidget::mouseReleaseEvent(QMouseEvent * e)
{
if (m_screenshotMode)
return;
if (e->button() == Qt::RightButton)
emit OnContextMenuRequested(e->globalPosition().toPoint());
QOpenGLWidget::mouseReleaseEvent(e);
if (IsLeftButton(e))
m_framework.TouchEvent(GetDfTouchEventFromQMouseEvent(e, df::TouchEvent::TOUCH_UP));
}
void MapWidget::wheelEvent(QWheelEvent * e)
{
if (m_screenshotMode)
return;
QOpenGLWidget::wheelEvent(e);
QPointF const pos = e->position();
double const factor = e->angleDelta().y() / 3.0 / 360.0;
// https://doc-snapshots.qt.io/qt6-dev/qwheelevent.html#angleDelta, angleDelta() returns in eighths of a degree.
/// @todo Here you can tune the speed of zooming.
m_framework.Scale(exp(factor), m2::PointD(L2D(pos.x()), L2D(pos.y())), false);
}
} // namespace qt::common

113
qt/qt_common/map_widget.hpp Normal file
View file

@ -0,0 +1,113 @@
#pragma once
#include "drape/pointers.hpp"
#include "drape_frontend/gui/skin.hpp"
#include "drape_frontend/user_event_stream.hpp"
#include "qt/qt_common/qtoglcontextfactory.hpp"
#include <QOpenGLWidget>
#include <QtCore/QTimer>
#include <memory>
class Framework;
class QMouseEvent;
class QWidget;
class ScreenBase;
class QOpenGLShaderProgram;
class QOpenGLVertexArrayObject;
class QOpenGLBuffer;
namespace qt::common
{
class ScaleSlider;
class MapWidget : public QOpenGLWidget
{
Q_OBJECT
public:
MapWidget(Framework & framework, bool isScreenshotMode, QWidget * parent);
~MapWidget() override;
void BindHotkeys(QWidget & parent);
void BindSlider(ScaleSlider & slider);
void CreateEngine();
void grabGestures(QList<Qt::GestureType> const & gestures);
signals:
void OnContextMenuRequested(QPoint const & p);
public slots:
void ScalePlus();
void ScaleMinus();
void ScalePlusLight();
void ScaleMinusLight();
void MoveRight();
void MoveRightSmooth();
void MoveLeft();
void MoveLeftSmooth();
void MoveUp();
void MoveUpSmooth();
void MoveDown();
void MoveDownSmooth();
void ScaleChanged(int action);
void SliderPressed();
void SliderReleased();
void AntialiasingOn();
void AntialiasingOff();
public:
Q_SIGNAL void BeforeEngineCreation();
protected:
enum class SliderState
{
Pressed,
Released
};
int L2D(int px) const { return px * m_ratio; }
m2::PointD GetDevicePoint(QMouseEvent * e) const;
df::Touch GetDfTouchFromQMouseEvent(QMouseEvent * e) const;
df::TouchEvent GetDfTouchEventFromQMouseEvent(QMouseEvent * e, df::TouchEvent::ETouchType type) const;
df::Touch GetSymmetrical(df::Touch const & touch) const;
void UpdateScaleControl();
void Build();
void ShowInfoPopup(QMouseEvent * e, m2::PointD const & pt);
void OnViewportChanged(ScreenBase const & screen);
// QOpenGLWidget overrides:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void mouseDoubleClickEvent(QMouseEvent * e) override;
void mousePressEvent(QMouseEvent * e) override;
void mouseMoveEvent(QMouseEvent * e) override;
void mouseReleaseEvent(QMouseEvent * e) override;
void wheelEvent(QWheelEvent * e) override;
Framework & m_framework;
bool m_screenshotMode;
ScaleSlider * m_slider;
SliderState m_sliderState;
float m_ratio;
drape_ptr<QtOGLContextFactory> m_contextFactory;
std::unique_ptr<gui::Skin> m_skin;
std::unique_ptr<QTimer> m_updateTimer;
std::unique_ptr<QOpenGLShaderProgram> m_program;
std::unique_ptr<QOpenGLVertexArrayObject> m_vao;
std::unique_ptr<QOpenGLBuffer> m_vbo;
};
} // namespace qt::common

View file

@ -0,0 +1,135 @@
#include "qt/qt_common/proxy_style.hpp"
namespace qt
{
namespace common
{
ProxyStyle::ProxyStyle(QStyle * p) : QStyle(), style(p) {}
void ProxyStyle::drawComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPainter * painter,
QWidget const * widget) const
{
style->drawComplexControl(control, option, painter, widget);
}
void ProxyStyle::drawControl(ControlElement element, QStyleOption const * option, QPainter * painter,
QWidget const * widget) const
{
style->drawControl(element, option, painter, widget);
}
void ProxyStyle::drawItemPixmap(QPainter * painter, QRect const & rect, int alignment, QPixmap const & pixmap) const
{
style->drawItemPixmap(painter, rect, alignment, pixmap);
}
void ProxyStyle::drawItemText(QPainter * painter, QRect const & rect, int alignment, QPalette const & pal, bool enabled,
QString const & text, QPalette::ColorRole textRole) const
{
style->drawItemText(painter, rect, alignment, pal, enabled, text, textRole);
}
void ProxyStyle::drawPrimitive(PrimitiveElement elem, QStyleOption const * option, QPainter * painter,
QWidget const * widget) const
{
style->drawPrimitive(elem, option, painter, widget);
}
QPixmap ProxyStyle::generatedIconPixmap(QIcon::Mode iconMode, QPixmap const & pixmap, QStyleOption const * option) const
{
return style->generatedIconPixmap(iconMode, pixmap, option);
}
QStyle::SubControl ProxyStyle::hitTestComplexControl(ComplexControl control, QStyleOptionComplex const * option,
QPoint const & pos, QWidget const * widget) const
{
return style->hitTestComplexControl(control, option, pos, widget);
}
QRect ProxyStyle::itemPixmapRect(QRect const & rect, int alignment, QPixmap const & pixmap) const
{
return style->itemPixmapRect(rect, alignment, pixmap);
}
QRect ProxyStyle::itemTextRect(QFontMetrics const & metrics, QRect const & rect, int alignment, bool enabled,
QString const & text) const
{
return style->itemTextRect(metrics, rect, alignment, enabled, text);
}
int ProxyStyle::pixelMetric(PixelMetric metric, QStyleOption const * option, QWidget const * widget) const
{
return style->pixelMetric(metric, option, widget);
}
void ProxyStyle::polish(QWidget * widget)
{
style->polish(widget);
}
void ProxyStyle::polish(QApplication * app)
{
style->polish(app);
}
void ProxyStyle::polish(QPalette & pal)
{
style->polish(pal);
}
QSize ProxyStyle::sizeFromContents(ContentsType type, QStyleOption const * option, QSize const & contentsSize,
QWidget const * widget) const
{
return style->sizeFromContents(type, option, contentsSize, widget);
}
QIcon ProxyStyle::standardIcon(StandardPixmap standardIcon, QStyleOption const * option, QWidget const * widget) const
{
return style->standardIcon(standardIcon, option, widget);
}
QPalette ProxyStyle::standardPalette() const
{
return style->standardPalette();
}
QPixmap ProxyStyle::standardPixmap(StandardPixmap standardPixmap, QStyleOption const * option,
QWidget const * widget) const
{
return style->standardPixmap(standardPixmap, option, widget);
}
int ProxyStyle::styleHint(StyleHint hint, QStyleOption const * option, QWidget const * widget,
QStyleHintReturn * returnData) const
{
return style->styleHint(hint, option, widget, returnData);
}
QRect ProxyStyle::subControlRect(ComplexControl control, QStyleOptionComplex const * option, SubControl subControl,
QWidget const * widget) const
{
return style->subControlRect(control, option, subControl, widget);
}
QRect ProxyStyle::subElementRect(SubElement element, QStyleOption const * option, QWidget const * widget) const
{
return style->subElementRect(element, option, widget);
}
void ProxyStyle::unpolish(QWidget * widget)
{
style->unpolish(widget);
}
void ProxyStyle::unpolish(QApplication * app)
{
style->unpolish(app);
}
int ProxyStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
Qt::Orientation orientation, QStyleOption const * option, QWidget const * widget) const
{
return style->layoutSpacing(control1, control2, orientation, option, widget);
}
} // namespace common
} // namespace qt

View file

@ -0,0 +1,55 @@
#pragma once
#include <QtWidgets/QStyle>
namespace qt
{
namespace common
{
class ProxyStyle : public QStyle
{
public:
explicit ProxyStyle(QStyle * p);
// QStyle overrides:
void drawComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPainter * painter,
QWidget const * widget = 0) const override;
void drawControl(ControlElement element, QStyleOption const * option, QPainter * painter,
QWidget const * widget = 0) const override;
void drawItemPixmap(QPainter * painter, QRect const & rect, int alignment, QPixmap const & pixmap) const override;
void drawItemText(QPainter * painter, QRect const & rect, int alignment, QPalette const & pal, bool enabled,
QString const & text, QPalette::ColorRole textRole = QPalette::NoRole) const override;
void drawPrimitive(PrimitiveElement elem, QStyleOption const * option, QPainter * painter,
QWidget const * widget = 0) const override;
QPixmap generatedIconPixmap(QIcon::Mode iconMode, QPixmap const & pixmap, QStyleOption const * option) const override;
SubControl hitTestComplexControl(ComplexControl control, QStyleOptionComplex const * option, QPoint const & pos,
QWidget const * widget = 0) const override;
QRect itemPixmapRect(QRect const & rect, int alignment, QPixmap const & pixmap) const override;
QRect itemTextRect(QFontMetrics const & metrics, QRect const & rect, int alignment, bool enabled,
QString const & text) const override;
int pixelMetric(PixelMetric metric, QStyleOption const * option = 0, QWidget const * widget = 0) const override;
void polish(QWidget * widget) override;
void polish(QApplication * app) override;
void polish(QPalette & pal) override;
QSize sizeFromContents(ContentsType type, QStyleOption const * option, QSize const & contentsSize,
QWidget const * widget = 0) const override;
QIcon standardIcon(StandardPixmap standardIcon, QStyleOption const * option = 0,
QWidget const * widget = 0) const override;
QPalette standardPalette() const override;
QPixmap standardPixmap(StandardPixmap standardPixmap, QStyleOption const * option = 0,
QWidget const * widget = 0) const override;
int styleHint(StyleHint hint, QStyleOption const * option = 0, QWidget const * widget = 0,
QStyleHintReturn * returnData = 0) const override;
QRect subControlRect(ComplexControl control, QStyleOptionComplex const * option, SubControl subControl,
QWidget const * widget = 0) const override;
QRect subElementRect(SubElement element, QStyleOption const * option, QWidget const * widget = 0) const override;
void unpolish(QWidget * widget) override;
void unpolish(QApplication * app) override;
int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation,
QStyleOption const * option = 0, QWidget const * widget = 0) const override;
private:
QStyle * style;
};
} // namespace common
} // namespace qt

View file

@ -0,0 +1,144 @@
#include "qt/qt_common/qtoglcontext.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include "base/macros.hpp"
#include "base/math.hpp"
#include "drape/gl_functions.hpp"
#include <memory>
namespace qt
{
namespace common
{
QtRenderOGLContext::QtRenderOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface)
: m_surface(surface)
, m_ctx(std::make_unique<QOpenGLContext>())
, m_isContextAvailable(false)
{
m_ctx->setFormat(rootContext->format());
m_ctx->setShareContext(rootContext);
m_ctx->create();
CHECK(m_ctx->isValid(), ());
}
void QtRenderOGLContext::Present()
{
GLFunctions::glFinish();
std::lock_guard<std::mutex> lock(m_frameMutex);
std::swap(m_frontFrame, m_backFrame);
m_frameUpdated = true;
}
void QtRenderOGLContext::MakeCurrent()
{
CHECK(m_ctx->makeCurrent(m_surface), ());
m_isContextAvailable = true;
}
void QtRenderOGLContext::DoneCurrent()
{
m_isContextAvailable = false;
m_ctx->doneCurrent();
}
void QtRenderOGLContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer)
{
if (framebuffer)
framebuffer->Bind();
else if (m_backFrame != nullptr)
m_backFrame->bind();
}
void QtRenderOGLContext::Resize(uint32_t w, uint32_t h)
{
CHECK(m_isContextAvailable, ());
// This function can't be called inside BeginRendering - EndRendering.
std::lock_guard lock(m_frameMutex);
auto const nw = static_cast<int>(math::NextPowOf2(w));
auto const nh = static_cast<int>(math::NextPowOf2(h));
if (nw <= m_width && nh <= m_height && m_backFrame != nullptr)
{
m_frameRect = QRectF(0.0, 0.0, w / static_cast<float>(m_width), h / static_cast<float>(m_height));
return;
}
m_width = nw;
m_height = nh;
m_frameRect = QRectF(0.0, 0.0, w / static_cast<float>(m_width), h / static_cast<float>(m_height));
m_backFrame = std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
m_frontFrame = std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
m_acquiredFrame =
std::make_unique<QOpenGLFramebufferObject>(QSize(m_width, m_height), QOpenGLFramebufferObject::Depth);
}
bool QtRenderOGLContext::AcquireFrame()
{
if (!m_isContextAvailable)
return false;
std::lock_guard lock(m_frameMutex);
// Render current acquired frame.
if (!m_frameUpdated)
return true;
// Update acquired frame.
m_acquiredFrameRect = m_frameRect;
std::swap(m_acquiredFrame, m_frontFrame);
m_frameUpdated = false;
return true;
}
QRectF const & QtRenderOGLContext::GetTexRect() const
{
return m_acquiredFrameRect;
}
GLuint QtRenderOGLContext::GetTextureHandle() const
{
if (!m_acquiredFrame)
return 0;
CHECK(!m_acquiredFrame->isBound(), ());
return m_acquiredFrame->texture();
}
QtUploadOGLContext::QtUploadOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface)
: m_surface(surface)
, m_ctx(std::make_unique<QOpenGLContext>())
{
m_ctx->setFormat(rootContext->format());
m_ctx->setShareContext(rootContext);
m_ctx->create();
CHECK(m_ctx->isValid(), ());
}
void QtUploadOGLContext::MakeCurrent()
{
m_ctx->makeCurrent(m_surface);
}
void QtUploadOGLContext::DoneCurrent()
{
m_ctx->doneCurrent();
}
void QtUploadOGLContext::Present()
{
CHECK(false, ());
}
void QtUploadOGLContext::SetFramebuffer(ref_ptr<dp::BaseFramebuffer>)
{
CHECK(false, ());
}
} // namespace common
} // namespace qt

View file

@ -0,0 +1,66 @@
#pragma once
#include "drape/oglcontext.hpp"
#include <QOpenGLFramebufferObject>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLContext>
#include <atomic>
#include <memory>
#include <mutex>
namespace qt
{
namespace common
{
class QtRenderOGLContext : public dp::OGLContext
{
public:
QtRenderOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface);
void Present() override;
void MakeCurrent() override;
void DoneCurrent() override;
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override;
void Resize(uint32_t w, uint32_t h) override;
bool AcquireFrame();
GLuint GetTextureHandle() const;
QRectF const & GetTexRect() const;
private:
QOffscreenSurface * m_surface = nullptr;
std::unique_ptr<QOpenGLContext> m_ctx;
std::unique_ptr<QOpenGLFramebufferObject> m_frontFrame;
std::unique_ptr<QOpenGLFramebufferObject> m_backFrame;
std::unique_ptr<QOpenGLFramebufferObject> m_acquiredFrame;
QRectF m_acquiredFrameRect = QRectF(0.0, 0.0, 0.0, 0.0);
QRectF m_frameRect = QRectF(0.0, 0.0, 0.0, 0.0);
bool m_frameUpdated = false;
std::atomic<bool> m_isContextAvailable;
int m_width = 0;
int m_height = 0;
std::mutex m_frameMutex;
};
class QtUploadOGLContext : public dp::OGLContext
{
public:
QtUploadOGLContext(QOpenGLContext * rootContext, QOffscreenSurface * surface);
void Present() override;
void MakeCurrent() override;
void DoneCurrent() override;
void SetFramebuffer(ref_ptr<dp::BaseFramebuffer> framebuffer) override;
private:
QOffscreenSurface * m_surface = nullptr; // non-owning ptr
std::unique_ptr<QOpenGLContext> m_ctx;
};
} // namespace common
} // namespace qt

View file

@ -0,0 +1,78 @@
#include "qt/qt_common/qtoglcontextfactory.hpp"
#include "base/assert.hpp"
#include <memory>
namespace qt
{
namespace common
{
QtOGLContextFactory::QtOGLContextFactory(QOpenGLContext * rootContext) : m_rootContext(rootContext)
{
m_uploadSurface = CreateSurface();
m_drawSurface = CreateSurface();
}
QtOGLContextFactory::~QtOGLContextFactory()
{
m_drawContext.reset();
m_uploadContext.reset();
m_drawSurface->destroy();
m_uploadSurface->destroy();
}
void QtOGLContextFactory::PrepareToShutdown()
{
m_preparedToShutdown = true;
}
bool QtOGLContextFactory::AcquireFrame()
{
if (m_preparedToShutdown || !m_drawContext)
return false;
return m_drawContext->AcquireFrame();
}
QRectF const & QtOGLContextFactory::GetTexRect() const
{
ASSERT(m_drawContext != nullptr, ());
return m_drawContext->GetTexRect();
}
GLuint QtOGLContextFactory::GetTextureHandle() const
{
ASSERT(m_drawContext != nullptr, ());
return m_drawContext->GetTextureHandle();
}
dp::GraphicsContext * QtOGLContextFactory::GetDrawContext()
{
if (!m_drawContext)
m_drawContext = std::make_unique<QtRenderOGLContext>(m_rootContext, m_drawSurface.get());
return m_drawContext.get();
}
dp::GraphicsContext * QtOGLContextFactory::GetResourcesUploadContext()
{
if (!m_uploadContext)
m_uploadContext = std::make_unique<QtUploadOGLContext>(m_rootContext, m_uploadSurface.get());
return m_uploadContext.get();
}
std::unique_ptr<QOffscreenSurface> QtOGLContextFactory::CreateSurface()
{
QSurfaceFormat format = m_rootContext->format();
auto result = std::make_unique<QOffscreenSurface>(m_rootContext->screen());
result->setFormat(format);
result->create();
ASSERT(result->isValid(), ());
return result;
}
} // namespace common
} // namespace qt

View file

@ -0,0 +1,43 @@
#pragma once
#include "drape/graphics_context_factory.hpp"
#include "qt/qt_common/qtoglcontext.hpp"
#include <QtGui/QOpenGLContext>
#include <memory>
namespace qt
{
namespace common
{
class QtOGLContextFactory : public dp::GraphicsContextFactory
{
public:
QtOGLContextFactory(QOpenGLContext * rootContext);
~QtOGLContextFactory() override;
void PrepareToShutdown();
bool AcquireFrame();
GLuint GetTextureHandle() const;
QRectF const & GetTexRect() const;
// dp::GraphicsContextFactory overrides:
dp::GraphicsContext * GetDrawContext() override;
dp::GraphicsContext * GetResourcesUploadContext() override;
bool IsDrawContextCreated() const override { return m_drawContext != nullptr; }
bool IsUploadContextCreated() const override { return m_uploadContext != nullptr; }
private:
std::unique_ptr<QOffscreenSurface> CreateSurface();
QOpenGLContext * m_rootContext;
std::unique_ptr<QtRenderOGLContext> m_drawContext;
std::unique_ptr<QOffscreenSurface> m_drawSurface;
std::unique_ptr<QtUploadOGLContext> m_uploadContext;
std::unique_ptr<QOffscreenSurface> m_uploadSurface;
bool m_preparedToShutdown = false;
};
} // namespace common
} // namespace qt

BIN
qt/qt_common/res/minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
qt/qt_common/res/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,22 @@
<RCC>
<qresource prefix="/common">
<file>minus.png</file>
<file>plus.png</file>
<file>spinner1.png</file>
<file>spinner2.png</file>
<file>spinner3.png</file>
<file>spinner4.png</file>
<file>spinner5.png</file>
<file>spinner6.png</file>
<file>spinner7.png</file>
<file>spinner8.png</file>
<file>spinner9.png</file>
<file>spinner10.png</file>
<file>spinner11.png</file>
<file>spinner12.png</file>
<file>shaders/gl_150.fsh.glsl</file>
<file>shaders/gl_150.vsh.glsl</file>
<file>shaders/gles_300.fsh.glsl</file>
<file>shaders/gles_300.vsh.glsl</file>
</qresource>
</RCC>

View file

@ -0,0 +1,10 @@
#version 150 core
uniform sampler2D u_sampler;
in vec2 v_texCoord;
out vec4 v_FragColor;
void main()
{
v_FragColor = vec4(texture(u_sampler, v_texCoord).rgb, 1.0);
}

View file

@ -0,0 +1,11 @@
#version 150 core
in vec4 a_position;
uniform vec2 u_samplerSize;
out vec2 v_texCoord;
void main()
{
v_texCoord = vec2(a_position.z * u_samplerSize.x, a_position.w * u_samplerSize.y);
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
}

View file

@ -0,0 +1,16 @@
#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform sampler2D u_sampler;
in vec2 v_texCoord;
out vec4 v_FragColor;
void main()
{
v_FragColor = vec4(texture(u_sampler, v_texCoord).rgb, 1.0);
}

View file

@ -0,0 +1,17 @@
#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
in vec4 a_position;
uniform vec2 u_samplerSize;
out vec2 v_texCoord;
void main()
{
v_texCoord = vec2(a_position.z * u_samplerSize.x, a_position.w * u_samplerSize.y);
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

View file

@ -0,0 +1,79 @@
#include "qt/qt_common/scale_slider.hpp"
#include "qt/qt_common/map_widget.hpp"
#include "qt/qt_common/proxy_style.hpp"
#include "indexer/scales.hpp"
#include "base/math.hpp"
#include <QtWidgets/QToolBar>
#include <cmath>
#include <memory>
namespace qt
{
namespace common
{
namespace
{
class MyProxyStyle : public ProxyStyle
{
public:
explicit MyProxyStyle(QStyle * parent) : ProxyStyle(parent) {}
int styleHint(StyleHint hint, QStyleOption const * option, QWidget const * widget,
QStyleHintReturn * returnData) const override
{
if (hint == SH_Slider_AbsoluteSetButtons)
return Qt::LeftButton;
return ProxyStyle::styleHint(hint, option, widget, returnData);
}
};
} // namespace
ScaleSlider::ScaleSlider(Qt::Orientation orient, QWidget * parent) : QSlider(orient, parent), m_factor(20)
{
setStyle(new MyProxyStyle(style()));
SetRange(2, scales::GetUpperScale());
setTickPosition(QSlider::TicksRight);
}
// static
void ScaleSlider::Embed(Qt::Orientation orient, QToolBar & toolBar, MapWidget & mapWidget)
{
toolBar.addAction(QIcon(":/common/plus.png"), tr("Scale +"), &mapWidget, SLOT(ScalePlus()));
{
auto slider = std::make_unique<ScaleSlider>(orient, &toolBar);
mapWidget.BindSlider(*slider);
toolBar.addWidget(slider.release());
}
toolBar.addAction(QIcon(":/common/minus.png"), tr("Scale -"), &mapWidget, SLOT(ScaleMinus()));
}
double ScaleSlider::GetScaleFactor() const
{
double const oldValue = value();
double const newValue = sliderPosition();
if (oldValue == newValue)
return 1.0;
double const f = pow(2, fabs(oldValue - newValue) / m_factor);
return (newValue > oldValue ? f : 1.0 / f);
}
void ScaleSlider::SetPosWithBlockedSignals(double pos)
{
bool const blocked = signalsBlocked();
blockSignals(true);
setSliderPosition(std::lround(pos * m_factor));
blockSignals(blocked);
}
void ScaleSlider::SetRange(int low, int up)
{
setRange(low * m_factor, up * m_factor);
}
} // namespace common
} // namespace qt

View file

@ -0,0 +1,30 @@
#pragma once
#include <QtCore/Qt>
#include <QtWidgets/QSlider>
class QToolBar;
namespace qt
{
namespace common
{
class MapWidget;
class ScaleSlider : public QSlider
{
public:
ScaleSlider(Qt::Orientation orient, QWidget * parent);
static void Embed(Qt::Orientation orient, QToolBar & toolBar, MapWidget & mapWidget);
double GetScaleFactor() const;
void SetPosWithBlockedSignals(double pos);
private:
void SetRange(int low, int up);
int m_factor;
};
} // namespace common
} // namespace qt

52
qt/qt_common/spinner.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "qt/qt_common/spinner.hpp"
#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtCore/Qt>
namespace
{
int constexpr kMinSpinnerPixmap = 1;
int constexpr kMaxSpinnerPixmap = 12;
int constexpr kTimeoutMs = 100;
} // namespace
Spinner::Spinner()
{
for (int i = kMinSpinnerPixmap; i <= kMaxSpinnerPixmap; ++i)
{
auto const path = ":common/spinner" + QString::number(i) + ".png";
m_pixmaps.emplace_back(path);
}
setEnabled(false);
setAlignment(Qt::AlignCenter);
QSizePolicy policy(QSizePolicy::Fixed, QSizePolicy::Fixed);
policy.setRetainSizeWhenHidden(true);
setSizePolicy(policy);
setMinimumSize(m_pixmaps.front().size());
setPixmap(m_pixmaps.front());
m_timer = new QTimer(this /* parent */);
connect(m_timer, &QTimer::timeout, [this]()
{
m_progress = (m_progress + 1) % m_pixmaps.size();
setPixmap(m_pixmaps[m_progress]);
});
}
void Spinner::Show()
{
m_progress = 0;
setPixmap(m_pixmaps[m_progress]);
m_timer->start(kTimeoutMs);
show();
}
void Spinner::Hide()
{
m_timer->stop();
hide();
}

23
qt/qt_common/spinner.hpp Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <cstddef>
#include <vector>
#include <QtGui/QPixmap>
#include <QtWidgets/QLabel>
class Spinner : private QLabel
{
public:
Spinner();
void Show();
void Hide();
QLabel & AsWidget() { return static_cast<QLabel &>(*this); }
private:
std::vector<QPixmap> m_pixmaps;
QTimer * m_timer = nullptr;
size_t m_progress = 0;
};

View file

@ -0,0 +1,37 @@
#include "qt/qt_common/text_dialog.hpp"
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QVBoxLayout>
TextDialog::TextDialog(QWidget * parent, QString const & htmlOrText, QString const & title) : QDialog(parent)
{
auto * textEdit = new QTextEdit(this);
textEdit->setReadOnly(true);
textEdit->setHtml(htmlOrText);
auto * closeButton = new QPushButton("Close");
closeButton->setDefault(true);
connect(closeButton, &QAbstractButton::clicked, this, &TextDialog::OnClose);
auto * dbb = new QDialogButtonBox();
dbb->addButton(closeButton, QDialogButtonBox::RejectRole);
auto * vBoxLayout = new QVBoxLayout(this);
vBoxLayout->addWidget(textEdit);
vBoxLayout->addWidget(dbb);
setLayout(vBoxLayout);
setWindowTitle(title);
if (htmlOrText.size() > 10000)
setWindowState(Qt::WindowMaximized);
else
resize(parent->size());
}
void TextDialog::OnClose()
{
reject();
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <QtWidgets/QDialog>
/// A reusable dialog that shows text or HTML.
class TextDialog : public QDialog
{
Q_OBJECT
public:
TextDialog(QWidget * parent, QString const & htmlOrText, QString const & title = "");
private slots:
void OnClose();
};