diff options
author | David Redondo <qt@david-redondo.de> | 2023-03-10 16:44:20 +0100 |
---|---|---|
committer | David Redondo <qt@david-redondo.de> | 2023-06-02 19:53:34 +0200 |
commit | 581c4bcb62a9d3cbb4c33df3f0f7a0a965225e74 (patch) | |
tree | 22da571a5a8c0d5fdbfd14a17654cf3a8b28d5ba /src | |
parent | 1eb4d17cb48a70501ebf51fc8732e14fec2cc12f (diff) |
Use platform drags for drags of docks and toolbars on wayland
On Wayland we can't know where windows are in relation to
each other so the whole mechanism was broken, toolbars
were unmovable once undocked and could be blindly redocked.
Dockwidgets had native toolbars to move them around but could
not be redocked.
This introduces an alternative code path that uses a platform
drag with a special mimetype that enables the platform to
issue a combined drag and window move using the relevant protocol.
Should the protocol not be available this doesn't make things
actively worse as it will be similar broken as before.
Fixes: QTBUG-87332
Change-Id: I3b8bdc0b1bc22569a64cb8bf7ca7d37d223936a6
Reviewed-by: David Edmundson <davidedmundson@kde.org>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/widgets/widgets/qdockwidget.cpp | 28 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindow.cpp | 20 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindow.h | 1 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindowlayout.cpp | 39 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindowlayout_p.h | 12 | ||||
-rw-r--r-- | src/widgets/widgets/qtoolbar.cpp | 37 |
6 files changed, 130 insertions, 7 deletions
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 375bd3daae..c952c8d4fd 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -253,7 +253,9 @@ bool QDockWidgetLayout::wmSupportsNativeWindowDeco() return false; #else static const bool xcb = !QGuiApplication::platformName().compare("xcb"_L1, Qt::CaseInsensitive); - return !xcb; + static const bool wayland = + QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive); + return !(xcb || wayland); #endif } @@ -778,6 +780,8 @@ void QDockWidgetPrivate::startDrag(bool group) QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q); Q_ASSERT(layout != nullptr); + bool wasFloating = q->isFloating(); + state->widgetItem = layout->unplug(q, group); if (state->widgetItem == nullptr) { /* Dock widget has a QMainWindow parent, but was never inserted with @@ -796,6 +800,20 @@ void QDockWidgetPrivate::startDrag(bool group) layout->restore(); state->dragging = true; + +#if QT_CONFIG(draganddrop) + if (QMainWindowLayout::needsPlatformDrag()) { + Qt::DropAction result = + layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos); + if (result == Qt::IgnoreAction && !wasFloating) { + layout->revert(state->widgetItem); + delete state; + state = nullptr; + } else { + endDrag(); + } + } +#endif } /*! \internal @@ -1038,6 +1056,10 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event) bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event) { #if QT_CONFIG(mainwindow) + // if we are peforming a platform drag ignore the release here and end the drag when the actual + // drag ends. + if (QMainWindowLayout::needsPlatformDrag()) + return false; if (event->button() == Qt::LeftButton && state && !state->nca) { endDrag(); @@ -1185,7 +1207,9 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect flags |= Qt::FramelessWindowHint; } - if (unplug) + // If we are performing a platform drag the flag is not needed and we want to avoid recreating + // the platform window when it would be removed later + if (unplug && !QMainWindowLayout::needsPlatformDrag()) flags |= Qt::X11BypassWindowManagerHint; q->setWindowFlags(flags); diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp index a5f7973994..0fc8aeace7 100644 --- a/src/widgets/widgets/qmainwindow.cpp +++ b/src/widgets/widgets/qmainwindow.cpp @@ -25,6 +25,7 @@ #include <qstyle.h> #include <qdebug.h> #include <qpainter.h> +#include <qmimedata.h> #include <private/qwidget_p.h> #if QT_CONFIG(toolbar) @@ -36,6 +37,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class QMainWindowPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QMainWindow) @@ -121,6 +124,7 @@ void QMainWindowPrivate::init() const int metric = q->style()->pixelMetric(QStyle::PM_ToolBarIconSize, nullptr, q); iconSize = QSize(metric, metric); q->setAttribute(Qt::WA_Hover); + q->setAcceptDrops(true); } /* @@ -1296,6 +1300,22 @@ bool QMainWindow::event(QEvent *event) if (!d->explicitIconSize) setIconSize(QSize()); break; +#if QT_CONFIG(draganddrop) + case QEvent::DragEnter: + case QEvent::Drop: + if (!d->layout->draggingWidget) + break; + event->accept(); + return true; + case QEvent::DragMove: { + if (!d->layout->draggingWidget) + break; + auto dragMoveEvent = static_cast<QDragMoveEvent *>(event); + d->layout->hover(d->layout->draggingWidget, dragMoveEvent->position().toPoint()); + event->accept(); + return true; + } +#endif default: break; } diff --git a/src/widgets/widgets/qmainwindow.h b/src/widgets/widgets/qmainwindow.h index 90e37d7283..a050093417 100644 --- a/src/widgets/widgets/qmainwindow.h +++ b/src/widgets/widgets/qmainwindow.h @@ -178,6 +178,7 @@ protected: private: Q_DECLARE_PRIVATE(QMainWindow) Q_DISABLE_COPY(QMainWindow) + friend class QDockWidgetGroupWindow; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QMainWindow::DockOptions) diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index 6fb263f87d..0971ac5d58 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -24,6 +24,10 @@ #endif #include <qapplication.h> +#if QT_CONFIG(draganddrop) +#include <qdrag.h> +#endif +#include <qmimedata.h> #if QT_CONFIG(statusbar) #include <qstatusbar.h> #endif @@ -3008,6 +3012,41 @@ bool QMainWindowLayout::restoreState(QDataStream &stream) return true; } +#if QT_CONFIG(draganddrop) +bool QMainWindowLayout::needsPlatformDrag() +{ + static const bool wayland = + QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive); + return wayland; +} + +Qt::DropAction QMainWindowLayout::performPlatformWidgetDrag(QLayoutItem *widgetItem, + const QPoint &pressPosition) +{ + draggingWidget = widgetItem; + QWidget *widget = widgetItem->widget(); + auto drag = QDrag(widget); + auto mimeData = new QMimeData(); + auto window = widgetItem->widget()->windowHandle(); + + auto serialize = [](const auto &object) { + QByteArray data; + QDataStream dataStream(&data, QIODevice::WriteOnly); + dataStream << object; + return data; + }; + mimeData->setData("application/x-qt-mainwindowdrag-window"_L1, + serialize(reinterpret_cast<qintptr>(window))); + mimeData->setData("application/x-qt-mainwindowdrag-position"_L1, serialize(pressPosition)); + drag.setMimeData(mimeData); + + auto result = drag.exec(); + + draggingWidget = nullptr; + return result; +} +#endif + QT_END_NAMESPACE #include "moc_qmainwindowlayout_p.cpp" diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h index 0f784b56d0..e3d1bb1eeb 100644 --- a/src/widgets/widgets/qmainwindowlayout_p.h +++ b/src/widgets/widgets/qmainwindowlayout_p.h @@ -306,8 +306,10 @@ class Q_AUTOTEST_EXPORT QDockWidgetGroupWindow : public QWidget { Q_OBJECT public: - explicit QDockWidgetGroupWindow(QWidget* parent = nullptr, Qt::WindowFlags f = { }) - : QWidget(parent, f) {} + explicit QDockWidgetGroupWindow(QWidget *parent = nullptr, Qt::WindowFlags f = {}) + : QWidget(parent, f) + { + } QDockAreaLayoutInfo *layoutInfo() const; #if QT_CONFIG(tabbar) const QDockAreaLayoutInfo *tabLayoutInfo() const; @@ -571,6 +573,12 @@ public: void restore(bool keepSavedState = false); void animationFinished(QWidget *widget); +#if QT_CONFIG(draganddrop) + static bool needsPlatformDrag(); + Qt::DropAction performPlatformWidgetDrag(QLayoutItem *widgetItem, const QPoint &pressPosition); + QLayoutItem *draggingWidget = nullptr; +#endif + protected: void timerEvent(QTimerEvent *e) override; diff --git a/src/widgets/widgets/qtoolbar.cpp b/src/widgets/widgets/qtoolbar.cpp index b5950fdf23..54fd3ac525 100644 --- a/src/widgets/widgets/qtoolbar.cpp +++ b/src/widgets/widgets/qtoolbar.cpp @@ -7,6 +7,9 @@ #if QT_CONFIG(combobox) #include <qcombobox.h> #endif +#if QT_CONFIG(draganddrop) +#include <qdrag.h> +#endif #include <qevent.h> #include <qlayout.h> #include <qmainwindow.h> @@ -14,6 +17,7 @@ #if QT_CONFIG(menubar) #include <qmenubar.h> #endif +#include <qmimedata.h> #if QT_CONFIG(rubberband) #include <qrubberband.h> #endif @@ -40,6 +44,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + // qmainwindow.cpp extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); @@ -105,7 +111,9 @@ void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug) flags |= Qt::FramelessWindowHint; - if (unplug) + // If we are performing a platform drag the flag is not needed and we want to avoid recreating + // the platform window when it would be removed later + if (unplug && !QMainWindowLayout::needsPlatformDrag()) flags |= Qt::X11BypassWindowManagerHint; q->setWindowFlags(flags); @@ -117,8 +125,6 @@ void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &re bool visible = !q->isHidden(); bool wasFloating = q->isFloating(); // ...is also currently using popup menus - q->hide(); - updateWindowFlags(floating, unplug); if (floating != wasFloating) @@ -172,12 +178,27 @@ void QToolBarPrivate::startDrag(bool moving) QMainWindowLayout *layout = qt_mainwindow_layout(win); Q_ASSERT(layout != nullptr); + const bool wasFloating = q->isFloating(); + if (!moving) { state->widgetItem = layout->unplug(q); Q_ASSERT(state->widgetItem != nullptr); } state->dragging = !moving; state->moving = moving; + +#if QT_CONFIG(draganddrop) + if (QMainWindowLayout::needsPlatformDrag() && state->dragging) { + auto result = layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos); + if (result == Qt::IgnoreAction && !wasFloating) { + layout->revert(state->widgetItem); + delete state; + state = nullptr; + } else { + endDrag(); + } + } +#endif } void QToolBarPrivate::endDrag() @@ -243,6 +264,11 @@ bool QToolBarPrivate::mousePressEvent(QMouseEvent *event) bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*) { + // if we are peforming a platform drag ignore the release here and end the drag when the actual + // drag ends. + if (QMainWindowLayout::needsPlatformDrag()) + return false; + if (state != nullptr) { endDrag(); return true; @@ -293,6 +319,11 @@ bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event) q->grabMouse(); } + if (!state) { + q->releaseMouse(); + return true; + } + if (state->dragging) { QPoint pos = event->globalPosition().toPoint(); // if we are right-to-left, we move so as to keep the right edge the same distance |