File indexing completed on 2024-05-19 13:14:18

0001 /*
0002  * breezewindowmanager.cpp
0003  * pass some window mouse press/release/move event actions to window manager
0004  * Largely inspired from BeSpin style
0005  *
0006  * SPDX-FileCopyrightText: 2007 Thomas Luebking <thomas.luebking@web.de>
0007  * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0008  *
0009  * SPDX-License-Identifier: GPL-2.0-or-later OR MIT
0010  */
0011 
0012 #include "breezewindowmanager.h"
0013 #include "breezehelper.h"
0014 #include "breezepropertynames.h"
0015 
0016 #include <QComboBox>
0017 #include <QDialog>
0018 #include <QDockWidget>
0019 #include <QGraphicsView>
0020 #include <QGroupBox>
0021 #include <QLabel>
0022 #include <QListView>
0023 #include <QMainWindow>
0024 #include <QMdiSubWindow>
0025 #include <QMenuBar>
0026 #include <QMouseEvent>
0027 #include <QProgressBar>
0028 #include <QScreen>
0029 #include <QScrollBar>
0030 #include <QStatusBar>
0031 #include <QStyle>
0032 #include <QStyleOptionGroupBox>
0033 #include <QTabBar>
0034 #include <QTabWidget>
0035 #include <QToolBar>
0036 #include <QToolButton>
0037 #include <QTreeView>
0038 
0039 #include <QTextStream>
0040 
0041 // needed to deal with device pixel ratio
0042 #include <QWindow>
0043 
0044 #if BREEZE_HAVE_QTQUICK
0045 // needed to enable dragging from QQuickWindows
0046 #include <QQuickRenderControl>
0047 #include <QQuickWindow>
0048 #endif
0049 
0050 namespace Util
0051 {
0052 template<class T>
0053 inline T makeT(std::initializer_list<typename T::key_type> &&reference)
0054 {
0055     return T(std::move(reference));
0056 }
0057 }
0058 
0059 namespace Breeze
0060 {
0061 //* provide application-wise event filter
0062 /**
0063 it us used to unlock dragging and make sure event look is properly restored
0064 after a drag has occurred
0065 */
0066 class AppEventFilter : public QObject
0067 {
0068 public:
0069     //* constructor
0070     explicit AppEventFilter(WindowManager *parent)
0071         : QObject(parent)
0072         , _parent(parent)
0073     {
0074     }
0075 
0076     //* event filter
0077     bool eventFilter(QObject *object, QEvent *event) override
0078     {
0079         if (event->type() == QEvent::MouseButtonRelease) {
0080             // stop drag timer
0081             if (_parent->_dragTimer.isActive()) {
0082                 _parent->resetDrag();
0083             }
0084 
0085             // unlock
0086             if (_parent->isLocked()) {
0087                 _parent->setLocked(false);
0088             }
0089         }
0090 
0091         if (!_parent->enabled()) {
0092             return false;
0093         }
0094 
0095         /*
0096         if a drag is in progress, the widget will not receive any event
0097         we trigger on the first MouseMove or MousePress events that are received
0098         by any widget in the application to detect that the drag is finished
0099         */
0100         if (_parent->_dragInProgress && _parent->_target && (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress)) {
0101             return appMouseEvent(object, event);
0102         }
0103 
0104         return false;
0105     }
0106 
0107 protected:
0108     //* application-wise event.
0109     /** needed to catch end of XMoveResize events */
0110     bool appMouseEvent(QObject *, QEvent *event)
0111     {
0112         Q_UNUSED(event);
0113 
0114         /*
0115         post some mouseRelease event to the target, in order to counter balance
0116         the mouse press that triggered the drag. Note that it triggers a resetDrag
0117         */
0118         QMouseEvent mouseEvent(QEvent::MouseButtonRelease, _parent->_dragPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
0119         qApp->sendEvent(_parent->_target.data(), &mouseEvent);
0120 
0121         return false;
0122     }
0123 
0124 private:
0125     //* parent
0126     WindowManager *_parent = nullptr;
0127 };
0128 
0129 //_____________________________________________________________
0130 WindowManager::WindowManager(QObject *parent)
0131     : QObject(parent)
0132 {
0133     // install application wise event filter
0134     _appEventFilter = new AppEventFilter(this);
0135     qApp->installEventFilter(_appEventFilter);
0136 }
0137 
0138 //_____________________________________________________________
0139 void WindowManager::initialize()
0140 {
0141     setEnabled(StyleConfigData::windowDragMode() != StyleConfigData::WD_NONE);
0142     setDragMode(StyleConfigData::windowDragMode());
0143     setDragDistance(QApplication::startDragDistance());
0144     setDragDelay(QApplication::startDragTime());
0145 
0146     initializeWhiteList();
0147     initializeBlackList();
0148 }
0149 
0150 //_____________________________________________________________
0151 void WindowManager::registerWidget(QWidget *widget)
0152 {
0153     if (isBlackListed(widget) || isDragable(widget) || widget->inherits("QQuickWidget")) {
0154         /*
0155         install filter for dragable widgets.
0156         also install filter for blacklisted widgets
0157         to be able to catch the relevant events and prevent
0158         the drag to happen
0159         */
0160         widget->removeEventFilter(this);
0161         widget->installEventFilter(this);
0162     }
0163 }
0164 
0165 #if BREEZE_HAVE_QTQUICK
0166 //_____________________________________________________________
0167 void WindowManager::registerQuickItem(QQuickItem *item)
0168 {
0169     if (!item) {
0170         return;
0171     }
0172 
0173     if (auto window = item->window()) {
0174         auto contentItem = window->contentItem();
0175         contentItem->setAcceptedMouseButtons(Qt::LeftButton);
0176         contentItem->removeEventFilter(this);
0177         contentItem->installEventFilter(this);
0178     }
0179 }
0180 #endif
0181 
0182 //_____________________________________________________________
0183 void WindowManager::unregisterWidget(QWidget *widget)
0184 {
0185     if (widget) {
0186         widget->removeEventFilter(this);
0187     }
0188 }
0189 
0190 //_____________________________________________________________
0191 void WindowManager::initializeWhiteList()
0192 {
0193     _whiteList = Util::makeT<ExceptionSet>({ExceptionId(QStringLiteral("MplayerWindow")),
0194                                             ExceptionId(QStringLiteral("ViewSliders@kmix")),
0195                                             ExceptionId(QStringLiteral("Sidebar_Widget@konqueror"))});
0196 
0197     foreach (const QString &exception, StyleConfigData::windowDragWhiteList()) {
0198         ExceptionId id(exception);
0199         if (!id.className().isEmpty()) {
0200             _whiteList.insert(ExceptionId(exception));
0201         }
0202     }
0203 }
0204 
0205 //_____________________________________________________________
0206 void WindowManager::initializeBlackList()
0207 {
0208     _blackList = Util::makeT<ExceptionSet>(
0209         {ExceptionId(QStringLiteral("CustomTrackView@kdenlive")), ExceptionId(QStringLiteral("MuseScore")), ExceptionId(QStringLiteral("KGameCanvasWidget"))});
0210 
0211     foreach (const QString &exception, StyleConfigData::windowDragBlackList()) {
0212         ExceptionId id(exception);
0213         if (!id.className().isEmpty()) {
0214             _blackList.insert(ExceptionId(exception));
0215         }
0216     }
0217 }
0218 
0219 //_____________________________________________________________
0220 bool WindowManager::eventFilter(QObject *object, QEvent *event)
0221 {
0222     if (!enabled()) {
0223         return false;
0224     }
0225 
0226     switch (event->type()) {
0227     case QEvent::MouseButtonPress:
0228         return mousePressEvent(object, event);
0229         break;
0230 
0231     case QEvent::MouseMove:
0232         if (object == _target.data()
0233 #if BREEZE_HAVE_QTQUICK
0234             || object == _quickTarget.data()
0235 #endif
0236         ) {
0237             return mouseMoveEvent(object, event);
0238         }
0239         break;
0240 
0241     case QEvent::MouseButtonRelease:
0242         if (_target
0243 #if BREEZE_HAVE_QTQUICK
0244             || _quickTarget
0245 #endif
0246         ) {
0247             return mouseReleaseEvent(object, event);
0248         }
0249         break;
0250 
0251     default:
0252         break;
0253     }
0254 
0255     return false;
0256 }
0257 
0258 //_____________________________________________________________
0259 void WindowManager::timerEvent(QTimerEvent *event)
0260 {
0261     if (event->timerId() == _dragTimer.timerId()) {
0262         _dragTimer.stop();
0263         setLocked(false);
0264         if (_target) {
0265             startDrag(_target.data()->window()->windowHandle());
0266         }
0267 #if BREEZE_HAVE_QTQUICK
0268         else if (_quickTarget) {
0269             _quickTarget.data()->ungrabMouse();
0270             startDrag(_quickTarget.data()->window());
0271         }
0272 #endif
0273         resetDrag();
0274     } else {
0275         return QObject::timerEvent(event);
0276     }
0277 }
0278 
0279 //_____________________________________________________________
0280 bool WindowManager::mousePressEvent(QObject *object, QEvent *event)
0281 {
0282     // cast event and check buttons/modifiers
0283     auto mouseEvent = static_cast<QMouseEvent *>(event);
0284     if (mouseEvent->source() != Qt::MouseEventNotSynthesized) {
0285         return false;
0286     }
0287     if (!(mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton)) {
0288         return false;
0289     }
0290 
0291     // If we are in a QQuickWidget we don't want to ever do dragging from a qwidget in the
0292     // hyerarchy, but only from an internal item, if any. If any event handler will manage
0293     // the event, we don't want the drag to start
0294     if (object->inherits("QQuickWidget")) {
0295         _eventInQQuickWidget = true;
0296         event->setAccepted(false);
0297         return false;
0298     } else {
0299         _eventInQQuickWidget = false;
0300     }
0301 
0302     // check lock
0303     if (isLocked()) {
0304         return false;
0305     } else {
0306         setLocked(true);
0307     }
0308 
0309 #if BREEZE_HAVE_QTQUICK
0310     // check QQuickItem - we can immediately start drag, because QQuickWindow's contentItem
0311     // only receives mouse events that weren't handled by children
0312     if (auto item = qobject_cast<QQuickItem *>(object)) {
0313         _quickTarget = item;
0314         _dragPoint = mouseEvent->pos();
0315         _globalDragPoint = mouseEvent->globalPos();
0316 
0317         if (_dragTimer.isActive()) {
0318             _dragTimer.stop();
0319         }
0320         _dragTimer.start(_dragDelay, this);
0321 
0322         return true;
0323     }
0324 #endif
0325 
0326     if (_eventInQQuickWidget) {
0327         event->setAccepted(true);
0328         return false;
0329     }
0330     _eventInQQuickWidget = false;
0331 
0332     // cast to widget
0333     auto widget = static_cast<QWidget *>(object);
0334 
0335     // check if widget can be dragged from current position
0336     if (isBlackListed(widget) || !canDrag(widget)) {
0337         return false;
0338     }
0339 
0340     // retrieve widget's child at event position
0341     auto position(mouseEvent->pos());
0342     auto child = widget->childAt(position);
0343     if (!canDrag(widget, child, position)) {
0344         return false;
0345     }
0346 
0347     // save target and drag point
0348     _target = widget;
0349     _dragPoint = position;
0350     _globalDragPoint = mouseEvent->globalPos();
0351     _dragAboutToStart = true;
0352 
0353     // send a move event to the current child with same position
0354     // if received, it is caught to actually start the drag
0355     auto localPoint(_dragPoint);
0356     if (child) {
0357         localPoint = child->mapFrom(widget, localPoint);
0358     } else {
0359         child = widget;
0360     }
0361     QMouseEvent localMouseEvent(QEvent::MouseMove, localPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
0362     qApp->sendEvent(child, &localMouseEvent);
0363 
0364     // never eat event
0365     return false;
0366 }
0367 
0368 //_____________________________________________________________
0369 bool WindowManager::mouseMoveEvent(QObject *object, QEvent *event)
0370 {
0371     Q_UNUSED(object);
0372 
0373     // stop timer
0374     if (_dragTimer.isActive()) {
0375         _dragTimer.stop();
0376     }
0377 
0378     // cast event and check drag distance
0379     auto mouseEvent = static_cast<QMouseEvent *>(event);
0380     if (mouseEvent->source() != Qt::MouseEventNotSynthesized) {
0381         return false;
0382     }
0383     if (!_dragInProgress) {
0384         if (_dragAboutToStart) {
0385             if (mouseEvent->pos() == _dragPoint) {
0386                 // start timer,
0387                 _dragAboutToStart = false;
0388                 if (_dragTimer.isActive()) {
0389                     _dragTimer.stop();
0390                 }
0391                 _dragTimer.start(_dragDelay, this);
0392 
0393             } else {
0394                 resetDrag();
0395             }
0396 
0397         } else if (QPoint(mouseEvent->globalPos() - _globalDragPoint).manhattanLength() >= _dragDistance) {
0398             _dragTimer.start(0, this);
0399         }
0400 
0401         return true;
0402 
0403     } else {
0404         return false;
0405     }
0406 }
0407 
0408 //_____________________________________________________________
0409 bool WindowManager::mouseReleaseEvent(QObject *object, QEvent *event)
0410 {
0411     Q_UNUSED(object);
0412     Q_UNUSED(event);
0413     resetDrag();
0414     return false;
0415 }
0416 
0417 //_____________________________________________________________
0418 bool WindowManager::isDragable(QWidget *widget)
0419 {
0420     // check widget
0421     if (!widget) {
0422         return false;
0423     }
0424 
0425     // accepted default types
0426     if ((qobject_cast<QDialog *>(widget) && widget->isWindow()) || (qobject_cast<QMainWindow *>(widget) && widget->isWindow())
0427         || qobject_cast<QGroupBox *>(widget)) {
0428         return true;
0429     }
0430 
0431     // more accepted types, provided they are not dock widget titles
0432     if ((qobject_cast<QMenuBar *>(widget) || qobject_cast<QTabBar *>(widget) || qobject_cast<QStatusBar *>(widget) || qobject_cast<QToolBar *>(widget))
0433         && !isDockWidgetTitle(widget)) {
0434         return true;
0435     }
0436 
0437     if (widget->inherits("KScreenSaver") && widget->inherits("KCModule")) {
0438         return true;
0439     }
0440 
0441     if (isWhiteListed(widget)) {
0442         return true;
0443     }
0444 
0445     // flat toolbuttons
0446     if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
0447         if (toolButton->autoRaise()) {
0448             return true;
0449         }
0450     }
0451 
0452     // viewports
0453     /*
0454     one needs to check that
0455     1/ the widget parent is a scrollarea
0456     2/ it matches its parent viewport
0457     3/ the parent is not blacklisted
0458     */
0459     if (auto listView = qobject_cast<QListView *>(widget->parentWidget())) {
0460         if (listView->viewport() == widget && !isBlackListed(listView)) {
0461             return true;
0462         }
0463     }
0464 
0465     if (auto treeView = qobject_cast<QTreeView *>(widget->parentWidget())) {
0466         if (treeView->viewport() == widget && !isBlackListed(treeView)) {
0467             return true;
0468         }
0469     }
0470 
0471     /*
0472     catch labels in status bars.
0473     this is because of kstatusbar
0474     who captures buttonPress/release events
0475     */
0476     if (auto label = qobject_cast<QLabel *>(widget)) {
0477         if (label->textInteractionFlags().testFlag(Qt::TextSelectableByMouse)) {
0478             return false;
0479         }
0480 
0481         QWidget *parent = label->parentWidget();
0482         while (parent) {
0483             if (qobject_cast<QStatusBar *>(parent)) {
0484                 return true;
0485             }
0486             parent = parent->parentWidget();
0487         }
0488     }
0489 
0490     return false;
0491 }
0492 
0493 //_____________________________________________________________
0494 bool WindowManager::isBlackListed(QWidget *widget)
0495 {
0496     // check against noAnimations property
0497     const auto propertyValue(widget->property(PropertyNames::noWindowGrab));
0498     if (propertyValue.isValid() && propertyValue.toBool()) {
0499         return true;
0500     }
0501 
0502     // list-based blacklisted widgets
0503     const auto appName(qApp->applicationName());
0504     foreach (const ExceptionId &id, _blackList) {
0505         if (!id.appName().isEmpty() && id.appName() != appName) {
0506             continue;
0507         }
0508         if (id.className() == QStringLiteral("*") && !id.appName().isEmpty()) {
0509             // if application name matches and all classes are selected
0510             // disable the grabbing entirely
0511             setEnabled(false);
0512             return true;
0513         }
0514         if (widget->inherits(id.className().toLatin1().data())) {
0515             return true;
0516         }
0517     }
0518 
0519     return false;
0520 }
0521 
0522 //_____________________________________________________________
0523 bool WindowManager::isWhiteListed(QWidget *widget) const
0524 {
0525     const auto appName(qApp->applicationName());
0526     foreach (const ExceptionId &id, _whiteList) {
0527         if (!(id.appName().isEmpty() || id.appName() == appName)) {
0528             continue;
0529         }
0530         if (widget->inherits(id.className().toLatin1().data())) {
0531             return true;
0532         }
0533     }
0534 
0535     return false;
0536 }
0537 
0538 //_____________________________________________________________
0539 bool WindowManager::canDrag(QWidget *widget)
0540 {
0541     // check if enabled
0542     if (!enabled()) {
0543         return false;
0544     }
0545 
0546     // assume isDragable widget is already passed
0547     // check some special cases where drag should not be effective
0548 
0549     // check mouse grabber
0550     if (QWidget::mouseGrabber()) {
0551         return false;
0552     }
0553 
0554     /*
0555     check cursor shape.
0556     Assume that a changed cursor means that some action is in progress
0557     and should prevent the drag
0558     */
0559     if (widget->cursor().shape() != Qt::ArrowCursor) {
0560         return false;
0561     }
0562 
0563     // accept
0564     return true;
0565 }
0566 
0567 //_____________________________________________________________
0568 bool WindowManager::canDrag(QWidget *widget, QWidget *child, const QPoint &position)
0569 {
0570     // retrieve child at given position and check cursor again
0571     if (child && child->cursor().shape() != Qt::ArrowCursor) {
0572         return false;
0573     }
0574 
0575     /*
0576     check against children from which drag should never be enabled,
0577     even if mousePress/Move has been passed to the parent
0578     */
0579     if (child && (qobject_cast<QComboBox *>(child) || qobject_cast<QProgressBar *>(child) || qobject_cast<QScrollBar *>(child))) {
0580         return false;
0581     }
0582 
0583     // tool buttons
0584     if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
0585         if (dragMode() == StyleConfigData::WD_MINIMAL && !qobject_cast<QToolBar *>(widget->parentWidget())) {
0586             return false;
0587         }
0588         return toolButton->autoRaise() && !toolButton->isEnabled();
0589     }
0590 
0591     // check menubar
0592     if (auto menuBar = qobject_cast<QMenuBar *>(widget)) {
0593         // do not drag from menubars embedded in Mdi windows
0594         if (findParent<QMdiSubWindow *>(widget)) {
0595             return false;
0596         }
0597 
0598         // check if there is an active action
0599         if (menuBar->activeAction() && menuBar->activeAction()->isEnabled()) {
0600             return false;
0601         }
0602 
0603         // check if action at position exists and is enabled
0604         if (auto action = menuBar->actionAt(position)) {
0605             if (action->isSeparator()) {
0606                 return true;
0607             }
0608             if (action->isEnabled()) {
0609                 return false;
0610             }
0611         }
0612 
0613         // return true in all other cases
0614         return true;
0615     }
0616 
0617     /*
0618     in MINIMAL mode, anything that has not been already accepted
0619     and does not come from a toolbar is rejected
0620     */
0621     if (dragMode() == StyleConfigData::WD_MINIMAL) {
0622         if (qobject_cast<QToolBar *>(widget)) {
0623             return true;
0624         } else {
0625             return false;
0626         }
0627     }
0628 
0629     /* following checks are relevant only for WD_FULL mode */
0630 
0631     // tabbar. Make sure no tab is under the cursor
0632     if (auto tabBar = qobject_cast<QTabBar *>(widget)) {
0633         return tabBar->tabAt(position) == -1;
0634     }
0635 
0636     /*
0637     check groupboxes
0638     prevent drag if unchecking grouboxes
0639     */
0640     if (auto groupBox = qobject_cast<QGroupBox *>(widget)) {
0641         // non checkable group boxes are always ok
0642         if (!groupBox->isCheckable()) {
0643             return true;
0644         }
0645 
0646         // gather options to retrieve checkbox subcontrol rect
0647         QStyleOptionGroupBox opt;
0648         opt.initFrom(groupBox);
0649         if (groupBox->isFlat()) {
0650             opt.features |= QStyleOptionFrame::Flat;
0651         }
0652         opt.lineWidth = 1;
0653         opt.midLineWidth = 0;
0654         opt.text = groupBox->title();
0655         opt.textAlignment = groupBox->alignment();
0656         opt.subControls = (QStyle::SC_GroupBoxFrame | QStyle::SC_GroupBoxCheckBox);
0657         if (!groupBox->title().isEmpty()) {
0658             opt.subControls |= QStyle::SC_GroupBoxLabel;
0659         }
0660 
0661         opt.state |= (groupBox->isChecked() ? QStyle::State_On : QStyle::State_Off);
0662 
0663         // check against groupbox checkbox
0664         if (groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, groupBox).contains(position)) {
0665             return false;
0666         }
0667 
0668         // check against groupbox label
0669         if (!groupBox->title().isEmpty()
0670             && groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, groupBox).contains(position)) {
0671             return false;
0672         }
0673 
0674         return true;
0675     }
0676 
0677     // labels
0678     if (auto label = qobject_cast<QLabel *>(widget)) {
0679         if (label->textInteractionFlags().testFlag(Qt::TextSelectableByMouse)) {
0680             return false;
0681         }
0682     }
0683 
0684     // abstract item views
0685     QAbstractItemView *itemView(nullptr);
0686     if ((itemView = qobject_cast<QListView *>(widget->parentWidget())) || (itemView = qobject_cast<QTreeView *>(widget->parentWidget()))) {
0687         if (widget == itemView->viewport()) {
0688             // QListView
0689             if (itemView->frameShape() != QFrame::NoFrame) {
0690                 return false;
0691             } else if (itemView->selectionMode() != QAbstractItemView::NoSelection && itemView->selectionMode() != QAbstractItemView::SingleSelection
0692                        && itemView->model() && itemView->model()->rowCount()) {
0693                 return false;
0694             } else if (itemView->model() && itemView->indexAt(position).isValid()) {
0695                 return false;
0696             }
0697         }
0698 
0699     } else if ((itemView = qobject_cast<QAbstractItemView *>(widget->parentWidget()))) {
0700         if (widget == itemView->viewport()) {
0701             // QAbstractItemView
0702             if (itemView->frameShape() != QFrame::NoFrame) {
0703                 return false;
0704             } else if (itemView->indexAt(position).isValid()) {
0705                 return false;
0706             }
0707         }
0708 
0709     } else if (auto graphicsView = qobject_cast<QGraphicsView *>(widget->parentWidget())) {
0710         if (widget == graphicsView->viewport()) {
0711             // QGraphicsView
0712             if (graphicsView->frameShape() != QFrame::NoFrame) {
0713                 return false;
0714             } else if (graphicsView->dragMode() != QGraphicsView::NoDrag) {
0715                 return false;
0716             } else if (graphicsView->itemAt(position)) {
0717                 return false;
0718             }
0719         }
0720     }
0721 
0722     return true;
0723 }
0724 
0725 //____________________________________________________________
0726 void WindowManager::resetDrag()
0727 {
0728     _target.clear();
0729 #if BREEZE_HAVE_QTQUICK
0730     _quickTarget.clear();
0731 #endif
0732     if (_dragTimer.isActive()) {
0733         _dragTimer.stop();
0734     }
0735     _dragPoint = QPoint();
0736     _globalDragPoint = QPoint();
0737     _dragAboutToStart = false;
0738     _dragInProgress = false;
0739 }
0740 
0741 //____________________________________________________________
0742 void WindowManager::startDrag(QWindow *window)
0743 {
0744     if (!(enabled() && window)) {
0745         return;
0746     }
0747     if (QWidget::mouseGrabber()) {
0748         return;
0749     }
0750 
0751 #if BREEZE_HAVE_QTQUICK
0752     if (_quickTarget) {
0753         if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(window)) {
0754             QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qw);
0755             if (renderWindow) {
0756                 _dragInProgress = renderWindow->startSystemMove();
0757             } else {
0758                 _dragInProgress = window->startSystemMove();
0759             }
0760         }
0761     } else
0762 #endif
0763     {
0764         _dragInProgress = window->startSystemMove();
0765     }
0766 }
0767 
0768 //____________________________________________________________
0769 bool WindowManager::isDockWidgetTitle(const QWidget *widget) const
0770 {
0771     if (!widget) {
0772         return false;
0773     }
0774     if (auto dockWidget = qobject_cast<const QDockWidget *>(widget->parent())) {
0775         return widget == dockWidget->titleBarWidget();
0776 
0777     } else {
0778         return false;
0779     }
0780 }
0781 
0782 }