Repo created
36
qt/qt_common/CMakeLists.txt
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
135
qt/qt_common/proxy_style.cpp
Normal 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
|
||||
55
qt/qt_common/proxy_style.hpp
Normal 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
|
||||
144
qt/qt_common/qtoglcontext.cpp
Normal 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
|
||||
66
qt/qt_common/qtoglcontext.hpp
Normal 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
|
||||
78
qt/qt_common/qtoglcontextfactory.cpp
Normal 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
|
||||
43
qt/qt_common/qtoglcontextfactory.hpp
Normal 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
|
After Width: | Height: | Size: 1.2 KiB |
BIN
qt/qt_common/res/plus.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
22
qt/qt_common/res/resources_common.qrc
Normal 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>
|
||||
10
qt/qt_common/res/shaders/gl_150.fsh.glsl
Normal 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);
|
||||
}
|
||||
11
qt/qt_common/res/shaders/gl_150.vsh.glsl
Normal 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);
|
||||
}
|
||||
16
qt/qt_common/res/shaders/gles_300.fsh.glsl
Normal 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);
|
||||
}
|
||||
17
qt/qt_common/res/shaders/gles_300.vsh.glsl
Normal 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);
|
||||
}
|
||||
BIN
qt/qt_common/res/spinner1.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner10.png
Normal file
|
After Width: | Height: | Size: 489 B |
BIN
qt/qt_common/res/spinner11.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner12.png
Normal file
|
After Width: | Height: | Size: 491 B |
BIN
qt/qt_common/res/spinner2.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner3.png
Normal file
|
After Width: | Height: | Size: 495 B |
BIN
qt/qt_common/res/spinner4.png
Normal file
|
After Width: | Height: | Size: 486 B |
BIN
qt/qt_common/res/spinner5.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
qt/qt_common/res/spinner6.png
Normal file
|
After Width: | Height: | Size: 492 B |
BIN
qt/qt_common/res/spinner7.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
qt/qt_common/res/spinner8.png
Normal file
|
After Width: | Height: | Size: 487 B |
BIN
qt/qt_common/res/spinner9.png
Normal file
|
After Width: | Height: | Size: 493 B |
79
qt/qt_common/scale_slider.cpp
Normal 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
|
||||
30
qt/qt_common/scale_slider.hpp
Normal 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
|
|
@ -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
|
|
@ -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;
|
||||
};
|
||||
37
qt/qt_common/text_dialog.cpp
Normal 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();
|
||||
}
|
||||
15
qt/qt_common/text_dialog.hpp
Normal 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();
|
||||
};
|
||||