diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index 50aa1d7..d09cb40 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -282,7 +282,7 @@ CMainWindow::CMainWindow(QWidget *parent) : d->createContent(); // Default window geometry - resize(800, 600); + resize(1280, 720); d->restoreState(); d->restorePerspectives(); diff --git a/src/DockAreaTabBar.cpp b/src/DockAreaTabBar.cpp index a579a6c..031a193 100644 --- a/src/DockAreaTabBar.cpp +++ b/src/DockAreaTabBar.cpp @@ -238,7 +238,7 @@ CFloatingDockContainer* CDockAreaTabBar::makeAreaFloating(const QPoint& Offset, { QSize Size = d->DockArea->size(); CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(d->DockArea); - FloatingWidget->startFloating(Offset, Size, DragState); + FloatingWidget->startFloating(Offset, Size, DragState, nullptr); auto TopLevelDockWidget = FloatingWidget->topLevelDockWidget(); if (TopLevelDockWidget) { diff --git a/src/DockAreaTitleBar.cpp b/src/DockAreaTitleBar.cpp index 5802760..021060b 100644 --- a/src/DockAreaTitleBar.cpp +++ b/src/DockAreaTitleBar.cpp @@ -97,6 +97,23 @@ struct DockAreaTitleBarPrivate { return DockArea->dockManager()->configFlags().testFlag(Flag); } + + /** + * Helper class to set title bar burron icons depending on operating system + * and to avoid duplicated code + */ + void setTitleBarButtonIcon(tTileBarButton* Button, QStyle::StandardPixmap StandarPixmap) + { + #ifdef Q_OS_LINUX + Button->setIcon(_this->style()->standardIcon(StandarPixmap)); + #else + QIcon Icon; + QPixmap normalPixmap = _this->style()->standardPixmap(StandarPixmap, 0, Button); + Icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); + Icon.addPixmap(normalPixmap, QIcon::Normal); + Button->setIcon(Icon); + #endif + } };// struct DockAreaTitleBarPrivate @@ -112,18 +129,12 @@ DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(CDockAreaTitleBar* _public) : //============================================================================ void DockAreaTitleBarPrivate::createButtons() { + // Tabs menu button TabsMenuButton = new tTileBarButton(); TabsMenuButton->setObjectName("tabsMenuButton"); TabsMenuButton->setAutoRaise(true); TabsMenuButton->setPopupMode(QToolButton::InstantPopup); - - QIcon MenuIcon; - QPixmap normalPixmap = _this->style()->standardPixmap( - QStyle::SP_TitleBarUnshadeButton, 0, TabsMenuButton); - MenuIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - MenuIcon.addPixmap(normalPixmap, QIcon::Normal); - TabsMenuButton->setIcon(MenuIcon); - + setTitleBarButtonIcon(TabsMenuButton, QStyle::SP_TitleBarUnshadeButton); QMenu* TabsMenu = new QMenu(TabsMenuButton); #ifndef QT_NO_TOOLTIP TabsMenu->setToolTipsVisible(true); @@ -138,6 +149,7 @@ void DockAreaTitleBarPrivate::createButtons() _this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)), SLOT(onTabsMenuActionTriggered(QAction*))); + // Undock button UndockButton = new tTileBarButton(); UndockButton->setObjectName("undockButton"); @@ -145,26 +157,17 @@ void DockAreaTitleBarPrivate::createButtons() #ifndef QT_NO_TOOLTIP UndockButton->setToolTip(QObject::tr("Detach Group")); #endif - QIcon UndockIcon; - normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarNormalButton, 0, UndockButton); - UndockIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - UndockIcon.addPixmap(normalPixmap, QIcon::Normal); - UndockButton->setIcon(UndockIcon); - UndockButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + setTitleBarButtonIcon(UndockButton, QStyle::SP_TitleBarNormalButton); + UndockButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); TopLayout->addWidget(UndockButton, 0); _this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked())); + + // Close button CloseButton = new tTileBarButton(); CloseButton->setObjectName("closeButton"); CloseButton->setAutoRaise(true); - - // The standard icons does not look good on high DPI screens - QIcon CloseIcon;// = _this->style()->standardIcon(QStyle::SP_TitleBarCloseButton); - normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton); - CloseIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - CloseIcon.addPixmap(normalPixmap, QIcon::Normal); - - CloseButton->setIcon(CloseIcon); + setTitleBarButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton); #ifndef QT_NO_TOOLTIP if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) { diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 44a0a2b..dfe5d6d 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -145,7 +145,11 @@ DockManagerPrivate::DockManagerPrivate(CDockManager* _public) : void DockManagerPrivate::loadStylesheet() { QString Result; +#ifdef Q_OS_LINUX + QFile StyleSheetFile(":ads/stylesheets/default_linux.css"); +#else QFile StyleSheetFile(":ads/stylesheets/default.css"); +#endif StyleSheetFile.open(QIODevice::ReadOnly); QTextStream StyleSheetStream(&StyleSheetFile); Result = StyleSheetStream.readAll(); diff --git a/src/DockOverlay.cpp b/src/DockOverlay.cpp index b1cb4f7..896de27 100644 --- a/src/DockOverlay.cpp +++ b/src/DockOverlay.cpp @@ -132,6 +132,20 @@ struct DockOverlayCrossPrivate return Color; } + //============================================================================ + /** + * Helper function that returns the drop indicator width depending on the + * operating system + */ + qreal dropIndicatiorWidth() const + { + #ifdef Q_OS_LINUX + return 40; + #else + return static_cast(l->fontMetrics().height()) * 3.f; + #endif + } + //============================================================================ QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea, @@ -140,7 +154,7 @@ struct DockOverlayCrossPrivate QLabel* l = new QLabel(); l->setObjectName("DockWidgetAreaLabel"); - const qreal metric = static_cast(l->fontMetrics().height()) * 3.f; + const qreal metric = dropIndicatiorWidth(); const QSizeF size(metric, metric); l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode)); @@ -154,7 +168,7 @@ struct DockOverlayCrossPrivate void updateDropIndicatorIcon(QWidget* DropIndicatorWidget) { QLabel* l = qobject_cast(DropIndicatorWidget); - const qreal metric = static_cast(l->fontMetrics().height()) * 3.f; + const qreal metric = dropIndicatiorWidth(); const QSizeF size(metric, metric); int Area = l->property("dockWidgetArea").toInt(); diff --git a/src/DockWidgetTab.cpp b/src/DockWidgetTab.cpp index 496a5f8..6e05086 100644 --- a/src/DockWidgetTab.cpp +++ b/src/DockWidgetTab.cpp @@ -151,7 +151,8 @@ void DockWidgetTabPrivate::createLayout() CloseIcon.addPixmap(normalPixmap, QIcon::Normal); CloseIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); CloseButton->setIcon(CloseIcon); - CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); CloseButton->setVisible(false); #ifndef QT_NO_TOOLTIP CloseButton->setToolTip(QObject::tr("Close Tab")); @@ -223,7 +224,7 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState) if (DraggingFloatingWidget == DraggingState) { - FloatingWidget->startDragging(DragStartMousePosition, Size); + FloatingWidget->startDragging(DragStartMousePosition, Size, _this); auto Overlay = DockWidget->dockManager()->containerOverlay(); Overlay->setAllowedAreas(OuterDockAreas); this->FloatingWidget = FloatingWidget; diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index aa92bec..56b499a 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include "DockContainerWidget.h" #include "DockAreaWidget.h" @@ -45,7 +47,12 @@ #include "DockWidget.h" #include "DockOverlay.h" +#ifdef Q_OS_LINUX +#include "FloatingWidgetTitleBar.h" +#endif + #include +#include namespace ads { @@ -63,6 +70,8 @@ struct FloatingDockContainerPrivate QPoint DragStartMousePosition; CDockContainerWidget* DropContainer = nullptr; CDockAreaWidget* SingleDockArea = nullptr; + QWidget* MouseEventHandler = nullptr; + CFloatingWidgetTitleBar* TitleBar = nullptr; /** * Private data constructor @@ -84,6 +93,15 @@ struct FloatingDockContainerPrivate { DraggingState = StateId; } + + void setWindowTitle(const QString& Text) + { +#ifdef Q_OS_LINUX + TitleBar->setTitle(Text); +#else + _this->setWindowTitle(Text); +#endif + } }; // struct FloatingDockContainerPrivate @@ -216,26 +234,37 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos) //============================================================================ CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) : - QWidget(DockManager, Qt::Window), + tFloatingWidgetBase(DockManager), d(new FloatingDockContainerPrivate(this)) { - setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); - d->DockManager = DockManager; + d->DockManager = DockManager; + d->DockContainer = new CDockContainerWidget(DockManager, this); + connect(d->DockContainer, SIGNAL(dockAreasAdded()), this, SLOT(onDockAreasAddedOrRemoved())); + connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this, SLOT(onDockAreasAddedOrRemoved())); + +#ifdef Q_OS_LINUX + d->TitleBar = new CFloatingWidgetTitleBar(this); + setWindowFlag(Qt::Tool); + QDockWidget::setWidget(d->DockContainer); + QDockWidget::setFloating(true); + QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures); + setTitleBarWidget(d->TitleBar); + connect(d->TitleBar, SIGNAL(closeRequested()), SLOT(close())); +#else + setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); setLayout(l); +#endif - d->DockContainer = new CDockContainerWidget(DockManager, this); - connect(d->DockContainer, SIGNAL(dockAreasAdded()), this, SLOT(onDockAreasAddedOrRemoved())); - connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this, SLOT(onDockAreasAddedOrRemoved())); - l->addWidget(d->DockContainer); DockManager->registerFloatingWidget(this); + // 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); + qApp->installEventFilter(this); } @@ -244,14 +273,20 @@ CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget* DockArea) : CFloatingDockContainer(DockArea->dockManager()) { d->DockContainer->addDockArea(DockArea); +#ifdef Q_OS_LINUX + d->TitleBar->enableCloseButton(isClosable()); +#endif } //============================================================================ CFloatingDockContainer::CFloatingDockContainer(CDockWidget* DockWidget) : - CFloatingDockContainer(DockWidget->dockManager()) + CFloatingDockContainer(DockWidget->dockManager()) { - d->DockContainer->addDockWidget(CenterDockWidgetArea, DockWidget); + d->DockContainer->addDockWidget(CenterDockWidgetArea, DockWidget); +#ifdef Q_OS_LINUX + d->TitleBar->enableCloseButton(isClosable()); +#endif } //============================================================================ @@ -290,7 +325,7 @@ void CFloatingDockContainer::changeEvent(QEvent *event) void CFloatingDockContainer::moveEvent(QMoveEvent *event) { QWidget::moveEvent(event); - switch (d->DraggingState) + switch (d->DraggingState) { case DraggingMousePressed: d->setState(DraggingFloatingWidget); @@ -302,7 +337,7 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event) break; default: break; - } + } } @@ -323,11 +358,15 @@ void CFloatingDockContainer::closeEvent(QCloseEvent *event) // Starting from Qt version 5.12.2 this seems to work again. But // now the QEvent::NonClientAreaMouseButtonPress function returns always // Qt::RightButton even if the left button was pressed +#ifndef Q_OS_LINUX #if (QT_VERSION > QT_VERSION_CHECK(5, 9, 2) && QT_VERSION < QT_VERSION_CHECK(5, 12, 2)) event->ignore(); this->hide(); #else Super::closeEvent(event); +#endif +#else // Q_OS_LINUX + Super::closeEvent(event); #endif } else @@ -438,12 +477,13 @@ bool CFloatingDockContainer::event(QEvent *e) //============================================================================ bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event) { - Q_UNUSED(watched); - if (event->type() == QEvent::MouseButtonRelease && d->isState(DraggingFloatingWidget)) + Q_UNUSED(watched); + if (event->type() == QEvent::MouseButtonRelease && d->isState(DraggingFloatingWidget)) { qDebug() << "FloatingWidget::eventFilter QEvent::MouseButtonRelease"; + finishDragging(); d->titleMouseReleaseEvent(); - } + } return false; } @@ -451,11 +491,23 @@ bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event) //============================================================================ void CFloatingDockContainer::startFloating(const QPoint& DragStartMousePos, const QSize& Size, - eDragState DragState) + eDragState DragState, QWidget* MouseEventHandler) { resize(Size); d->setState(DragState); d->DragStartMousePosition = DragStartMousePos; +#ifdef Q_OS_LINUX + if (DraggingFloatingWidget == DragState) + { + setAttribute(Qt::WA_X11NetWmWindowTypeDock, true); + setWindowOpacity(0.6); + d->MouseEventHandler = MouseEventHandler; + if (d->MouseEventHandler) + { + d->MouseEventHandler->grabMouse(); + } + } +#endif moveFloating(); show(); @@ -467,7 +519,7 @@ void CFloatingDockContainer::moveFloating() { int BorderSize = (frameSize().width() - size().width()) / 2; const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0); - move(moveToPos); + move(moveToPos); } @@ -486,7 +538,7 @@ void CFloatingDockContainer::onDockAreasAddedOrRemoved() if (TopLevelDockArea) { d->SingleDockArea = TopLevelDockArea; - this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); + d->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this, SLOT(onDockAreaCurrentChanged(int))); } @@ -498,7 +550,7 @@ void CFloatingDockContainer::onDockAreasAddedOrRemoved() SLOT(onDockAreaCurrentChanged(int))); d->SingleDockArea = nullptr; } - this->setWindowTitle(qApp->applicationDisplayName()); + d->setWindowTitle(qApp->applicationDisplayName()); } } @@ -509,11 +561,11 @@ void CFloatingDockContainer::updateWindowTitle() auto TopLevelDockArea = d->DockContainer->topLevelDockArea(); if (TopLevelDockArea) { - this->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle()); + d->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle()); } else { - this->setWindowTitle(qApp->applicationDisplayName()); + d->setWindowTitle(qApp->applicationDisplayName()); } } @@ -522,7 +574,7 @@ void CFloatingDockContainer::updateWindowTitle() void CFloatingDockContainer::onDockAreaCurrentChanged(int Index) { Q_UNUSED(Index); - this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); + d->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); } @@ -560,6 +612,24 @@ QList CFloatingDockContainer::dockWidgets() const } + +//============================================================================ +void CFloatingDockContainer::finishDragging() +{ + std::cout << "CFloatingDockContainer::finishDragging" << std::endl; +#ifdef Q_OS_LINUX + setAttribute(Qt::WA_X11NetWmWindowTypeDock, false); + setWindowOpacity(1); + activateWindow(); + if (d->MouseEventHandler) + { + d->MouseEventHandler->releaseMouse(); + d->MouseEventHandler = nullptr; + } +#endif +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 31af860..898f31c 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -29,10 +29,16 @@ //============================================================================ // INCLUDES //============================================================================ -#include - #include "ads_globals.h" +#ifdef Q_OS_LINUX +#include +#define tFloatingWidgetBase QDockWidget +#else +#include +#define tFloatingWidgetBase QWidget +#endif + class QXmlStreamReader; namespace ads @@ -49,13 +55,14 @@ class CDockWidgetTab; struct DockWidgetTabPrivate; class CDockAreaTitleBar; struct DockAreaTitleBarPrivate; +class CFloatingWidgetTitleBar; /** * This implements a floating widget that is a dock container that accepts * docking of dock widgets like the main window and that can be docked into * another dock container */ -class ADS_EXPORT CFloatingDockContainer : public QWidget +class ADS_EXPORT CFloatingDockContainer : public tFloatingWidgetBase { Q_OBJECT private: @@ -70,6 +77,7 @@ private: friend struct DockAreaTitleBarPrivate; friend class CDockWidget; friend class CDockAreaWidget; + friend class CFloatingWidgetTitleBar; private slots: void onDockAreasAddedOrRemoved(); @@ -82,23 +90,30 @@ protected: * depending on the start position given in Pos parameter */ void startFloating(const QPoint& DragStartMousePos, const QSize& Size, - eDragState DragState); + eDragState DragState, QWidget* MouseEventHandler); /** * Call this function to start dragging the floating widget */ - void startDragging(const QPoint& DragStartMousePos, const QSize& Size) + void startDragging(const QPoint& DragStartMousePos, const QSize& Size, + QWidget* MouseEventHandler) { - startFloating(DragStartMousePos, Size, DraggingFloatingWidget); + startFloating(DragStartMousePos, Size, DraggingFloatingWidget, MouseEventHandler); } + /** + * Call this function if you explecitely want to signal that dragging has + * finished + */ + void finishDragging(); + /** * Call this function if you just want to initialize the position * and size of the floating widget */ void initFloatingGeometry(const QPoint& DragStartMousePos, const QSize& Size) { - startFloating(DragStartMousePos, Size, DraggingInactive); + startFloating(DragStartMousePos, Size, DraggingInactive, nullptr); } /** diff --git a/src/FloatingWidgetTitleBar.h b/src/FloatingWidgetTitleBar.h new file mode 100644 index 0000000..6f02612 --- /dev/null +++ b/src/FloatingWidgetTitleBar.h @@ -0,0 +1,68 @@ +#ifndef FLOATINGWIDGETTITLEBAR_H +#define FLOATINGWIDGETTITLEBAR_H +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file FloatingWidgetTitleBar.h +/// \author Uwe Kindler +/// \date 13.05.2019 +/// \brief Declaration of CFloatingWidgetTitleBar class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include + +namespace ads +{ +class CFloatingDockContainer; + +struct FloatingWidgetTitleBarPrivate; + +class CFloatingWidgetTitleBar : public QWidget +{ + Q_OBJECT +private: + FloatingWidgetTitleBarPrivate* d; ///< private data (pimpl) + +protected: + virtual void mousePressEvent(QMouseEvent* ev) override; + virtual void mouseReleaseEvent(QMouseEvent* ev) override; + virtual void mouseMoveEvent(QMouseEvent* ev) override; + +public: + using Super = QWidget; + explicit CFloatingWidgetTitleBar (CFloatingDockContainer *parent = nullptr); + + /** + * Virtual Destructor + */ + virtual ~CFloatingWidgetTitleBar(); + + void enableCloseButton(bool Enable); + + void setTitle(const QString& Text); + +signals: + void closeRequested(); +}; +} // namespace ads +#endif // FLOATINGWIDGETTITLEBAR_H diff --git a/src/ads.qrc b/src/ads.qrc index 8e42166..ecb2a6e 100644 --- a/src/ads.qrc +++ b/src/ads.qrc @@ -3,5 +3,6 @@ stylesheets/default.css images/close-button.svg images/close-button-disabled.svg + stylesheets/default_linux.css diff --git a/src/src.pro b/src/src.pro index b97c1cf..2afe2ec 100644 --- a/src/src.pro +++ b/src/src.pro @@ -40,7 +40,8 @@ HEADERS += \ DockOverlay.h \ DockSplitter.h \ DockAreaTitleBar.h \ - ElidingLabel.h + ElidingLabel.h \ + FloatingWidgetTitleBar.h @@ -56,7 +57,8 @@ SOURCES += \ DockOverlay.cpp \ DockSplitter.cpp \ DockAreaTitleBar.cpp \ - ElidingLabel.cpp + ElidingLabel.cpp \ + FloatingWidgetTitleBar.cpp isEmpty(PREFIX){ PREFIX=..\installed @@ -66,3 +68,5 @@ headers.path=$$PREFIX/include headers.files=$$HEADERS target.path=$$PREFIX/lib INSTALLS += headers target + +DISTFILES +=