File indexing completed on 2024-05-12 05:39:19

0001 #include "SelectionEditor.h"
0002 
0003 #include <KLocalizedContext>
0004 #include <QGuiApplication>
0005 #include <QQmlApplicationEngine>
0006 #include <QQmlContext>
0007 #include <QQuickView>
0008 #include <QQuickWindow>
0009 #include <QScreen>
0010 
0011 class SelectionEditorPrivate
0012 {
0013 public:
0014     SelectionEditorPrivate(SelectionEditor *q);
0015     ~SelectionEditorPrivate();
0016 
0017     SelectionEditor *const q;
0018 
0019     void beginDragOperation(const QPointF &pos);
0020     void endDragOperation();
0021     void reset();
0022 
0023     bool setMouseCursor(const QPointF &pos);
0024 
0025     bool isDragging() const;
0026 
0027     QRectF rect() const;
0028 
0029 private:
0030     QPointF startPosition;
0031     bool dragging = false;
0032     QRectF selectedRegion;
0033 };
0034 
0035 SelectionEditorPrivate::SelectionEditorPrivate(SelectionEditor *q)
0036     : q(q)
0037 {
0038 }
0039 
0040 SelectionEditorPrivate::~SelectionEditorPrivate()
0041 {
0042 }
0043 
0044 void SelectionEditorPrivate::beginDragOperation(const QPointF &pos)
0045 {
0046     dragging = true;
0047     startPosition = pos;
0048 }
0049 
0050 void SelectionEditorPrivate::endDragOperation()
0051 {
0052     dragging = false;
0053 }
0054 
0055 void SelectionEditorPrivate::reset()
0056 {
0057     endDragOperation();
0058     startPosition = {0.f, 0.f};
0059     selectedRegion = {0.f, 0.f, 0.f, 0.f};
0060 }
0061 
0062 bool SelectionEditorPrivate::setMouseCursor(const QPointF &pos)
0063 {
0064     if (dragging) {
0065         selectedRegion =
0066             QRectF(qMin(startPosition.x(), pos.x()), qMin(startPosition.y(), pos.y()), qAbs(pos.x() - startPosition.x()), qAbs(pos.y() - startPosition.y()));
0067 
0068         return true;
0069     }
0070 
0071     return false;
0072 }
0073 
0074 bool SelectionEditorPrivate::isDragging() const
0075 {
0076     return dragging;
0077 }
0078 
0079 QRectF SelectionEditorPrivate::rect() const
0080 {
0081     return selectedRegion;
0082 }
0083 
0084 SelectionEditor::SelectionEditor(QObject *parent)
0085     : QObject(parent)
0086     , d(new SelectionEditorPrivate(this))
0087     , m_engine(new QQmlApplicationEngine(this))
0088 {
0089     auto context = new KLocalizedContext(m_engine);
0090     context->setTranslationDomain(TRANSLATION_DOMAIN);
0091     m_engine->rootContext()->setContextObject(context);
0092     m_engine->rootContext()->setContextProperty(QStringLiteral("SelectionEditor"), QVariant::fromValue<QObject *>(this));
0093 
0094     setObjectName(QStringLiteral("selectionEditor"));
0095 
0096     showViews();
0097     connect(qApp, &QGuiApplication::screenAdded, this, &SelectionEditor::showViews);
0098     connect(qApp, &QGuiApplication::screenRemoved, this, &SelectionEditor::showViews);
0099     connect(qApp, &QGuiApplication::primaryScreenChanged, this, &SelectionEditor::showViews);
0100     connect(qApp, &QGuiApplication::focusWindowChanged, this, [this](QWindow *newWindow) {
0101         bool focusLost = true;
0102 
0103         if (newWindow) {
0104             for (auto *view : m_views) {
0105                 if (view && newWindow->winId() == view->winId()) {
0106                     focusLost = false;
0107                     break;
0108                 }
0109             }
0110         }
0111         if (focusLost) {
0112             this->reject();
0113         }
0114     });
0115 }
0116 
0117 void SelectionEditor::showViews()
0118 {
0119     // delete existing views, since this function
0120     // may be called when workspace geometry changes
0121     for (auto *view : m_views) {
0122         view->deleteLater();
0123     }
0124 
0125     for (auto *screen : qApp->screens()) {
0126         auto view = new QQuickView(m_engine, nullptr);
0127 
0128         view->setScreen(screen);
0129 
0130         view->setSource(QUrl(QStringLiteral("qrc:/region-select/RegionSelectOverlay.qml")));
0131         view->installEventFilter(this);
0132 
0133         view->setResizeMode(QQuickView::SizeRootObjectToView);
0134         view->setColor(Qt::transparent);
0135         view->setFlags({
0136             Qt::Window, // the default window flag
0137             Qt::FramelessWindowHint,
0138             Qt::NoDropShadowWindowHint,
0139             Qt::MaximizeUsingFullscreenGeometryHint, // also use the areas where system UIs are
0140         });
0141         view->setWindowStates(Qt::WindowFullScreen);
0142         view->setVisibility(QWindow::Visibility::FullScreen);
0143 
0144         m_views << view;
0145     }
0146 }
0147 
0148 bool SelectionEditor::isDragging() const
0149 {
0150     return d->isDragging();
0151 }
0152 
0153 QRect SelectionEditor::rect() const
0154 {
0155     return d->rect().toRect();
0156 }
0157 
0158 bool SelectionEditor::eventFilter(QObject *watched, QEvent *event)
0159 {
0160     switch (event->type()) {
0161     case QEvent::KeyPress:
0162         keyPressEvent(static_cast<QKeyEvent *>(event));
0163         break;
0164     default:
0165         break;
0166     }
0167     return false;
0168 }
0169 
0170 void SelectionEditor::dragStart(const QString &screenName, int x, int y)
0171 {
0172     const auto screens = QGuiApplication::screens();
0173     auto screenIt = std::find_if(screens.begin(), screens.end(), [&screenName](auto screen) {
0174         return screen->name() == screenName;
0175     });
0176     d->beginDragOperation((*screenIt)->geometry().topLeft() + QPoint{x, y});
0177     Q_EMIT isDraggingChanged();
0178 }
0179 
0180 void SelectionEditor::setMousePosition(const QString &screenName, int x, int y)
0181 {
0182     const auto screens = QGuiApplication::screens();
0183     auto screenIt = std::find_if(screens.begin(), screens.end(), [&screenName](auto screen) {
0184         return screen->name() == screenName;
0185     });
0186     if (d->setMouseCursor((*screenIt)->geometry().topLeft() + QPoint{x, y})) {
0187         Q_EMIT rectChanged();
0188     }
0189 }
0190 
0191 void SelectionEditor::dragRelease(const QString &screenName, int x, int y)
0192 {
0193     const auto screens = QGuiApplication::screens();
0194     auto screenIt = std::find_if(screens.begin(), screens.end(), [&screenName](auto screen) {
0195         return screen->name() == screenName;
0196     });
0197     if (d->isDragging() && d->setMouseCursor((*screenIt)->geometry().topLeft() + QPoint{x, y})) {
0198         d->endDragOperation();
0199         Q_EMIT isDraggingChanged();
0200 
0201         // portal will error out if we try to stream an invalid region
0202         if (d->rect().isValid()) {
0203             accept();
0204         }
0205     }
0206 }
0207 
0208 void SelectionEditor::dragReset()
0209 {
0210     d->reset();
0211     Q_EMIT rectChanged();
0212     Q_EMIT isDraggingChanged();
0213 }
0214 
0215 void SelectionEditor::keyPressEvent(QKeyEvent *event)
0216 {
0217     switch (event->key()) {
0218     case Qt::Key_Escape:
0219         reject();
0220         event->accept();
0221         break;
0222     default:
0223         break;
0224     }
0225 }
0226 
0227 SelectionEditor::~SelectionEditor() noexcept
0228 {
0229     for (auto *view : m_views) {
0230         view->deleteLater();
0231     }
0232     delete d;
0233     delete m_engine;
0234 }
0235 
0236 bool SelectionEditor::exec()
0237 {
0238     return m_execLoop.exec() == 0;
0239 }
0240 
0241 void SelectionEditor::accept()
0242 {
0243     m_execLoop.quit();
0244 }
0245 void SelectionEditor::reject()
0246 {
0247     m_execLoop.exit(1);
0248 }