File indexing completed on 2024-04-28 05:26:27

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, QCursor::pos(), 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     const auto windowDragWhiteList = StyleConfigData::windowDragWhiteList();
0198     for (const QString &exception : windowDragWhiteList) {
0199         ExceptionId id(exception);
0200         if (!id.className().isEmpty()) {
0201             _whiteList.insert(ExceptionId(exception));
0202         }
0203     }
0204 }
0205 
0206 //_____________________________________________________________
0207 void WindowManager::initializeBlackList()
0208 {
0209     _blackList = Util::makeT<ExceptionSet>(
0210         {ExceptionId(QStringLiteral("CustomTrackView@kdenlive")), ExceptionId(QStringLiteral("MuseScore")), ExceptionId(QStringLiteral("KGameCanvasWidget"))});
0211 
0212     const auto windowDragBlackList = StyleConfigData::windowDragBlackList();
0213     for (const QString &exception : windowDragBlackList) {
0214         ExceptionId id(exception);
0215         if (!id.className().isEmpty()) {
0216             _blackList.insert(ExceptionId(exception));
0217         }
0218     }
0219 }
0220 
0221 //_____________________________________________________________
0222 bool WindowManager::eventFilter(QObject *object, QEvent *event)
0223 {
0224     if (!enabled()) {
0225         return false;
0226     }
0227 
0228     switch (event->type()) {
0229     case QEvent::MouseButtonPress:
0230         return mousePressEvent(object, event);
0231         break;
0232 
0233     case QEvent::MouseMove:
0234         if (object == _target.data()
0235 #if BREEZE_HAVE_QTQUICK
0236             || object == _quickTarget.data()
0237 #endif
0238         ) {
0239             return mouseMoveEvent(object, event);
0240         }
0241         break;
0242 
0243     case QEvent::MouseButtonRelease:
0244         if (_target
0245 #if BREEZE_HAVE_QTQUICK
0246             || _quickTarget
0247 #endif
0248         ) {
0249             return mouseReleaseEvent(object, event);
0250         }
0251         break;
0252 
0253     default:
0254         break;
0255     }
0256 
0257     return false;
0258 }
0259 
0260 //_____________________________________________________________
0261 void WindowManager::timerEvent(QTimerEvent *event)
0262 {
0263     if (event->timerId() == _dragTimer.timerId()) {
0264         _dragTimer.stop();
0265         setLocked(false);
0266         if (_target) {
0267             startDrag(_target.data()->window()->windowHandle());
0268         }
0269 #if BREEZE_HAVE_QTQUICK
0270         else if (_quickTarget) {
0271             _quickTarget.data()->ungrabMouse();
0272             startDrag(_quickTarget.data()->window());
0273         }
0274 #endif
0275         resetDrag();
0276     } else {
0277         return QObject::timerEvent(event);
0278     }
0279 }
0280 
0281 //_____________________________________________________________
0282 bool WindowManager::mousePressEvent(QObject *object, QEvent *event)
0283 {
0284     // cast event and check buttons/modifiers
0285     auto mouseEvent = static_cast<QMouseEvent *>(event);
0286     if (mouseEvent->source() != Qt::MouseEventNotSynthesized) {
0287         return false;
0288     }
0289     if (!(mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton)) {
0290         return false;
0291     }
0292 
0293     // If we are in a QQuickWidget we don't want to ever do dragging from a qwidget in the
0294     // hyerarchy, but only from an internal item, if any. If any event handler will manage
0295     // the event, we don't want the drag to start
0296     if (object->inherits("QQuickWidget")) {
0297         _eventInQQuickWidget = true;
0298         event->setAccepted(false);
0299         return false;
0300     } else {
0301         _eventInQQuickWidget = false;
0302     }
0303 
0304     // check lock
0305     if (isLocked()) {
0306         return false;
0307     } else {
0308         setLocked(true);
0309     }
0310 
0311 #if BREEZE_HAVE_QTQUICK
0312     // check QQuickItem - we can immediately start drag, because QQuickWindow's contentItem
0313     // only receives mouse events that weren't handled by children
0314     if (auto item = qobject_cast<QQuickItem *>(object)) {
0315         _quickTarget = item;
0316         _dragPoint = mouseEvent->pos();
0317         _globalDragPoint =
0318 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0319             mouseEvent->globalPosition().toPoint();
0320 #else
0321             mouseEvent->globalPos();
0322 #endif
0323 
0324         if (_dragTimer.isActive()) {
0325             _dragTimer.stop();
0326         }
0327         _dragTimer.start(_dragDelay, this);
0328 
0329         return true;
0330     }
0331 #endif
0332 
0333     if (_eventInQQuickWidget) {
0334         event->setAccepted(true);
0335         return false;
0336     }
0337     _eventInQQuickWidget = false;
0338 
0339     // cast to widget
0340     auto widget = static_cast<QWidget *>(object);
0341 
0342     // check if widget can be dragged from current position
0343     if (isBlackListed(widget) || !canDrag(widget)) {
0344         return false;
0345     }
0346 
0347     // retrieve widget's child at event position
0348     auto position(mouseEvent->pos());
0349     auto child = widget->childAt(position);
0350     if (!canDrag(widget, child, position)) {
0351         return false;
0352     }
0353 
0354     // save target and drag point
0355     _target = widget;
0356     _dragPoint = position;
0357     _globalDragPoint =
0358 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0359         mouseEvent->globalPosition().toPoint();
0360 #else
0361         mouseEvent->globalPos();
0362 #endif
0363     _dragAboutToStart = true;
0364 
0365     // send a move event to the current child with same position
0366     // if received, it is caught to actually start the drag
0367     auto localPoint(_dragPoint);
0368     if (child) {
0369         localPoint = child->mapFrom(widget, localPoint);
0370     } else {
0371         child = widget;
0372     }
0373     QMouseEvent localMouseEvent(QEvent::MouseMove,
0374                                 localPoint,
0375 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
0376                                 QCursor::pos(),
0377 #endif
0378                                 Qt::NoButton,
0379                                 Qt::LeftButton,
0380                                 Qt::NoModifier);
0381     localMouseEvent.setTimestamp(mouseEvent->timestamp());
0382     qApp->sendEvent(child, &localMouseEvent);
0383 
0384     // never eat event
0385     return false;
0386 }
0387 
0388 //_____________________________________________________________
0389 bool WindowManager::mouseMoveEvent(QObject *object, QEvent *event)
0390 {
0391     Q_UNUSED(object);
0392 
0393     // stop timer
0394     if (_dragTimer.isActive()) {
0395         _dragTimer.stop();
0396     }
0397 
0398     // cast event and check drag distance
0399     auto mouseEvent = static_cast<QMouseEvent *>(event);
0400     if (mouseEvent->source() != Qt::MouseEventNotSynthesized) {
0401         return false;
0402     }
0403     auto eventPos =
0404 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0405         mouseEvent->globalPosition().toPoint();
0406 #else
0407         mouseEvent->globalPos();
0408 #endif
0409     if (!_dragInProgress) {
0410         if (_dragAboutToStart) {
0411             if (mouseEvent->pos() == _dragPoint) {
0412                 // start timer,
0413                 _dragAboutToStart = false;
0414                 if (_dragTimer.isActive()) {
0415                     _dragTimer.stop();
0416                 }
0417                 _dragTimer.start(_dragDelay, this);
0418 
0419             } else {
0420                 resetDrag();
0421             }
0422 
0423         } else if (QPoint(eventPos - _globalDragPoint).manhattanLength() >= _dragDistance) {
0424             _dragTimer.start(0, this);
0425         }
0426 
0427         return true;
0428 
0429     } else {
0430         return false;
0431     }
0432 }
0433 
0434 //_____________________________________________________________
0435 bool WindowManager::mouseReleaseEvent(QObject *object, QEvent *event)
0436 {
0437     Q_UNUSED(object);
0438     Q_UNUSED(event);
0439     resetDrag();
0440     return false;
0441 }
0442 
0443 //_____________________________________________________________
0444 bool WindowManager::isDragable(QWidget *widget)
0445 {
0446     // check widget
0447     if (!widget) {
0448         return false;
0449     }
0450 
0451     // accepted default types
0452     if ((qobject_cast<QDialog *>(widget) && widget->isWindow()) || (qobject_cast<QMainWindow *>(widget) && widget->isWindow())
0453         || qobject_cast<QGroupBox *>(widget)) {
0454         return true;
0455     }
0456 
0457     // more accepted types, provided they are not dock widget titles
0458     if ((qobject_cast<QMenuBar *>(widget) || qobject_cast<QTabBar *>(widget) || qobject_cast<QStatusBar *>(widget) || qobject_cast<QToolBar *>(widget))
0459         && !isDockWidgetTitle(widget)) {
0460         return true;
0461     }
0462 
0463     if (widget->inherits("KScreenSaver") && widget->inherits("KCModule")) {
0464         return true;
0465     }
0466 
0467     if (isWhiteListed(widget)) {
0468         return true;
0469     }
0470 
0471     // flat toolbuttons
0472     if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
0473         if (toolButton->autoRaise()) {
0474             return true;
0475         }
0476     }
0477 
0478     // viewports
0479     /*
0480     one needs to check that
0481     1/ the widget parent is a scrollarea
0482     2/ it matches its parent viewport
0483     3/ the parent is not blacklisted
0484     */
0485     if (auto listView = qobject_cast<QListView *>(widget->parentWidget())) {
0486         if (listView->viewport() == widget && !isBlackListed(listView)) {
0487             return true;
0488         }
0489     }
0490 
0491     if (auto treeView = qobject_cast<QTreeView *>(widget->parentWidget())) {
0492         if (treeView->viewport() == widget && !isBlackListed(treeView)) {
0493             return true;
0494         }
0495     }
0496 
0497     /*
0498     catch labels in status bars.
0499     this is because of kstatusbar
0500     who captures buttonPress/release events
0501     */
0502     if (auto label = qobject_cast<QLabel *>(widget)) {
0503         if (label->textInteractionFlags().testFlag(Qt::TextSelectableByMouse)) {
0504             return false;
0505         }
0506 
0507         QWidget *parent = label->parentWidget();
0508         while (parent) {
0509             if (qobject_cast<QStatusBar *>(parent)) {
0510                 return true;
0511             }
0512             parent = parent->parentWidget();
0513         }
0514     }
0515 
0516     return false;
0517 }
0518 
0519 //_____________________________________________________________
0520 bool WindowManager::isBlackListed(QWidget *widget)
0521 {
0522     // check against noAnimations property
0523     const auto propertyValue(widget->property(PropertyNames::noWindowGrab));
0524     if (propertyValue.isValid() && propertyValue.toBool()) {
0525         return true;
0526     }
0527 
0528     // list-based blacklisted widgets
0529     const auto appName(qApp->applicationName());
0530     for (const ExceptionId &id : std::as_const(_blackList)) {
0531         if (!id.appName().isEmpty() && id.appName() != appName) {
0532             continue;
0533         }
0534         if (id.className() == QStringLiteral("*") && !id.appName().isEmpty()) {
0535             // if application name matches and all classes are selected
0536             // disable the grabbing entirely
0537             setEnabled(false);
0538             return true;
0539         }
0540         if (widget->inherits(id.className().toLatin1().data())) {
0541             return true;
0542         }
0543     }
0544 
0545     return false;
0546 }
0547 
0548 //_____________________________________________________________
0549 bool WindowManager::isWhiteListed(QWidget *widget) const
0550 {
0551     const auto appName(qApp->applicationName());
0552     for (const ExceptionId &id : std::as_const(_whiteList)) {
0553         if (!(id.appName().isEmpty() || id.appName() == appName)) {
0554             continue;
0555         }
0556         if (widget->inherits(id.className().toLatin1().data())) {
0557             return true;
0558         }
0559     }
0560 
0561     return false;
0562 }
0563 
0564 //_____________________________________________________________
0565 bool WindowManager::canDrag(QWidget *widget)
0566 {
0567     // check if enabled
0568     if (!enabled()) {
0569         return false;
0570     }
0571 
0572     // assume isDragable widget is already passed
0573     // check some special cases where drag should not be effective
0574 
0575     // check mouse grabber
0576     if (QWidget::mouseGrabber()) {
0577         return false;
0578     }
0579 
0580     /*
0581     check cursor shape.
0582     Assume that a changed cursor means that some action is in progress
0583     and should prevent the drag
0584     */
0585     if (widget->cursor().shape() != Qt::ArrowCursor) {
0586         return false;
0587     }
0588 
0589     // accept
0590     return true;
0591 }
0592 
0593 //_____________________________________________________________
0594 bool WindowManager::canDrag(QWidget *widget, QWidget *child, const QPoint &position)
0595 {
0596     // retrieve child at given position and check cursor again
0597     if (child && child->cursor().shape() != Qt::ArrowCursor) {
0598         return false;
0599     }
0600 
0601     /*
0602     check against children from which drag should never be enabled,
0603     even if mousePress/Move has been passed to the parent
0604     */
0605     if (child && (qobject_cast<QComboBox *>(child) || qobject_cast<QProgressBar *>(child) || qobject_cast<QScrollBar *>(child))) {
0606         return false;
0607     }
0608 
0609     // tool buttons
0610     if (auto toolButton = qobject_cast<QToolButton *>(widget)) {
0611         if (dragMode() == StyleConfigData::WD_MINIMAL && !qobject_cast<QToolBar *>(widget->parentWidget())) {
0612             return false;
0613         }
0614         return toolButton->autoRaise() && !toolButton->isEnabled();
0615     }
0616 
0617     // check menubar
0618     if (auto menuBar = qobject_cast<QMenuBar *>(widget)) {
0619         // do not drag from menubars embedded in Mdi windows
0620         if (findParent<QMdiSubWindow *>(widget)) {
0621             return false;
0622         }
0623 
0624         // check if there is an active action
0625         if (menuBar->activeAction() && menuBar->activeAction()->isEnabled()) {
0626             return false;
0627         }
0628 
0629         // check if action at position exists and is enabled
0630         if (auto action = menuBar->actionAt(position)) {
0631             if (action->isSeparator()) {
0632                 return true;
0633             }
0634             if (action->isEnabled()) {
0635                 return false;
0636             }
0637         }
0638 
0639         // return true in all other cases
0640         return true;
0641     }
0642 
0643     /*
0644     in MINIMAL mode, anything that has not been already accepted
0645     and does not come from a toolbar is rejected
0646     */
0647     if (dragMode() == StyleConfigData::WD_MINIMAL) {
0648         if (qobject_cast<QToolBar *>(widget)) {
0649             return true;
0650         } else {
0651             return false;
0652         }
0653     }
0654 
0655     /* following checks are relevant only for WD_FULL mode */
0656 
0657     // tabbar. Make sure no tab is under the cursor
0658     if (auto tabBar = qobject_cast<QTabBar *>(widget)) {
0659         return tabBar->tabAt(position) == -1;
0660     }
0661 
0662     /*
0663     check groupboxes
0664     prevent drag if unchecking grouboxes
0665     */
0666     if (auto groupBox = qobject_cast<QGroupBox *>(widget)) {
0667         // non checkable group boxes are always ok
0668         if (!groupBox->isCheckable()) {
0669             return true;
0670         }
0671 
0672         // gather options to retrieve checkbox subcontrol rect
0673         QStyleOptionGroupBox opt;
0674         opt.initFrom(groupBox);
0675         if (groupBox->isFlat()) {
0676             opt.features |= QStyleOptionFrame::Flat;
0677         }
0678         opt.lineWidth = 1;
0679         opt.midLineWidth = 0;
0680         opt.text = groupBox->title();
0681         opt.textAlignment = groupBox->alignment();
0682         opt.subControls = (QStyle::SC_GroupBoxFrame | QStyle::SC_GroupBoxCheckBox);
0683         if (!groupBox->title().isEmpty()) {
0684             opt.subControls |= QStyle::SC_GroupBoxLabel;
0685         }
0686 
0687         opt.state |= (groupBox->isChecked() ? QStyle::State_On : QStyle::State_Off);
0688 
0689         // check against groupbox checkbox
0690         if (groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, groupBox).contains(position)) {
0691             return false;
0692         }
0693 
0694         // check against groupbox label
0695         if (!groupBox->title().isEmpty()
0696             && groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, groupBox).contains(position)) {
0697             return false;
0698         }
0699 
0700         return true;
0701     }
0702 
0703     // labels
0704     if (auto label = qobject_cast<QLabel *>(widget)) {
0705         if (label->textInteractionFlags().testFlag(Qt::TextSelectableByMouse)) {
0706             return false;
0707         }
0708     }
0709 
0710     // abstract item views
0711     QAbstractItemView *itemView(nullptr);
0712     if ((itemView = qobject_cast<QListView *>(widget->parentWidget())) || (itemView = qobject_cast<QTreeView *>(widget->parentWidget()))) {
0713         if (widget == itemView->viewport()) {
0714             // QListView
0715             if (itemView->frameShape() != QFrame::NoFrame) {
0716                 return false;
0717             } else if (itemView->selectionMode() != QAbstractItemView::NoSelection && itemView->selectionMode() != QAbstractItemView::SingleSelection
0718                        && itemView->model() && itemView->model()->rowCount()) {
0719                 return false;
0720             } else if (itemView->model() && itemView->indexAt(position).isValid()) {
0721                 return false;
0722             }
0723         }
0724 
0725     } else if ((itemView = qobject_cast<QAbstractItemView *>(widget->parentWidget()))) {
0726         if (widget == itemView->viewport()) {
0727             // QAbstractItemView
0728             if (itemView->frameShape() != QFrame::NoFrame) {
0729                 return false;
0730             } else if (itemView->indexAt(position).isValid()) {
0731                 return false;
0732             }
0733         }
0734 
0735     } else if (auto graphicsView = qobject_cast<QGraphicsView *>(widget->parentWidget())) {
0736         if (widget == graphicsView->viewport()) {
0737             // QGraphicsView
0738             if (graphicsView->frameShape() != QFrame::NoFrame) {
0739                 return false;
0740             } else if (graphicsView->dragMode() != QGraphicsView::NoDrag) {
0741                 return false;
0742             } else if (graphicsView->itemAt(position)) {
0743                 return false;
0744             }
0745         }
0746     }
0747 
0748     return true;
0749 }
0750 
0751 //____________________________________________________________
0752 void WindowManager::resetDrag()
0753 {
0754     _target.clear();
0755 #if BREEZE_HAVE_QTQUICK
0756     _quickTarget.clear();
0757 #endif
0758     if (_dragTimer.isActive()) {
0759         _dragTimer.stop();
0760     }
0761     _dragPoint = QPoint();
0762     _globalDragPoint = QPoint();
0763     _dragAboutToStart = false;
0764     _dragInProgress = false;
0765 }
0766 
0767 //____________________________________________________________
0768 void WindowManager::startDrag(QWindow *window)
0769 {
0770     if (!(enabled() && window)) {
0771         return;
0772     }
0773     if (QWidget::mouseGrabber()) {
0774         return;
0775     }
0776 
0777 #if BREEZE_HAVE_QTQUICK
0778     if (_quickTarget) {
0779         if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(window)) {
0780             QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qw);
0781             if (renderWindow) {
0782                 _dragInProgress = renderWindow->startSystemMove();
0783             } else {
0784                 _dragInProgress = window->startSystemMove();
0785             }
0786         }
0787     } else
0788 #endif
0789     {
0790         _dragInProgress = window->startSystemMove();
0791     }
0792 }
0793 
0794 //____________________________________________________________
0795 bool WindowManager::isDockWidgetTitle(const QWidget *widget) const
0796 {
0797     if (!widget) {
0798         return false;
0799     }
0800     if (auto dockWidget = qobject_cast<const QDockWidget *>(widget->parent())) {
0801         return widget == dockWidget->titleBarWidget();
0802 
0803     } else {
0804         return false;
0805     }
0806 }
0807 
0808 }