From 2fe542c3ef4cbfab7d91b5018c3aabe125bb4674 Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Thu, 28 Nov 2019 09:09:36 +0100 Subject: [PATCH] Improved transparent docking --- demo/MainWindow.cpp | 4 +- src/DockManager.h | 14 ++++++- src/FloatingOverlay.cpp | 90 ++++++++++++++++++++++++++++------------- src/FloatingOverlay.h | 6 +-- 4 files changed, 82 insertions(+), 32 deletions(-) diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index 2549377..c63af5b 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -322,7 +322,9 @@ CMainWindow::CMainWindow(QWidget *parent) : // uncomment the following line if you wand a fixed tab width that does // not change if the visibility of the close button changes - CDockManager::setConfigFlag(CDockManager::OpaqueUndocking, false); + //CDockManager::setConfigFlag(CDockManager::OpaqueUndocking, false); + //CDockManager::setConfigFlag(CDockManager::DragPreviewIsDynamic, false); + CDockManager::setConfigFlags(CDockManager::NonOpaqueWithWindowFrame); // Now create the dock manager and its content d->DockManager = new CDockManager(this); diff --git a/src/DockManager.h b/src/DockManager.h index d96b6f3..59a2447 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -152,12 +152,24 @@ public: TabCloseButtonIsToolButton = 0x0040,//! If enabled the tab close buttons will be QToolButtons instead of QPushButtons - disabled by default AllTabsHaveCloseButton = 0x0080, //!< if this flag is set, then all tabs that are closable show a close button RetainTabSizeWhenCloseButtonHidden = 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible - OpaqueUndocking = 0x0200, + OpaqueUndocking = 0x0200,///< If enabled, the widgets are immediately undocked into floating widgets, if disabled, only a draw preview is undocked and the real undocking is deferred until the mouse is released + DragPreviewIsDynamic = 0x0400,///< If opaque undocking is disabled, this flag defines the behavior of the drag preview window, if this flag is enabled, the preview will be adjusted dynamically to the drop area + DragPreviewShowsContentPixmap = 0x0800,///< If opaque undocking is disabled, the created drag preview window shows a copy of the content of the dock widget / dock are that is dragged + DragPreviewHasWindowFrame = 0x1000,///< If opaque undocking is disabled, then this flag configures if the drag preview is frameless or looks like a real window DefaultConfig = ActiveTabHasCloseButton | DockAreaHasCloseButton | OpaqueSplitterResize | XmlCompressionEnabled | OpaqueUndocking, ///< the default configuration + DefaultNonOpaqueConfig = ActiveTabHasCloseButton + | DockAreaHasCloseButton + | XmlCompressionEnabled + | DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations + NonOpaqueWithWindowFrame = ActiveTabHasCloseButton + | DockAreaHasCloseButton + | XmlCompressionEnabled + | DragPreviewShowsContentPixmap + | DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame }; Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag) diff --git a/src/FloatingOverlay.cpp b/src/FloatingOverlay.cpp index e34f674..037dc1f 100644 --- a/src/FloatingOverlay.cpp +++ b/src/FloatingOverlay.cpp @@ -40,6 +40,7 @@ struct FloatingOverlayPrivate qreal WindowOpacity; bool Hidden = false; bool IgnoreMouseEvents = false; + QPixmap ContentPreviewPixmap; /** @@ -99,7 +100,10 @@ void FloatingOverlayPrivate::updateDropOverlays(const QPoint &GlobalPos) { ContainerOverlay->hideOverlay(); DockAreaOverlay->hideOverlay(); - setHidden(false); + if (CDockManager::configFlags().testFlag(CDockManager::DragPreviewIsDynamic)) + { + setHidden(false); + } return; } @@ -140,7 +144,10 @@ void FloatingOverlayPrivate::updateDropOverlays(const QPoint &GlobalPos) } } - setHidden(DockDropArea != InvalidDockWidgetArea || ContainerDropArea != InvalidDockWidgetArea); + if (CDockManager::configFlags().testFlag(CDockManager::DragPreviewIsDynamic)) + { + setHidden(DockDropArea != InvalidDockWidgetArea || ContainerDropArea != InvalidDockWidgetArea); + } } @@ -153,21 +160,37 @@ FloatingOverlayPrivate::FloatingOverlayPrivate(CFloatingOverlay *_public) : //============================================================================ CFloatingOverlay::CFloatingOverlay(QWidget* Content, QWidget* parent) : - QFrame(parent), + QWidget(parent), d(new FloatingOverlayPrivate(this)) { d->Content = Content; setAttribute(Qt::WA_DeleteOnClose); + if (CDockManager::configFlags().testFlag(CDockManager::DragPreviewHasWindowFrame)) + { + setWindowFlags( + Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); + } + else + { + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_TranslucentBackground); + } - setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); - setWindowOpacity(1); - setWindowTitle("FloatingOverlay"); - setAttribute(Qt::WA_NoSystemBackground); - setAttribute(Qt::WA_TranslucentBackground); + setWindowOpacity(0.6); // We install an event filter to detect mouse release events because we // do not receive mouse release event if the floating widget is behind // the drop overlay cross qApp->installEventFilter(this); + + // Create a static image of the widget that should get undocked + // This is like some kind preview image like it is uses in drag and drop + // operations + if (CDockManager::configFlags().testFlag(CDockManager::DragPreviewShowsContentPixmap)) + { + d->ContentPreviewPixmap = QPixmap(Content->size()); + Content->render(&d->ContentPreviewPixmap); + } } @@ -181,6 +204,7 @@ CFloatingOverlay::CFloatingOverlay(CDockWidget* Content) d->ContentSourceArea = Content->dockAreaWidget(); d->ContenSourceContainer = Content->dockContainer(); } + setWindowTitle(Content->windowTitle()); } @@ -191,6 +215,7 @@ CFloatingOverlay::CFloatingOverlay(CDockAreaWidget* Content) d->DockManager = Content->dockManager(); d->ContentSourceArea = Content; d->ContenSourceContainer = Content->dockContainer(); + setWindowTitle(Content->currentDockWidget()->windowTitle()); } @@ -241,7 +266,6 @@ bool CFloatingOverlay::eventFilter(QObject *watched, QEvent *event) if (event->type() == QEvent::MouseButtonRelease && !d->IgnoreMouseEvents) { ADS_PRINT("FloatingWidget::eventFilter QEvent::MouseButtonRelease"); - std::cout << "CFloatingOverlay::eventFilter QEvent::MouseButtonRelease" << std::endl; auto DockDropArea = d->DockManager->dockAreaOverlay()->dropAreaUnderCursor(); auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); @@ -265,16 +289,22 @@ bool CFloatingOverlay::eventFilter(QObject *watched, QEvent *event) } FloatingWidget->setGeometry(this->geometry()); FloatingWidget->show(); - QApplication::processEvents(); - int FrameHeight = FloatingWidget->frameGeometry().height() - FloatingWidget->geometry().height(); - QRect FixedGeometry = this->geometry(); - FixedGeometry.adjust(0, FrameHeight, 0, 0); - FloatingWidget->setGeometry(FixedGeometry); + if (!CDockManager::configFlags().testFlag(CDockManager::DragPreviewHasWindowFrame)) + { + QApplication::processEvents(); + int FrameHeight = FloatingWidget->frameGeometry().height() - FloatingWidget->geometry().height(); + QRect FixedGeometry = this->geometry(); + FixedGeometry.adjust(0, FrameHeight, 0, 0); + FloatingWidget->setGeometry(FixedGeometry); + } } this->close(); d->DockManager->containerOverlay()->hideOverlay(); d->DockManager->dockAreaOverlay()->hideOverlay(); + // Because we use the event filter, we receive multiple mouse release + // events. To prevent multiple code execution, we ignore all mouse + // events after the first mouse event d->IgnoreMouseEvents = true; } @@ -286,25 +316,31 @@ bool CFloatingOverlay::eventFilter(QObject *watched, QEvent *event) void CFloatingOverlay::paintEvent(QPaintEvent* event) { Q_UNUSED(event); - if (d->Hidden) { return; } - QRect r = rect(); QPainter painter(this); - QColor Color = palette().color(QPalette::Active, QPalette::Highlight); - QPen Pen = painter.pen(); - Pen.setColor(Color.darker(120)); - Pen.setStyle(Qt::SolidLine); - Pen.setWidth(1); - Pen.setCosmetic(true); - painter.setPen(Pen); - Color = Color.lighter(130); - Color.setAlpha(64); - painter.setBrush(Color); - painter.drawRect(r.adjusted(0, 0, -1, -1)); + if (CDockManager::configFlags().testFlag(CDockManager::DragPreviewShowsContentPixmap)) + { + painter.drawPixmap(QPoint(0, 0), d->ContentPreviewPixmap); + } + + if (!CDockManager::configFlags().testFlag(CDockManager::DragPreviewHasWindowFrame)) + { + QColor Color = palette().color(QPalette::Active, QPalette::Highlight); + QPen Pen = painter.pen(); + Pen.setColor(Color.darker(120)); + Pen.setStyle(Qt::SolidLine); + Pen.setWidth(1); + Pen.setCosmetic(true); + painter.setPen(Pen); + Color = Color.lighter(130); + Color.setAlpha(64); + painter.setBrush(Color); + painter.drawRect(rect().adjusted(0, 0, -1, -1)); + } } diff --git a/src/FloatingOverlay.h b/src/FloatingOverlay.h index 9a3a3df..7f3f387 100644 --- a/src/FloatingOverlay.h +++ b/src/FloatingOverlay.h @@ -10,7 +10,7 @@ //============================================================================ // INCLUDES //============================================================================ -#include +#include #include "FloatingDockContainer.h" namespace ads @@ -23,7 +23,7 @@ struct FloatingOverlayPrivate; * A floating overlay is a temporary floating widget that is just used to * indicate the floating widget movement */ -class CFloatingOverlay : public QFrame, public IFloatingWidget +class CFloatingOverlay : public QWidget, public IFloatingWidget { private: FloatingOverlayPrivate* d; @@ -40,7 +40,7 @@ protected: CFloatingOverlay(QWidget* Content, QWidget* parent); public: - using Super = QRubberBand; + using Super = QWidget; CFloatingOverlay(CDockWidget* Content); CFloatingOverlay(CDockAreaWidget* Content);