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 #include #include +#include #include #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(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 +#if QT_CONFIG(draganddrop) +#include +#endif +#include #if QT_CONFIG(statusbar) #include #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(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 #endif +#if QT_CONFIG(draganddrop) +#include +#endif #include #include #include @@ -14,6 +17,7 @@ #if QT_CONFIG(menubar) #include #endif +#include #if QT_CONFIG(rubberband) #include #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