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 }