diff --git a/map/framework.cpp b/map/framework.cpp index 32fa8aba04..3276a0fa46 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -13,6 +13,7 @@ #include "routing/road_graph_router.hpp" #include "routing/route.hpp" #include "routing/routing_algorithm.hpp" +#include "routing/routing_helpers.hpp" #include "search/downloader_search_callback.hpp" #include "search/editor_delegate.hpp" @@ -3137,3 +3138,41 @@ bool Framework::GenerateRouteAltitudeChart(uint32_t width, uint32_t height, } return true; } + +namespace +{ + vector colorList = { dp::Color(255, 0, 0, 255), dp::Color(0, 255, 0, 255), dp::Color(0, 0, 255, 255), + dp::Color(255, 255, 0, 255), dp::Color(0, 255, 255, 255), dp::Color(255, 0, 255, 255), + dp::Color(100, 0, 0, 255), dp::Color(0, 100, 0, 255), dp::Color(0, 0, 100, 255), + dp::Color(100, 100, 0, 255), dp::Color(0, 100, 100, 255), dp::Color(100, 0, 100, 255) + }; +} // namespace + +void Framework::VizualizeRoadsInRect(m2::RectD const & rect) +{ + int constexpr kScale = scales::GetUpperScale(); + size_t counter = 0; + m_model.ForEachFeature(rect, [this, &counter, &rect](FeatureType & ft) + { + if (routing::IsRoad(feature::TypesHolder(ft))) + { + bool allPointsOutside = true; + vector points; + ft.ForEachPoint([&points, &rect, &allPointsOutside](m2::PointD const & pt) + { + if (rect.IsPointInside(pt)) + allPointsOutside = false; + points.push_back(pt); + }, kScale); + + if (!allPointsOutside) + { + size_t const colorIndex = counter % colorList.size(); + m_drapeApi.AddLine(strings::to_string(ft.GetID().m_index), + df::DrapeApiLineData(points, colorList[colorIndex]) + .Width(3.0f).ShowPoints(true).ShowId()); + counter++; + } + } + }, kScale); +} diff --git a/map/framework.hpp b/map/framework.hpp index 4e509a0d96..b03c6b0036 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -303,6 +303,9 @@ public: m2::PointD GetSearchMarkSize(SearchMarkType searchMarkType); + // Utilities + void VizualizeRoadsInRect(m2::RectD const & rect); + protected: // search::ViewportSearchCallback::Delegate overrides: void RunUITask(function fn) override { GetPlatform().RunOnGuiThread(move(fn)); } diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index 91307fb90c..d70dcfb960 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -44,8 +44,7 @@ bool IsLeftButton(Qt::MouseButtons buttons) { return buttons & Qt::LeftButton; } - -bool IsLeftButton(QMouseEvent * e) +bool IsLeftButton(QMouseEvent const * const e) { return IsLeftButton(e->button()) || IsLeftButton(e->buttons()); } @@ -54,37 +53,25 @@ bool IsRightButton(Qt::MouseButtons buttons) { return buttons & Qt::RightButton; } - -bool IsRightButton(QMouseEvent * e) +bool IsRightButton(QMouseEvent const * const e) { return IsRightButton(e->button()) || IsRightButton(e->buttons()); } -bool IsRotation(QMouseEvent * e) -{ - return e->modifiers() & Qt::ControlModifier; -} - -bool IsRouting(QMouseEvent * e) -{ - return e->modifiers() & Qt::ShiftModifier; -} - -bool IsLocationEmulation(QMouseEvent * e) -{ - return e->modifiers() & Qt::AltModifier; -} - +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; } } // namespace DrawWidget::DrawWidget(QWidget * parent) - : TBase(parent), - m_contextFactory(nullptr), - m_framework(new Framework()), - m_ratio(1.0), - m_pScale(nullptr), - m_enableScaleUpdate(true), - m_emulatingLocation(false) + : TBase(parent) + , m_contextFactory(nullptr) + , m_framework(new Framework()) + , m_ratio(1.0) + , m_pScale(nullptr) + , m_rubberBand(nullptr) + , m_enableScaleUpdate(true) + , m_emulatingLocation(false) { m_framework->SetMapSelectionListeners([this](place_page::Info const & info) { @@ -115,6 +102,8 @@ DrawWidget::DrawWidget(QWidget * parent) DrawWidget::~DrawWidget() { + delete m_rubberBand; + m_framework->EnterBackground(); m_framework.reset(); } @@ -387,15 +376,28 @@ void DrawWidget::mousePressEvent(QMouseEvent * e) if (IsLeftButton(e)) { - if (IsRouting(e)) + if (IsShiftModifier(e)) SubmitRoutingPoint(pt); - else if (IsLocationEmulation(e)) + else if (IsAltModifier(e)) SubmitFakeLocationPoint(pt); else m_framework->TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_DOWN)); } else if (IsRightButton(e)) - ShowInfoPopup(e, pt); + { + if (!m_selectionMode || IsCommandModifier(e)) + { + ShowInfoPopup(e, pt); + } + else + { + m_rubberBandOrigin = e->pos(); + if (m_rubberBand == nullptr) + m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this); + m_rubberBand->setGeometry(QRect(m_rubberBandOrigin, QSize())); + m_rubberBand->show(); + } + } } void DrawWidget::mouseDoubleClickEvent(QMouseEvent * e) @@ -408,15 +410,33 @@ void DrawWidget::mouseDoubleClickEvent(QMouseEvent * e) void DrawWidget::mouseMoveEvent(QMouseEvent * e) { TBase::mouseMoveEvent(e); - if (IsLeftButton(e) && !IsLocationEmulation(e)) + if (IsLeftButton(e) && !IsAltModifier(e)) m_framework->TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_MOVE)); + + if (m_selectionMode && m_rubberBand != nullptr && m_rubberBand->isVisible()) + { + m_rubberBand->setGeometry(QRect(m_rubberBandOrigin, e->pos()).normalized()); + } } void DrawWidget::mouseReleaseEvent(QMouseEvent * e) { TBase::mouseReleaseEvent(e); - if (IsLeftButton(e) && !IsLocationEmulation(e)) + if (IsLeftButton(e) && !IsAltModifier(e)) + { m_framework->TouchEvent(GetTouchEvent(e, df::TouchEvent::TOUCH_UP)); + } + else if (m_selectionMode && IsRightButton(e) && m_rubberBand != nullptr && + m_rubberBand->isVisible()) + { + QPoint const lt = m_rubberBand->geometry().topLeft(); + QPoint const rb = m_rubberBand->geometry().bottomRight(); + m2::RectD rect; + rect.Add(m_framework->PtoG(m2::PointD(L2D(lt.x()), L2D(lt.y())))); + rect.Add(m_framework->PtoG(m2::PointD(L2D(rb.x()), L2D(rb.y())))); + m_framework->VizualizeRoadsInRect(rect); + m_rubberBand->hide(); + } } void DrawWidget::keyPressEvent(QKeyEvent * e) @@ -652,7 +672,7 @@ df::TouchEvent DrawWidget::GetTouchEvent(QMouseEvent * e, df::TouchEvent::ETouch df::TouchEvent event; event.SetTouchType(type); event.SetFirstTouch(GetTouch(e)); - if (IsRotation(e)) + if (IsCommandModifier(e)) event.SetSecondTouch(GetSymmetrical(event.GetFirstTouch())); return event; @@ -667,5 +687,5 @@ void DrawWidget::SetRouter(routing::RouterType routerType) { m_framework->SetRouter(routerType); } - +void DrawWidget::SetSelectionMode(bool mode) { m_selectionMode = mode; } } diff --git a/qt/draw_widget.hpp b/qt/draw_widget.hpp index ec69452f20..2e60e40d93 100644 --- a/qt/draw_widget.hpp +++ b/qt/draw_widget.hpp @@ -15,6 +15,7 @@ #include "std/unique_ptr.hpp" #include +#include class QQuickWindow; @@ -82,6 +83,8 @@ namespace qt void DownloadCountry(storage::TCountryId const & countryId); void RetryToDownloadCountry(storage::TCountryId const & countryId); + void SetSelectionMode(bool mode); + protected: void initializeGL() override; void paintGL() override; @@ -115,6 +118,8 @@ namespace qt void UpdateCountryStatus(storage::TCountryId const & countryId); QScaleSlider * m_pScale; + QRubberBand * m_rubberBand; + QPoint m_rubberBandOrigin; bool m_enableScaleUpdate; bool m_emulatingLocation; @@ -125,5 +130,7 @@ namespace qt TCurrentCountryChanged m_currentCountryChanged; storage::TCountryId m_countryId; + + bool m_selectionMode = false; }; } diff --git a/qt/mainwindow.cpp b/qt/mainwindow.cpp index 8eb1b23eff..a233206e34 100644 --- a/qt/mainwindow.cpp +++ b/qt/mainwindow.cpp @@ -278,21 +278,28 @@ void MainWindow::CreateNavigationBar() { // TODO(AlexZ): Replace icon. - m_pCreateFeatureAction = pToolBar->addAction(QIcon(":/navig64/select.png"), - tr("Create Feature"), - this, - SLOT(OnCreateFeatureClicked())); + m_pCreateFeatureAction = pToolBar->addAction(QIcon(":/navig64/select.png"), tr("Create Feature"), + this, SLOT(OnCreateFeatureClicked())); m_pCreateFeatureAction->setCheckable(true); m_pCreateFeatureAction->setToolTip(tr("Please select position on a map.")); m_pCreateFeatureAction->setShortcut(QKeySequence::New); pToolBar->addSeparator(); + m_selectionMode = pToolBar->addAction(QIcon(":/navig64/selectmode.png"), tr("Selection mode"), + this, SLOT(OnSwitchSelectionMode())); + m_selectionMode->setCheckable(true); + m_selectionMode->setToolTip(tr("Turn on/off selection mode")); + + m_clearSelection = pToolBar->addAction(QIcon(":/navig64/clear.png"), tr("Clear selection"), + this, SLOT(OnClearSelection())); + m_clearSelection->setToolTip(tr("Clear selection")); + + pToolBar->addSeparator(); + // Add search button with "checked" behavior. - m_pSearchAction = pToolBar->addAction(QIcon(":/navig64/search.png"), - tr("Search"), - this, - SLOT(OnSearchButtonClicked())); + m_pSearchAction = pToolBar->addAction(QIcon(":/navig64/search.png"), tr("Search"), + this, SLOT(OnSearchButtonClicked())); m_pSearchAction->setCheckable(true); m_pSearchAction->setToolTip(tr("Offline Search")); m_pSearchAction->setShortcut(QKeySequence::Find); @@ -475,6 +482,12 @@ void MainWindow::OnCreateFeatureClicked() } } +void MainWindow::OnSwitchSelectionMode() +{ + m_pDrawWidget->SetSelectionMode(m_selectionMode->isChecked()); +} + +void MainWindow::OnClearSelection() { m_pDrawWidget->GetFramework().GetDrapeApi().Clear(); } void MainWindow::OnSearchButtonClicked() { if (m_pSearchAction->isChecked()) diff --git a/qt/mainwindow.hpp b/qt/mainwindow.hpp index b08281159b..0812beb2a2 100644 --- a/qt/mainwindow.hpp +++ b/qt/mainwindow.hpp @@ -28,6 +28,8 @@ namespace qt { QAction * m_pMyPositionAction; QAction * m_pCreateFeatureAction; + QAction * m_selectionMode; + QAction * m_clearSelection; QAction * m_pSearchAction; DrawWidget * m_pDrawWidget; @@ -84,5 +86,8 @@ namespace qt void OnDownloadClicked(); void OnRetryDownloadClicked(); + + void OnSwitchSelectionMode(); + void OnClearSelection(); }; } diff --git a/qt/res/resources.qrc b/qt/res/resources.qrc index a7451afabd..5f53a1fe33 100644 --- a/qt/res/resources.qrc +++ b/qt/res/resources.qrc @@ -11,6 +11,9 @@ minus.png location-search.png location.png + select.png + selectmode.png + clear.png logo.png diff --git a/qt/res/selectmode.png b/qt/res/selectmode.png new file mode 100644 index 0000000000..21cbb5ea24 Binary files /dev/null and b/qt/res/selectmode.png differ