File indexing completed on 2024-05-19 05:42:21

0001 // ct_lvtqtw_minimap.h                                 -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtqtc_minimap.h>
0021 
0022 #include <QGraphicsScene>
0023 #include <QGuiApplication>
0024 #include <QMenu>
0025 #include <QMouseEvent>
0026 #include <QScreen>
0027 
0028 #include <preferences.h>
0029 
0030 namespace Codethink::lvtqtc {
0031 
0032 struct Minimap::Private {
0033     bool isDraggingScene = false;
0034     bool isDraggingSelf = false;
0035     QPoint movementDelta;
0036     QRectF sceneRect;
0037 
0038     int minimapSize = Minimap::MINIMAP_SMALL;
0039 };
0040 
0041 Minimap::Minimap(QGraphicsScene *mainScene, QWidget *parent):
0042     QGraphicsView(parent), d(std::make_unique<Minimap::Private>())
0043 {
0044     setScene(mainScene);
0045     setBackgroundBrush(QBrush(Qt::white));
0046     setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::TextAntialiasing
0047                    | QPainter::RenderHint::SmoothPixmapTransform);
0048 
0049     setCacheMode(QGraphicsView::CacheModeFlag::CacheNone);
0050 
0051     setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
0052     setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
0053 
0054     setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
0055 
0056     d->minimapSize = Preferences::minimapSize();
0057 
0058     setGeometry(QRect(0, 0, 100, 80));
0059 
0060     connect(scene(), &QGraphicsScene::sceneRectChanged, this, [this] {
0061         calculateGeometry();
0062     });
0063 }
0064 
0065 Minimap::~Minimap() = default;
0066 
0067 void Minimap::setSceneRect(const QRectF& rect)
0068 {
0069     if (d->sceneRect == rect) {
0070         return;
0071     }
0072     d->sceneRect = rect;
0073     scene()->update();
0074 }
0075 
0076 void Minimap::calculateGeometry()
0077 {
0078     QScreen *thisScreen = QGuiApplication::primaryScreen();
0079     if (!thisScreen) {
0080         return;
0081     }
0082 
0083     const qreal sceneWidth = scene()->sceneRect().width();
0084     const qreal sceneHeight = scene()->sceneRect().height();
0085 
0086     if (sceneWidth == 0 || sceneHeight == 0) {
0087         setGeometry(QRect(0, 0, 100, 80));
0088         return;
0089     }
0090 
0091     int thisWidth = 0;
0092     int thisHeight = 0;
0093 
0094     if (sceneWidth > sceneHeight) {
0095         thisWidth = thisScreen->geometry().width() / (int) d->minimapSize;
0096         assert(sceneHeight < INT_MAX && scene()->sceneRect().width() < INT_MAX);
0097         thisHeight = (thisWidth * (int) sceneHeight) / (int) sceneWidth;
0098     } else {
0099         thisHeight = thisScreen->geometry().height() / (int) d->minimapSize;
0100         assert(sceneHeight < INT_MAX && scene()->sceneRect().width() < INT_MAX);
0101         thisWidth = (thisHeight * (int) sceneWidth) / (int) sceneHeight;
0102     }
0103 
0104     QRect geom = geometry();
0105     geom.setWidth(thisWidth);
0106     geom.setHeight(thisHeight);
0107     if (geom.width() < 100 || geom.height() < 100) {
0108         geom.setWidth(geom.width() * 2);
0109         geom.setHeight(geom.height() * 2);
0110     }
0111 
0112     setGeometry(geom);
0113     fitInView(scene()->sceneRect());
0114 }
0115 
0116 void Minimap::setMapSize(MinimapSize size)
0117 {
0118     if (d->minimapSize == size) {
0119         return;
0120     }
0121 
0122     d->minimapSize = size;
0123     Preferences::setMinimapSize(size);
0124 
0125     calculateGeometry();
0126 }
0127 
0128 void Minimap::contextMenuEvent(QContextMenuEvent *ev)
0129 {
0130     QMenu menu;
0131     menu.addAction(tr("Select Minimap Size"));
0132     menu.addSeparator();
0133     QAction *ac = menu.addAction(tr("Small"));
0134     connect(ac, &QAction::triggered, this, [this] {
0135         setMapSize(MinimapSize::MINIMAP_SMALL);
0136     });
0137 
0138     ac = menu.addAction(tr("Medium"));
0139     connect(ac, &QAction::triggered, this, [this] {
0140         setMapSize(MinimapSize::MINIMAP_MEDIUM);
0141     });
0142 
0143     ac = menu.addAction(tr("Large"));
0144     connect(ac, &QAction::triggered, this, [this] {
0145         setMapSize(MinimapSize::MINIMAP_LARGE);
0146     });
0147 
0148     menu.exec(ev->globalPos());
0149 }
0150 
0151 void Minimap::drawForeground(QPainter *painter, const QRectF& rect)
0152 {
0153     if (d->sceneRect.width() > rect.width()) {
0154         return;
0155     }
0156     if (d->sceneRect.height() > rect.height()) {
0157         return;
0158     }
0159 
0160     painter->save();
0161     painter->setPen(QPen(Qt::NoPen));
0162     painter->setBrush(QBrush(QColor(100, 100, 100, 100)));
0163     painter->drawRect(d->sceneRect);
0164     painter->restore();
0165 }
0166 
0167 void Minimap::mousePressEvent(QMouseEvent *ev)
0168 {
0169     if (ev->modifiers() & Preferences::dragModifier()) {
0170         d->isDraggingScene = false;
0171         d->isDraggingSelf = true;
0172 
0173         QLine line(mapToParent(ev->pos()), pos());
0174         d->movementDelta.setX(line.dx());
0175         d->movementDelta.setY(line.dy());
0176     } else {
0177         d->isDraggingScene = true;
0178         d->isDraggingSelf = false;
0179         Q_EMIT focusSceneAt(mapToScene(ev->localPos().toPoint()));
0180     }
0181 }
0182 
0183 void Minimap::mouseMoveEvent(QMouseEvent *ev)
0184 {
0185     if (d->isDraggingScene) {
0186         Q_EMIT focusSceneAt(mapToScene(ev->localPos().toPoint()));
0187     } else if (d->isDraggingSelf) {
0188         QRect pos = geometry();
0189         pos.moveTo(mapToParent(ev->pos()) + d->movementDelta);
0190         setGeometry(pos);
0191     }
0192 }
0193 
0194 void Minimap::mouseReleaseEvent(QMouseEvent *ev)
0195 {
0196     Q_UNUSED(ev);
0197     d->isDraggingScene = false;
0198     d->isDraggingSelf = false;
0199     QGraphicsView::mouseReleaseEvent(ev);
0200 }
0201 
0202 // TODO: Show a rect on the current visible part of the graph.
0203 } // namespace Codethink::lvtqtc