Merge pull request #4 from githubuser0xFFFF/master

fork update
This commit is contained in:
Andreev Alexander 2019-09-14 20:18:35 +02:00 committed by GitHub
commit 84b501205a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1198 additions and 427 deletions

View File

@ -71,15 +71,6 @@
</storageModule> </storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/> <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope"/> <storageModule moduleId="refreshScope"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base.1947822681;cdt.managedbuild.tool.gnu.cpp.compiler.input.1318830536">
<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.c.compiler.mingw.base.389117097;cdt.managedbuild.tool.gnu.c.compiler.input.1568363924">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"> <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
<buildTargets> <buildTargets>
<target name="Build all" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder"> <target name="Build all" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
@ -356,4 +347,13 @@
</target> </target>
</buildTargets> </buildTargets>
</storageModule> </storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base.1947822681;cdt.managedbuild.tool.gnu.cpp.compiler.input.1318830536">
<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.c.compiler.mingw.base.389117097;cdt.managedbuild.tool.gnu.c.compiler.input.1568363924">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
</cproject> </cproject>

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ moc_*
ui_* ui_*
Makefile Makefile
# IDEs
.idea

View File

@ -40,6 +40,7 @@ set(ads_SRCS
src/ElidingLabel.cpp src/ElidingLabel.cpp
src/FloatingDockContainer.cpp src/FloatingDockContainer.cpp
src/ads.qrc src/ads.qrc
src/linux/FloatingWidgetTitleBar.cpp
) )
set(ads_INSTALL_INCLUDE set(ads_INSTALL_INCLUDE
src/ads_globals.h src/ads_globals.h
@ -54,6 +55,7 @@ set(ads_INSTALL_INCLUDE
src/DockWidgetTab.h src/DockWidgetTab.h
src/ElidingLabel.h src/ElidingLabel.h
src/FloatingDockContainer.h src/FloatingDockContainer.h
src/linux/FloatingWidgetTitleBar.h
) )
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(ads_PlatformDir "x86") set(ads_PlatformDir "x86")
@ -84,7 +86,7 @@ install(TARGETS qtadvanceddocking
ARCHIVE DESTINATION lib COMPONENT library ARCHIVE DESTINATION lib COMPONENT library
) )
target_include_directories(qtadvanceddocking PUBLIC target_include_directories(qtadvanceddocking PUBLIC
$<BUILD_INTERFACE:${ads_INCLUDE}> "$<BUILD_INTERFACE:${ads_INCLUDE}>"
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include>
) )
target_link_libraries(qtadvanceddocking PUBLIC ${ads_LIBS}) target_link_libraries(qtadvanceddocking PUBLIC ${ads_LIBS})

View File

@ -1,6 +1,7 @@
# Advanced Docking System for Qt # Advanced Docking System for Qt
[![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System) [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
[![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
[![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](gnu-lgpl-v2.1.md)
@ -8,7 +9,7 @@ Qt Advanced Docking System lets you create customizable layouts using a full
featured window docking system similar to what is found in many popular featured window docking system similar to what is found in many popular
integrated development environements (IDEs) such as Visual Studio. integrated development environements (IDEs) such as Visual Studio.
Everything is implemented with standard Qt functionality without any Everything is implemented with standard Qt functionality without any
platform specific code. Basic usage of QWidgets an QLayouts and using basic platform specific code. Basic usage of QWidgets and QLayouts and using basic
styles as much as possible. styles as much as possible.
This work is based on and inspired by the This work is based on and inspired by the
@ -60,9 +61,22 @@ main window layout.
![Perspective](doc/perspectives_dark.png) ![Perspective](doc/perspectives_dark.png)
## Tested Compatible Environments ## Tested Compatible Environments
- Windows 10 [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master) ### Windows
- Ubuntu [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System) Windows 10 [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
- macOS [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
The library was developed on and for Windows. It is used in a commercial Windows application and is therefore constantly tested.
### macOS
macOS [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
The application can be compiled for macOS. A user reported, that the library works on macOS. If have not tested it.
### Linux
Ubuntu [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
The application can be compiled for Linux and has been developed and tested with **Kubuntu 18.04**.
![Advanced Docking on Linux](doc/linux_kubuntu_1804.png)
## Build ## Build
Open the `ads.pro` with QtCreator and start the build, that's it. Open the `ads.pro` with QtCreator and start the build, that's it.
@ -144,5 +158,6 @@ MainWindow::~MainWindow()
- Manuel Freiholz - Manuel Freiholz
## License information ## License information
[![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](gnu-lgpl-v2.1.md)
This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md) This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md)

View File

@ -23,8 +23,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(ads_demo_SRCS set(ads_demo_SRCS
main.cpp main.cpp
MainWindow.cpp MainWindow.cpp
MainWindow.ui mainwindow.ui
main.qrc
) )
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS}) add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
if(BUILD_STATIC) if(BUILD_STATIC)

View File

@ -88,6 +88,32 @@ static ads::CDockWidget* createLongTextLabelDockWidget(QMenu* ViewMenu)
} }
/**
* Function returns a features string with closable (c), movable (m) and floatable (f)
* features. i.e. The following string is for a not closable but movable and floatable
* widget: c- m+ f+
*/
static QString featuresString(ads::CDockWidget* DockWidget)
{
auto f = DockWidget->features();
return QString("c%1 m%2 f%3")
.arg(f.testFlag(ads::CDockWidget::DockWidgetClosable) ? "+" : "-")
.arg(f.testFlag(ads::CDockWidget::DockWidgetMovable) ? "+" : "-")
.arg(f.testFlag(ads::CDockWidget::DockWidgetFloatable) ? "+" : "-");
}
/**
* Appends the string returned by featuresString() to the window title of
* the given DockWidget
*/
static void appendFeaturStringToWindowTitle(ads::CDockWidget* DockWidget)
{
DockWidget->setWindowTitle(DockWidget->windowTitle()
+ QString(" (%1)").arg(featuresString(DockWidget)));
}
//============================================================================ //============================================================================
static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu) static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu)
{ {
@ -110,7 +136,8 @@ static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu)
QFileSystemModel* m = new QFileSystemModel(w); QFileSystemModel* m = new QFileSystemModel(w);
m->setRootPath(QDir::currentPath()); m->setRootPath(QDir::currentPath());
w->setModel(m); w->setModel(m);
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1").arg(FileSystemCount++)); ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1")
.arg(FileSystemCount++));
DockWidget->setWidget(w); DockWidget->setWidget(w);
ViewMenu->addAction(DockWidget->toggleViewAction()); ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget; return DockWidget;
@ -185,6 +212,8 @@ void MainWindowPrivate::createContent()
ToolBar->addAction(ui.actionSaveState); ToolBar->addAction(ui.actionSaveState);
ToolBar->addAction(ui.actionRestoreState); ToolBar->addAction(ui.actionRestoreState);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false); FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
appendFeaturStringToWindowTitle(FileSystemWidget);
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget); auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
DockWidget = createCalendarDockWidget(ViewMenu); DockWidget = createCalendarDockWidget(ViewMenu);
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false); DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
@ -270,19 +299,27 @@ CMainWindow::CMainWindow(QWidget *parent) :
d->ui.setupUi(this); d->ui.setupUi(this);
d->createActions(); d->createActions();
// uncomment the following line if the tab close button should be
// a QToolButton instead of a QPushButton
// CDockManager::setConfigFlags(CDockManager::configFlags() | CDockManager::TabCloseButtonIsToolButton);
// 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::RetainTabSizeWhenCloseButtonHidden, true);
// Now create the dock manager and its content // Now create the dock manager and its content
d->DockManager = new CDockManager(this); d->DockManager = new CDockManager(this);
// Uncomment the following line to have the old style where the dock // Uncomment the following line to have the old style where the dock
// area close button closes the active tab // area close button closes the active tab
//d->DockManager->setConfigFlags({ // CDockManager::setConfigFlags({CDockManager::DockAreaHasCloseButton
// CDockManager::DockAreaHasCloseButton | CDockManager::DockAreaCloseButtonClosesTab}); // | CDockManager::DockAreaCloseButtonClosesTab});
connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)), connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)),
d->DockManager, SLOT(openPerspective(const QString&))); d->DockManager, SLOT(openPerspective(const QString&)));
d->createContent(); d->createContent();
// Default window geometry // Default window geometry
resize(800, 600); resize(1280, 720);
d->restoreState(); d->restoreState();
d->restorePerspectives(); d->restorePerspectives();

View File

@ -21,7 +21,6 @@ HEADERS += \
FORMS += \ FORMS += \
mainwindow.ui mainwindow.ui
RESOURCES += main.qrc
LIBS += -L$${ADS_OUT_ROOT}/lib LIBS += -L$${ADS_OUT_ROOT}/lib

View File

@ -1,3 +0,0 @@
<RCC>
<qresource prefix="/main"/>
</RCC>

BIN
doc/linux_kubuntu_1804.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

View File

@ -23,7 +23,6 @@ HEADERS += \
FORMS += \ FORMS += \
MainWindow.ui MainWindow.ui
#RESOURCES += main.qrc
LIBS += -L$${ADS_OUT_ROOT}/lib LIBS += -L$${ADS_OUT_ROOT}/lib

View File

@ -113,7 +113,7 @@ CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
d(new DockAreaTabBarPrivate(this)) d(new DockAreaTabBarPrivate(this))
{ {
d->DockArea = parent; d->DockArea = parent;
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true); setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@ -171,7 +171,7 @@ void CDockAreaTabBar::mouseReleaseEvent(QMouseEvent* ev)
{ {
if (ev->button() == Qt::LeftButton) if (ev->button() == Qt::LeftButton)
{ {
qDebug() << "CTabsScrollArea::mouseReleaseEvent"; ADS_PRINT("CTabsScrollArea::mouseReleaseEvent");
ev->accept(); ev->accept();
d->FloatingWidget = nullptr; d->FloatingWidget = nullptr;
d->DragStartMousePos = QPoint(); d->DragStartMousePos = QPoint();
@ -205,10 +205,17 @@ void CDockAreaTabBar::mouseMoveEvent(QMouseEvent* ev)
return; return;
} }
// If one single dock widget in this area is not floatable then the whole
// area is not floatable
if (!d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable))
{
return;
}
int DragDistance = (d->DragStartMousePos - ev->pos()).manhattanLength(); int DragDistance = (d->DragStartMousePos - ev->pos()).manhattanLength();
if (DragDistance >= CDockManager::startDragDistance()) if (DragDistance >= CDockManager::startDragDistance())
{ {
qDebug() << "CTabsScrollArea::startFloating"; ADS_PRINT("CTabsScrollArea::startFloating");
startFloating(d->DragStartMousePos); startFloating(d->DragStartMousePos);
auto Overlay = d->DockArea->dockManager()->containerOverlay(); auto Overlay = d->DockArea->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas); Overlay->setAllowedAreas(OuterDockAreas);
@ -228,6 +235,11 @@ void CDockAreaTabBar::mouseDoubleClickEvent(QMouseEvent *event)
{ {
return; return;
} }
if (!d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable))
{
return;
}
makeAreaFloating(event->pos(), DraggingInactive); makeAreaFloating(event->pos(), DraggingInactive);
} }
@ -238,7 +250,7 @@ CFloatingDockContainer* CDockAreaTabBar::makeAreaFloating(const QPoint& Offset,
{ {
QSize Size = d->DockArea->size(); QSize Size = d->DockArea->size();
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(d->DockArea); CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(d->DockArea);
FloatingWidget->startFloating(Offset, Size, DragState); FloatingWidget->startFloating(Offset, Size, DragState, nullptr);
auto TopLevelDockWidget = FloatingWidget->topLevelDockWidget(); auto TopLevelDockWidget = FloatingWidget->topLevelDockWidget();
if (TopLevelDockWidget) if (TopLevelDockWidget)
{ {
@ -309,7 +321,7 @@ void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
{ {
return; return;
} }
qDebug() << "CDockAreaTabBar::removeTab "; ADS_PRINT("CDockAreaTabBar::removeTab ");
int NewCurrentIndex = currentIndex(); int NewCurrentIndex = currentIndex();
int RemoveIndex = d->TabsLayout->indexOf(Tab); int RemoveIndex = d->TabsLayout->indexOf(Tab);
if (count() == 1) if (count() == 1)
@ -352,7 +364,7 @@ void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
d->TabsLayout->removeWidget(Tab); d->TabsLayout->removeWidget(Tab);
Tab->disconnect(this); Tab->disconnect(this);
Tab->removeEventFilter(this); Tab->removeEventFilter(this);
qDebug() << "NewCurrentIndex " << NewCurrentIndex; ADS_PRINT("NewCurrentIndex " << NewCurrentIndex);
if (NewCurrentIndex != d->CurrentIndex) if (NewCurrentIndex != d->CurrentIndex)
{ {
setCurrentIndex(NewCurrentIndex); setCurrentIndex(NewCurrentIndex);
@ -480,7 +492,7 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
{ {
if (MousePos.x() > tab(count() - 1)->geometry().right()) if (MousePos.x() > tab(count() - 1)->geometry().right())
{ {
qDebug() << "after all tabs"; ADS_PRINT("after all tabs");
toIndex = count() - 1; toIndex = count() - 1;
} }
else else
@ -493,7 +505,7 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
d->TabsLayout->insertWidget(toIndex, MovingTab); d->TabsLayout->insertWidget(toIndex, MovingTab);
if (toIndex >= 0) if (toIndex >= 0)
{ {
qDebug() << "tabMoved from " << fromIndex << " to " << toIndex; ADS_PRINT("tabMoved from " << fromIndex << " to " << toIndex);
emit tabMoved(fromIndex, toIndex); emit tabMoved(fromIndex, toIndex);
setCurrentIndex(toIndex); setCurrentIndex(toIndex);
} }
@ -553,6 +565,23 @@ bool CDockAreaTabBar::isTabOpen(int Index) const
return !tab(Index)->isHidden(); return !tab(Index)->isHidden();
} }
//===========================================================================
QSize CDockAreaTabBar::minimumSizeHint() const
{
QSize Size = sizeHint();
Size.setWidth(Super::minimumSizeHint().width());// this defines the minimum width of a dock area
return Size;
}
//===========================================================================
QSize CDockAreaTabBar::sizeHint() const
{
QSize Size = Super::sizeHint();
Size.setHeight(d->TabsContainerWidget->sizeHint().height());
return Size;
}
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -44,6 +44,10 @@ class CFloatingDockContainer;
* Custom tabbar implementation for tab area that is shown on top of a * Custom tabbar implementation for tab area that is shown on top of a
* dock area widget. * dock area widget.
* The tabbar displays the tab widgets of the contained dock widgets. * The tabbar displays the tab widgets of the contained dock widgets.
* We cannot use QTabBar here because it does a lot of fancy animations
* that will crash the application if a tab is removed while the animation
* has not finished. And we need to remove a tab, if the user drags a
* a dock widget out of a group of tabbed widgets
*/ */
class CDockAreaTabBar : public QScrollArea class CDockAreaTabBar : public QScrollArea
{ {
@ -61,6 +65,7 @@ private slots:
protected: protected:
virtual void wheelEvent(QWheelEvent* Event) override; virtual void wheelEvent(QWheelEvent* Event) override;
/** /**
* Stores mouse position to detect dragging * Stores mouse position to detect dragging
*/ */
@ -96,6 +101,7 @@ protected:
public: public:
using Super = QScrollArea; using Super = QScrollArea;
/** /**
* Default Constructor * Default Constructor
*/ */
@ -150,6 +156,21 @@ public:
*/ */
bool isTabOpen(int Index) const; bool isTabOpen(int Index) const;
/**
* Overrides the minimumSizeHint() function of QScrollArea
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
* area because even if the scrollbars are invisible, the required speace
* is reserved in the minimumSizeHint(). This override simply returns
* sizeHint();
*/
virtual QSize minimumSizeHint() const override;
/**
* The function provides a sizeHint that matches the height of the
* internal viewport.
*/
virtual QSize sizeHint() const override;
public slots: public slots:
/** /**
* This property sets the index of the tab bar's visible tab * This property sets the index of the tab bar's visible tab

View File

@ -95,8 +95,26 @@ struct DockAreaTitleBarPrivate
*/ */
bool testConfigFlag(CDockManager::eConfigFlag Flag) const bool testConfigFlag(CDockManager::eConfigFlag Flag) const
{ {
return DockArea->dockManager()->configFlags().testFlag(Flag); return CDockManager::configFlags().testFlag(Flag);
} }
/**
* Helper function to set title bar button icons depending on operating
* system and to avoid duplicated code. On windows the standard icons
* are blurry since Qt 5.11 so we need to do some additional steps
*/
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 };// struct DockAreaTitleBarPrivate
@ -112,50 +130,47 @@ DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(CDockAreaTitleBar* _public) :
//============================================================================ //============================================================================
void DockAreaTitleBarPrivate::createButtons() void DockAreaTitleBarPrivate::createButtons()
{ {
QSizePolicy ButtonSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
// Tabs menu button
TabsMenuButton = new tTileBarButton(); TabsMenuButton = new tTileBarButton();
TabsMenuButton->setObjectName("tabsMenuButton"); TabsMenuButton->setObjectName("tabsMenuButton");
TabsMenuButton->setAutoRaise(true); TabsMenuButton->setAutoRaise(true);
TabsMenuButton->setPopupMode(QToolButton::InstantPopup); TabsMenuButton->setPopupMode(QToolButton::InstantPopup);
TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton)); setTitleBarButtonIcon(TabsMenuButton, QStyle::SP_TitleBarUnshadeButton);
QMenu* TabsMenu = new QMenu(TabsMenuButton); QMenu* TabsMenu = new QMenu(TabsMenuButton);
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
TabsMenu->setToolTipsVisible(true); TabsMenu->setToolTipsVisible(true);
#endif #endif
_this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow())); _this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow()));
TabsMenuButton->setMenu(TabsMenu); TabsMenuButton->setMenu(TabsMenu);
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
TabsMenuButton->setToolTip(QObject::tr("List all tabs")); TabsMenuButton->setToolTip(QObject::tr("List all tabs"));
#endif #endif
TabsMenuButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); TabsMenuButton->setSizePolicy(ButtonSizePolicy);
TopLayout->addWidget(TabsMenuButton, 0); TopLayout->addWidget(TabsMenuButton, 0);
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)), _this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
SLOT(onTabsMenuActionTriggered(QAction*))); SLOT(onTabsMenuActionTriggered(QAction*)));
// Undock button // Undock button
UndockButton = new tTileBarButton(); UndockButton = new tTileBarButton();
UndockButton->setObjectName("undockButton"); UndockButton->setObjectName("undockButton");
UndockButton->setAutoRaise(true); UndockButton->setAutoRaise(true);
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
UndockButton->setToolTip(QObject::tr("Detach Group")); UndockButton->setToolTip(QObject::tr("Detach Group"));
#endif #endif
UndockButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarNormalButton)); setTitleBarButtonIcon(UndockButton, QStyle::SP_TitleBarNormalButton);
UndockButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); UndockButton->setSizePolicy(ButtonSizePolicy);
TopLayout->addWidget(UndockButton, 0); TopLayout->addWidget(UndockButton, 0);
_this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked())); _this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked()));
// Close button
CloseButton = new tTileBarButton(); CloseButton = new tTileBarButton();
CloseButton->setObjectName("closeButton"); CloseButton->setObjectName("closeButton");
CloseButton->setAutoRaise(true); CloseButton->setAutoRaise(true);
setTitleBarButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton);
// The standard icons do not look good on high DPI screens #ifndef QT_NO_TOOLTIP
QIcon CloseIcon = _this->style()->standardIcon(QStyle::SP_TitleBarCloseButton);
QPixmap normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton);
QPixmap disabledPixmap = internal::createTransparentPixmap(normalPixmap, 0.25);
CloseIcon.addPixmap(disabledPixmap, QIcon::Disabled);
CloseButton->setIcon(CloseIcon);
#ifndef QT_NO_TOOLTIP
if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
{ {
CloseButton->setToolTip(QObject::tr("Close Active Tab")); CloseButton->setToolTip(QObject::tr("Close Active Tab"));
@ -164,9 +179,13 @@ void DockAreaTitleBarPrivate::createButtons()
{ {
CloseButton->setToolTip(QObject::tr("Close Group")); CloseButton->setToolTip(QObject::tr("Close Group"));
} }
#endif #endif
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); CloseButton->setSizePolicy(ButtonSizePolicy);
TopLayout->addWidget(CloseButton, 0); CloseButton->setIconSize(QSize(16, 16));
if (testConfigFlag(CDockManager::DockAreaHasCloseButton))
{
TopLayout->addWidget(CloseButton, 0);
}
_this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked())); _this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked()));
} }
@ -262,7 +281,7 @@ void CDockAreaTitleBar::onTabsMenuAboutToShow()
//============================================================================ //============================================================================
void CDockAreaTitleBar::onCloseButtonClicked() void CDockAreaTitleBar::onCloseButtonClicked()
{ {
qDebug() << "CDockAreaTitleBar::onCloseButtonClicked"; ADS_PRINT("CDockAreaTitleBar::onCloseButtonClicked");
if (d->testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) if (d->testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
{ {
d->TabBar->closeTab(d->TabBar->currentIndex()); d->TabBar->closeTab(d->TabBar->currentIndex());
@ -277,7 +296,10 @@ void CDockAreaTitleBar::onCloseButtonClicked()
//============================================================================ //============================================================================
void CDockAreaTitleBar::onUndockButtonClicked() void CDockAreaTitleBar::onUndockButtonClicked()
{ {
d->TabBar->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); if (d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable))
{
d->TabBar->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
}
} }
@ -332,9 +354,10 @@ void CDockAreaTitleBar::setVisible(bool Visible)
void CDockAreaTitleBar::showContextMenu(const QPoint& pos) void CDockAreaTitleBar::showContextMenu(const QPoint& pos)
{ {
QMenu Menu(this); QMenu Menu(this);
Menu.addAction(tr("Detach Area"), this, SLOT(onUndockButtonClicked())); auto Action = Menu.addAction(tr("Detach Area"), this, SLOT(onUndockButtonClicked()));
Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable));
Menu.addSeparator(); Menu.addSeparator();
auto Action = Menu.addAction(tr("Close Area"), this, SLOT(onCloseButtonClicked())); Action = Menu.addAction(tr("Close Area"), this, SLOT(onCloseButtonClicked()));
Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable)); Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable));
Menu.addAction(tr("Close Other Areas"), d->DockArea, SLOT(closeOtherAreas())); Menu.addAction(tr("Close Other Areas"), d->DockArea, SLOT(closeOtherAreas()));
Menu.exec(mapToGlobal(pos)); Menu.exec(mapToGlobal(pos));

View File

@ -43,7 +43,9 @@ class CDockAreaWidget;
struct DockAreaTitleBarPrivate; struct DockAreaTitleBarPrivate;
/** /**
* Title bar of a dock area * Title bar of a dock area.
* The title bar contains a tabbar with all tabs for a dock widget group and
* with a tabs menu button, a undock button and a close button.
*/ */
class CDockAreaTitleBar : public QFrame class CDockAreaTitleBar : public QFrame
{ {
@ -61,6 +63,10 @@ private slots:
void showContextMenu(const QPoint& pos); void showContextMenu(const QPoint& pos);
public slots: public slots:
/**
* Call this slot to tell the title bar that it should update the tabs menu
* the next time it is shown.
*/
void markTabsMenuOutdated(); void markTabsMenuOutdated();
@ -87,7 +93,8 @@ public:
QAbstractButton* button(TitleBarButton which) const; QAbstractButton* button(TitleBarButton which) const;
/** /**
* This function is here for debug reasons * Marks the tabs menu outdated before it calls its base class
* implementation
*/ */
virtual void setVisible(bool Visible) override; virtual void setVisible(bool Visible) override;

View File

@ -63,8 +63,11 @@ static const char* const INDEX_PROPERTY = "index";
static const char* const ACTION_PROPERTY = "action"; static const char* const ACTION_PROPERTY = "action";
/** /**
* New dock area layout mimics stack layout but only inserts the current * Internal dock area layout mimics stack layout but only inserts the current
* widget into the internal QLayout object * widget into the internal QLayout object.
* \warning Only the current widget has a parent. All other widgets
* do not have a parent. That means, a widget that is in this layout may
* return nullptr for its parent() function if it is not the current widget.
*/ */
class CDockAreaLayout class CDockAreaLayout
{ {
@ -118,7 +121,7 @@ public:
} }
/** /**
* Removes the given widget from the lyout * Removes the given widget from the layout
*/ */
void removeWidget(QWidget* Widget) void removeWidget(QWidget* Widget)
{ {
@ -241,7 +244,7 @@ struct DockAreaWidgetPrivate
DockAreaLayout* ContentsLayout = nullptr; DockAreaLayout* ContentsLayout = nullptr;
CDockAreaTitleBar* TitleBar = nullptr; CDockAreaTitleBar* TitleBar = nullptr;
CDockManager* DockManager = nullptr; CDockManager* DockManager = nullptr;
bool UpdateCloseButton = false; bool UpdateTitleBarButtons = false;
/** /**
* Private data constructor * Private data constructor
@ -295,9 +298,9 @@ struct DockAreaWidgetPrivate
} }
/** /**
* Udpates the enable state of the close button * Udpates the enable state of the close and detach button
*/ */
void updateCloseButtonState(); void updateTitleBarButtonStates();
}; };
// struct DockAreaWidgetPrivate // struct DockAreaWidgetPrivate
@ -322,17 +325,19 @@ void DockAreaWidgetPrivate::createTitleBar()
//============================================================================ //============================================================================
void DockAreaWidgetPrivate::updateCloseButtonState() void DockAreaWidgetPrivate::updateTitleBarButtonStates()
{ {
if (_this->isHidden()) if (_this->isHidden())
{ {
UpdateCloseButton = true; UpdateTitleBarButtons = true;
return; return;
} }
TitleBar->button(TitleBarButtonClose)->setEnabled( TitleBar->button(TitleBarButtonClose)->setEnabled(
_this->features().testFlag(CDockWidget::DockWidgetClosable)); _this->features().testFlag(CDockWidget::DockWidgetClosable));
UpdateCloseButton = false; TitleBar->button(TitleBarButtonUndock)->setEnabled(
_this->features().testFlag(CDockWidget::DockWidgetFloatable));
UpdateTitleBarButtons = false;
} }
@ -354,7 +359,7 @@ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget
//============================================================================ //============================================================================
CDockAreaWidget::~CDockAreaWidget() CDockAreaWidget::~CDockAreaWidget()
{ {
qDebug() << "~CDockAreaWidget()"; ADS_PRINT("~CDockAreaWidget()");
delete d->ContentsLayout; delete d->ContentsLayout;
delete d; delete d;
} }
@ -400,28 +405,29 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
setCurrentIndex(index); setCurrentIndex(index);
} }
DockWidget->setDockArea(this); DockWidget->setDockArea(this);
d->updateCloseButtonState(); d->updateTitleBarButtonStates();
} }
//============================================================================ //============================================================================
void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget) void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
{ {
qDebug() << "CDockAreaWidget::removeDockWidget"; ADS_PRINT("CDockAreaWidget::removeDockWidget");
auto NextOpenDockWidget = nextOpenDockWidget(DockWidget); auto NextOpenDockWidget = nextOpenDockWidget(DockWidget);
d->ContentsLayout->removeWidget(DockWidget); d->ContentsLayout->removeWidget(DockWidget);
auto TabWidget = DockWidget->tabWidget(); auto TabWidget = DockWidget->tabWidget();
TabWidget->hide(); TabWidget->hide();
d->tabBar()->removeTab(TabWidget); d->tabBar()->removeTab(TabWidget);
CDockContainerWidget* DockContainer = dockContainer();
if (NextOpenDockWidget) if (NextOpenDockWidget)
{ {
setCurrentDockWidget(NextOpenDockWidget); setCurrentDockWidget(NextOpenDockWidget);
} }
else if (d->ContentsLayout->isEmpty()) else if (d->ContentsLayout->isEmpty() && DockContainer->dockAreaCount() > 1)
{ {
qDebug() << "Dock Area empty"; ADS_PRINT("Dock Area empty");
dockContainer()->removeDockArea(this); DockContainer->removeDockArea(this);
this->deleteLater(); this->deleteLater();
} }
else else
@ -432,16 +438,15 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
hideAreaWithNoVisibleContent(); hideAreaWithNoVisibleContent();
} }
d->updateCloseButtonState(); d->updateTitleBarButtonStates();
updateTitleBarVisibility(); updateTitleBarVisibility();
auto TopLevelDockWidget = dockContainer()->topLevelDockWidget(); auto TopLevelDockWidget = DockContainer->topLevelDockWidget();
if (TopLevelDockWidget) if (TopLevelDockWidget)
{ {
TopLevelDockWidget->emitTopLevelChanged(true); TopLevelDockWidget->emitTopLevelChanged(true);
} }
#if (ADS_DEBUG_LEVEL > 0) #if (ADS_DEBUG_LEVEL > 0)
CDockContainerWidget* DockContainer = dockContainer();
DockContainer->dumpLayout(); DockContainer->dumpLayout();
#endif #endif
} }
@ -481,7 +486,7 @@ void CDockAreaWidget::hideAreaWithNoVisibleContent()
//============================================================================ //============================================================================
void CDockAreaWidget::onTabCloseRequested(int Index) void CDockAreaWidget::onTabCloseRequested(int Index)
{ {
qDebug() << "CDockAreaWidget::onTabCloseRequested " << Index; ADS_PRINT("CDockAreaWidget::onTabCloseRequested " << Index);
dockWidget(Index)->toggleView(false); dockWidget(Index)->toggleView(false);
} }
@ -644,11 +649,11 @@ CDockWidget* CDockAreaWidget::dockWidget(int Index) const
//============================================================================ //============================================================================
void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex) void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
{ {
qDebug() << "CDockAreaWidget::reorderDockWidget"; ADS_PRINT("CDockAreaWidget::reorderDockWidget");
if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0 if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0
|| toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex) || toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex)
{ {
qDebug() << "Invalid index for tab movement" << fromIndex << toIndex; ADS_PRINT("Invalid index for tab movement" << fromIndex << toIndex);
return; return;
} }
@ -703,8 +708,8 @@ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const
auto CurrentDockWidget = currentDockWidget(); auto CurrentDockWidget = currentDockWidget();
QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : ""; QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : "";
s.writeAttribute("Current", Name); s.writeAttribute("Current", Name);
qDebug() << "CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count() ADS_PRINT("CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
<< " Current: " << Name; << " Current: " << Name);
for (int i = 0; i < d->ContentsLayout->count(); ++i) for (int i = 0; i < d->ContentsLayout->count(); ++i)
{ {
dockWidget(i)->saveState(s); dockWidget(i)->saveState(s);
@ -765,9 +770,9 @@ void CDockAreaWidget::toggleView(bool Open)
void CDockAreaWidget::setVisible(bool Visible) void CDockAreaWidget::setVisible(bool Visible)
{ {
Super::setVisible(Visible); Super::setVisible(Visible);
if (d->UpdateCloseButton) if (d->UpdateTitleBarButtons)
{ {
d->updateCloseButtonState(); d->updateTitleBarButtonStates();
} }
} }

View File

@ -185,12 +185,12 @@ public:
QList<CDockWidget*> dockWidgets() const; QList<CDockWidget*> dockWidgets() const;
/** /**
* Returns the number of dock widgets in this area * Returns the number of open dock widgets in this area
*/ */
int openDockWidgetsCount() const; int openDockWidgetsCount() const;
/** /**
* Returns a list of dock widgets that are not closed * Returns a list of dock widgets that are not closed.
*/ */
QList<CDockWidget*> openedDockWidgets() const; QList<CDockWidget*> openedDockWidgets() const;
@ -236,8 +236,10 @@ public:
* This functions returns the dock widget features of all dock widget in * This functions returns the dock widget features of all dock widget in
* this area. * this area.
* A bitwise and is used to combine the flags of all dock widgets. That * A bitwise and is used to combine the flags of all dock widgets. That
* means, if only dock widget does not support a certain flag, the whole * means, if only one single dock widget does not support a certain flag,
* dock are does not support the flag. * the whole dock are does not support the flag. I.e. if one single
* dock widget in this area is not closabe, the whole dock are is not
* closable.
*/ */
CDockWidget::DockWidgetFeatures features() const; CDockWidget::DockWidgetFeatures features() const;

View File

@ -269,7 +269,7 @@ public:
CDockSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = nullptr) CDockSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = nullptr)
{ {
CDockSplitter* s = new CDockSplitter(orientation, parent); CDockSplitter* s = new CDockSplitter(orientation, parent);
s->setOpaqueResize(DockManager->configFlags().testFlag(CDockManager::OpaqueSplitterResize)); s->setOpaqueResize(CDockManager::configFlags().testFlag(CDockManager::OpaqueSplitterResize));
s->setChildrenCollapsible(false); s->setChildrenCollapsible(false);
return s; return s;
} }
@ -557,8 +557,8 @@ void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidge
s.writeStartElement("Splitter"); s.writeStartElement("Splitter");
s.writeAttribute("Orientation", (Splitter->orientation() == Qt::Horizontal) ? "-" : "|"); s.writeAttribute("Orientation", (Splitter->orientation() == Qt::Horizontal) ? "-" : "|");
s.writeAttribute("Count", QString::number(Splitter->count())); s.writeAttribute("Count", QString::number(Splitter->count()));
qDebug() << "NodeSplitter orient: " << Splitter->orientation() ADS_PRINT("NodeSplitter orient: " << Splitter->orientation()
<< " WidgetCont: " << Splitter->count(); << " WidgetCont: " << Splitter->count());
for (int i = 0; i < Splitter->count(); ++i) for (int i = 0; i < Splitter->count(); ++i)
{ {
saveChildNodesState(s, Splitter->widget(i)); saveChildNodesState(s, Splitter->widget(i));
@ -608,8 +608,8 @@ bool DockContainerWidgetPrivate::restoreSplitter(QXmlStreamReader& s,
{ {
return false; return false;
} }
qDebug() << "Restore NodeSplitter Orientation: " << Orientation << ADS_PRINT("Restore NodeSplitter Orientation: " << Orientation <<
" WidgetCount: " << WidgetCount; " WidgetCount: " << WidgetCount);
QSplitter* Splitter = nullptr; QSplitter* Splitter = nullptr;
if (!Testing) if (!Testing)
{ {
@ -632,7 +632,7 @@ bool DockContainerWidgetPrivate::restoreSplitter(QXmlStreamReader& s,
else if (s.name() == "Sizes") else if (s.name() == "Sizes")
{ {
QString sSizes = s.readElementText().trimmed(); QString sSizes = s.readElementText().trimmed();
qDebug() << "Sizes: " << sSizes; ADS_PRINT("Sizes: " << sSizes);
QTextStream TextStream(&sSizes); QTextStream TextStream(&sSizes);
while (!TextStream.atEnd()) while (!TextStream.atEnd())
{ {
@ -656,8 +656,8 @@ bool DockContainerWidgetPrivate::restoreSplitter(QXmlStreamReader& s,
continue; continue;
} }
qDebug() << "ChildNode isVisible " << ChildNode->isVisible() ADS_PRINT("ChildNode isVisible " << ChildNode->isVisible()
<< " isVisibleTo " << ChildNode->isVisibleTo(Splitter); << " isVisibleTo " << ChildNode->isVisibleTo(Splitter));
Splitter->addWidget(ChildNode); Splitter->addWidget(ChildNode);
Visible |= ChildNode->isVisibleTo(Splitter); Visible |= ChildNode->isVisibleTo(Splitter);
} }
@ -703,8 +703,8 @@ bool DockContainerWidgetPrivate::restoreDockArea(QXmlStreamReader& s,
QString CurrentDockWidget = s.attributes().value("Current").toString(); QString CurrentDockWidget = s.attributes().value("Current").toString();
qDebug() << "Restore NodeDockArea Tabs: " << Tabs << " Current: " ADS_PRINT("Restore NodeDockArea Tabs: " << Tabs << " Current: "
<< CurrentDockWidget; << CurrentDockWidget);
CDockAreaWidget* DockArea = nullptr; CDockAreaWidget* DockArea = nullptr;
if (!Testing) if (!Testing)
@ -738,7 +738,7 @@ bool DockContainerWidgetPrivate::restoreDockArea(QXmlStreamReader& s,
continue; continue;
} }
qDebug() << "Dock Widget found - parent " << DockWidget->parent(); ADS_PRINT("Dock Widget found - parent " << DockWidget->parent());
// We hide the DockArea here to prevent the short display (the flashing) // We hide the DockArea here to prevent the short display (the flashing)
// of the dock areas during application startup // of the dock areas during application startup
DockArea->hide(); DockArea->hide();
@ -780,17 +780,17 @@ bool DockContainerWidgetPrivate::restoreChildNodes(QXmlStreamReader& s,
if (s.name() == "Splitter") if (s.name() == "Splitter")
{ {
Result = restoreSplitter(s, CreatedWidget, Testing); Result = restoreSplitter(s, CreatedWidget, Testing);
qDebug() << "Splitter"; ADS_PRINT("Splitter");
} }
else if (s.name() == "Area") else if (s.name() == "Area")
{ {
Result = restoreDockArea(s, CreatedWidget, Testing); Result = restoreDockArea(s, CreatedWidget, Testing);
qDebug() << "DockAreaWidget"; ADS_PRINT("DockAreaWidget");
} }
else else
{ {
s.skipCurrentElement(); s.skipCurrentElement();
qDebug() << "Unknown element"; ADS_PRINT("Unknown element");
} }
} }
@ -862,15 +862,17 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
buf.fill(' ', level * 4); buf.fill(' ', level * 4);
if (Splitter) if (Splitter)
{ {
#ifdef ADS_DEBUG_PRINT
qDebug("%sSplitter %s v: %s c: %s", qDebug("%sSplitter %s v: %s c: %s",
(const char*)buf, (const char*)buf,
(Splitter->orientation() == Qt::Vertical) ? "--" : "|", (Splitter->orientation() == Qt::Vertical) ? "--" : "|",
Splitter->isHidden() ? " " : "v", Splitter->isHidden() ? " " : "v",
QString::number(Splitter->count()).toStdString().c_str()); QString::number(Splitter->count()).toStdString().c_str());
std::cout << (const char*)buf << "Splitter " std::cout << (const char*)buf << "Splitter "
<< ((Splitter->orientation() == Qt::Vertical) ? "--" : "|") << " " << ((Splitter->orientation() == Qt::Vertical) ? "--" : "|") << " "
<< (Splitter->isHidden() ? " " : "v") << " " << (Splitter->isHidden() ? " " : "v") << " "
<< QString::number(Splitter->count()).toStdString() << std::endl; << QString::number(Splitter->count()).toStdString() << std::endl;
#endif
for (int i = 0; i < Splitter->count(); ++i) for (int i = 0; i < Splitter->count(); ++i)
{ {
dumpRecursive(level + 1, Splitter->widget(i)); dumpRecursive(level + 1, Splitter->widget(i));
@ -883,6 +885,7 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
{ {
return; return;
} }
#ifdef ADS_DEBUG_PRINT
qDebug("%sDockArea", (const char*)buf); qDebug("%sDockArea", (const char*)buf);
std::cout << (const char*)buf std::cout << (const char*)buf
<< (DockArea->isHidden() ? " " : "v") << (DockArea->isHidden() ? " " : "v")
@ -896,7 +899,8 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
std::cout << (DockWidget->isHidden() ? " " : "v"); std::cout << (DockWidget->isHidden() ? " " : "v");
std::cout << (DockWidget->isClosed() ? "c" : " ") << " "; std::cout << (DockWidget->isClosed() ? "c" : " ") << " ";
std::cout << DockWidget->windowTitle().toStdString() << std::endl; std::cout << DockWidget->windowTitle().toStdString() << std::endl;
} }
#endif
} }
#else #else
Q_UNUSED(level); Q_UNUSED(level);
@ -923,12 +927,12 @@ CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetAr
int index = TargetAreaSplitter ->indexOf(TargetDockArea); int index = TargetAreaSplitter ->indexOf(TargetDockArea);
if (TargetAreaSplitter->orientation() == InsertParam.orientation()) if (TargetAreaSplitter->orientation() == InsertParam.orientation())
{ {
qDebug() << "TargetAreaSplitter->orientation() == InsertParam.orientation()"; ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()");
TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea); TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea);
} }
else else
{ {
qDebug() << "TargetAreaSplitter->orientation() != InsertParam.orientation()"; ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()");
QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
NewSplitter->addWidget(TargetDockArea); NewSplitter->addWidget(TargetDockArea);
insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
@ -998,6 +1002,15 @@ CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockW
} }
} }
//============================================================================
void CDockContainerWidget::removeDockWidget(CDockWidget* Dockwidget)
{
CDockAreaWidget* Area = Dockwidget->dockAreaWidget();
if (Area)
{
Area->removeDockWidget(Dockwidget);
}
}
//============================================================================ //============================================================================
unsigned int CDockContainerWidget::zOrderIndex() const unsigned int CDockContainerWidget::zOrderIndex() const
@ -1047,7 +1060,7 @@ void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget,
//============================================================================ //============================================================================
void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
{ {
qDebug() << "CDockContainerWidget::removeDockArea"; ADS_PRINT("CDockContainerWidget::removeDockArea");
area->disconnect(this); area->disconnect(this);
d->DockAreas.removeAll(area); d->DockAreas.removeAll(area);
CDockSplitter* Splitter = internal::findParent<CDockSplitter*>(area); CDockSplitter* Splitter = internal::findParent<CDockSplitter*>(area);
@ -1057,6 +1070,12 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
area->setParent(nullptr); area->setParent(nullptr);
internal::hideEmptyParentSplitters(Splitter); internal::hideEmptyParentSplitters(Splitter);
// Remove this area from cached areas
const auto& cache = d->LastAddedAreaCache;
if (auto p = std::find(cache, cache+sizeof(cache)/sizeof(cache[0]), area)) {
d->LastAddedAreaCache[std::distance(cache, p)] = nullptr;
}
// If splitter has more than 1 widgets, we are finished and can leave // If splitter has more than 1 widgets, we are finished and can leave
if (Splitter->count() > 1) if (Splitter->count() > 1)
{ {
@ -1067,7 +1086,7 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
// avoid too many empty splitters // avoid too many empty splitters
if (Splitter == d->RootSplitter) if (Splitter == d->RootSplitter)
{ {
qDebug() << "Removed from RootSplitter"; ADS_PRINT("Removed from RootSplitter");
// If splitter is empty, we are finished // If splitter is empty, we are finished
if (!Splitter->count()) if (!Splitter->count())
{ {
@ -1089,11 +1108,11 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
QLayoutItem* li = d->Layout->replaceWidget(Splitter, ChildSplitter); QLayoutItem* li = d->Layout->replaceWidget(Splitter, ChildSplitter);
d->RootSplitter = ChildSplitter; d->RootSplitter = ChildSplitter;
delete li; delete li;
qDebug() << "RootSplitter replaced by child splitter"; ADS_PRINT("RootSplitter replaced by child splitter");
} }
else if (Splitter->count() == 1) else if (Splitter->count() == 1)
{ {
qDebug() << "Replacing splitter with content"; ADS_PRINT("Replacing splitter with content");
QSplitter* ParentSplitter = internal::findParent<QSplitter*>(Splitter); QSplitter* ParentSplitter = internal::findParent<QSplitter*>(Splitter);
auto Sizes = ParentSplitter->sizes(); auto Sizes = ParentSplitter->sizes();
QWidget* widget = Splitter->widget(0); QWidget* widget = Splitter->widget(0);
@ -1172,7 +1191,7 @@ int CDockContainerWidget::visibleDockAreaCount() const
void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget, void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget,
const QPoint& TargetPos) const QPoint& TargetPos)
{ {
qDebug() << "CDockContainerWidget::dropFloatingWidget"; ADS_PRINT("CDockContainerWidget::dropFloatingWidget");
CDockAreaWidget* DockArea = dockAreaAt(TargetPos); CDockAreaWidget* DockArea = dockAreaAt(TargetPos);
auto dropArea = InvalidDockWidgetArea; auto dropArea = InvalidDockWidgetArea;
auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor();
@ -1192,7 +1211,7 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
if (dropArea != InvalidDockWidgetArea) if (dropArea != InvalidDockWidgetArea)
{ {
qDebug() << "Dock Area Drop Content: " << dropArea; ADS_PRINT("Dock Area Drop Content: " << dropArea);
d->dropIntoSection(FloatingWidget, DockArea, dropArea); d->dropIntoSection(FloatingWidget, DockArea, dropArea);
} }
} }
@ -1201,7 +1220,7 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
if (InvalidDockWidgetArea == dropArea) if (InvalidDockWidgetArea == dropArea)
{ {
dropArea = ContainerDropArea; dropArea = ContainerDropArea;
qDebug() << "Container Drop Content: " << dropArea; ADS_PRINT("Container Drop Content: " << dropArea);
if (dropArea != InvalidDockWidgetArea) if (dropArea != InvalidDockWidgetArea)
{ {
d->dropIntoContainer(FloatingWidget, dropArea); d->dropIntoContainer(FloatingWidget, dropArea);
@ -1243,8 +1262,8 @@ QList<CDockAreaWidget*> CDockContainerWidget::openedDockAreas() const
//============================================================================ //============================================================================
void CDockContainerWidget::saveState(QXmlStreamWriter& s) const void CDockContainerWidget::saveState(QXmlStreamWriter& s) const
{ {
qDebug() << "CDockContainerWidget::saveState isFloating " ADS_PRINT("CDockContainerWidget::saveState isFloating "
<< isFloating(); << isFloating());
s.writeStartElement("Container"); s.writeStartElement("Container");
s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0)); s.writeAttribute("Floating", QString::number(isFloating() ? 1 : 0));
@ -1267,18 +1286,19 @@ void CDockContainerWidget::saveState(QXmlStreamWriter& s) const
bool CDockContainerWidget::restoreState(QXmlStreamReader& s, bool Testing) bool CDockContainerWidget::restoreState(QXmlStreamReader& s, bool Testing)
{ {
bool IsFloating = s.attributes().value("Floating").toInt(); bool IsFloating = s.attributes().value("Floating").toInt();
qDebug() << "Restore CDockContainerWidget Floating" << IsFloating; ADS_PRINT("Restore CDockContainerWidget Floating" << IsFloating);
QWidget*NewRootSplitter {}; QWidget*NewRootSplitter {};
if (!Testing) if (!Testing)
{ {
d->VisibleDockAreaCount = -1;// invalidate the dock area count d->VisibleDockAreaCount = -1;// invalidate the dock area count
d->DockAreas.clear(); d->DockAreas.clear();
std::fill(std::begin(d->LastAddedAreaCache),std::end(d->LastAddedAreaCache), nullptr);
} }
if (IsFloating) if (IsFloating)
{ {
qDebug() << "Restore floating widget"; ADS_PRINT("Restore floating widget");
if (!s.readNextStartElement() || s.name() != "Geometry") if (!s.readNextStartElement() || s.name() != "Geometry")
{ {
return false; return false;

View File

@ -50,7 +50,10 @@ struct FloatingDockContainerPrivate;
/** /**
* Container that manages a number of dock areas with single dock widgets * Container that manages a number of dock areas with single dock widgets
* or tabyfied dock widgets in each area * or tabyfied dock widgets in each area.
* Each window that support docking has a DockContainerWidget. That means
* the main application window and all floating windows are ore contain
* an DockContainerWidget.
*/ */
class ADS_EXPORT CDockContainerWidget : public QFrame class ADS_EXPORT CDockContainerWidget : public QFrame
{ {
@ -166,6 +169,11 @@ public:
CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget, CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* DockAreaWidget = nullptr); CDockAreaWidget* DockAreaWidget = nullptr);
/**
* Removes dockwidget
*/
void removeDockWidget(CDockWidget* Dockwidget);
/** /**
* Returns the current zOrderIndex * Returns the current zOrderIndex
*/ */

View File

@ -56,6 +56,8 @@
namespace ads namespace ads
{ {
static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultConfig;
/** /**
* Private data class of CDockManager class (pimpl) * Private data class of CDockManager class (pimpl)
*/ */
@ -72,7 +74,6 @@ struct DockManagerPrivate
QMenu* ViewMenu; QMenu* ViewMenu;
CDockManager::eViewMenuInsertionOrder MenuInsertionOrder = CDockManager::MenuAlphabeticallySorted; CDockManager::eViewMenuInsertionOrder MenuInsertionOrder = CDockManager::MenuAlphabeticallySorted;
bool RestoringState = false; bool RestoringState = false;
CDockManager::ConfigFlags ConfigFlags = CDockManager::DefaultConfig;
/** /**
* Private data constructor * Private data constructor
@ -145,7 +146,11 @@ DockManagerPrivate::DockManagerPrivate(CDockManager* _public) :
void DockManagerPrivate::loadStylesheet() void DockManagerPrivate::loadStylesheet()
{ {
QString Result; QString Result;
#ifdef Q_OS_LINUX
QFile StyleSheetFile(":ads/stylesheets/default_linux.css");
#else
QFile StyleSheetFile(":ads/stylesheets/default.css"); QFile StyleSheetFile(":ads/stylesheets/default.css");
#endif
StyleSheetFile.open(QIODevice::ReadOnly); StyleSheetFile.open(QIODevice::ReadOnly);
QTextStream StyleSheetStream(&StyleSheetFile); QTextStream StyleSheetStream(&StyleSheetFile);
Result = StyleSheetStream.readAll(); Result = StyleSheetStream.readAll();
@ -170,7 +175,7 @@ bool DockManagerPrivate::restoreContainer(int Index, QXmlStreamReader& stream, b
} }
else else
{ {
qDebug() << "d->Containers[i]->restoreState "; ADS_PRINT("d->Containers[i]->restoreState ");
auto Container = Containers[Index]; auto Container = Containers[Index];
if (Container->isFloating()) if (Container->isFloating())
{ {
@ -207,7 +212,7 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versi
{ {
return false; return false;
} }
qDebug() << s.attributes().value("Version"); ADS_PRINT(s.attributes().value("Version"));
bool ok; bool ok;
int v = s.attributes().value("Version").toInt(&ok); int v = s.attributes().value("Version").toInt(&ok);
if (!ok || v != version) if (!ok || v != version)
@ -217,7 +222,7 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versi
bool Result = true; bool Result = true;
int DockContainers = s.attributes().value("Containers").toInt(); int DockContainers = s.attributes().value("Containers").toInt();
qDebug() << DockContainers; ADS_PRINT(DockContainers);
int DockContainerCount = 0; int DockContainerCount = 0;
while (s.readNextStartElement()) while (s.readNextStartElement())
{ {
@ -336,11 +341,12 @@ void DockManagerPrivate::emitTopLevelEvents()
//============================================================================ //============================================================================
bool DockManagerPrivate::restoreState(const QByteArray &state, int version) bool DockManagerPrivate::restoreState(const QByteArray& State, int version)
{ {
QByteArray state = State.startsWith("<?xml") ? State : qUncompress(State);
if (!checkFormat(state, version)) if (!checkFormat(state, version))
{ {
qDebug() << "checkFormat: Error checking format!!!!!!!"; ADS_PRINT("checkFormat: Error checking format!!!!!!!");
return false; return false;
} }
@ -350,7 +356,7 @@ bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
if (!restoreStateFromXml(state, version)) if (!restoreStateFromXml(state, version))
{ {
qDebug() << "restoreState: Error restoring state!!!!!!!"; ADS_PRINT("restoreState: Error restoring state!!!!!!!");
return false; return false;
} }
@ -425,7 +431,7 @@ CDockManager::~CDockManager()
void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget) void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget)
{ {
d->FloatingWidgets.append(FloatingWidget); d->FloatingWidgets.append(FloatingWidget);
qDebug() << "d->FloatingWidgets.count() " << d->FloatingWidgets.count(); ADS_PRINT("d->FloatingWidgets.count() " << d->FloatingWidgets.count());
} }
@ -489,11 +495,12 @@ unsigned int CDockManager::zOrderIndex() const
//============================================================================ //============================================================================
QByteArray CDockManager::saveState(eXmlMode XmlMode, int version) const QByteArray CDockManager::saveState(int version) const
{ {
QByteArray xmldata; QByteArray xmldata;
QXmlStreamWriter s(&xmldata); QXmlStreamWriter s(&xmldata);
s.setAutoFormatting(XmlAutoFormattingEnabled == XmlMode); auto ConfigFlags = CDockManager::configFlags();
s.setAutoFormatting(ConfigFlags.testFlag(XmlAutoFormattingEnabled));
s.writeStartDocument(); s.writeStartDocument();
s.writeStartElement("QtAdvancedDockingSystem"); s.writeStartElement("QtAdvancedDockingSystem");
s.writeAttribute("Version", QString::number(version)); s.writeAttribute("Version", QString::number(version));
@ -506,7 +513,8 @@ QByteArray CDockManager::saveState(eXmlMode XmlMode, int version) const
s.writeEndElement(); s.writeEndElement();
s.writeEndDocument(); s.writeEndDocument();
return xmldata; return ConfigFlags.testFlag(XmlCompressionEnabled)
? qCompress(xmldata, 9) : xmldata;
} }
@ -565,9 +573,13 @@ CDockAreaWidget* CDockManager::addDockWidgetTab(DockWidgetArea area,
{ {
return addDockWidget(ads::CenterDockWidgetArea, Dockwidget, AreaWidget); return addDockWidget(ads::CenterDockWidgetArea, Dockwidget, AreaWidget);
} }
else if (!openedDockAreas().isEmpty())
{
return addDockWidget(area, Dockwidget, openedDockAreas().last());
}
else else
{ {
return addDockWidget(area, Dockwidget, AreaWidget); return addDockWidget(area, Dockwidget, nullptr);
} }
} }
@ -586,6 +598,12 @@ CDockWidget* CDockManager::findDockWidget(const QString& ObjectName) const
return d->DockWidgetsMap.value(ObjectName, nullptr); return d->DockWidgetsMap.value(ObjectName, nullptr);
} }
//============================================================================
void CDockManager::removeDockWidget(CDockWidget* Dockwidget)
{
d->DockWidgetsMap.remove(Dockwidget->objectName());
CDockContainerWidget::removeDockWidget(Dockwidget);
}
//============================================================================ //============================================================================
QMap<QString, CDockWidget*> CDockManager::dockWidgetsMap() const QMap<QString, CDockWidget*> CDockManager::dockWidgetsMap() const
@ -747,16 +765,23 @@ int CDockManager::startDragDistance()
//=========================================================================== //===========================================================================
CDockManager::ConfigFlags CDockManager::configFlags() const CDockManager::ConfigFlags CDockManager::configFlags()
{ {
return d->ConfigFlags; return StaticConfigFlags;
} }
//=========================================================================== //===========================================================================
void CDockManager::setConfigFlags(const ConfigFlags Flags) void CDockManager::setConfigFlags(const ConfigFlags Flags)
{ {
d->ConfigFlags = Flags; StaticConfigFlags = Flags;
}
//===========================================================================
void CDockManager::setConfigFlag(eConfigFlag Flag, bool On)
{
internal::setFlag(StaticConfigFlags, Flag, On);
} }

View File

@ -51,7 +51,16 @@ struct DockWidgetTabPrivate;
struct DockAreaWidgetPrivate; struct DockAreaWidgetPrivate;
/** /**
* The central dock manager that maintains the complete docking system * The central dock manager that maintains the complete docking system.
* With the configuration flags you can globally control the functionality
* of the docking system. The dock manager uses an internal stylesheet to
* style its components like splitters, tabs and buttons. If you want to
* disable this stylesheet because your application uses its own,
* just call the function for settings the stylesheet with an empty
* string.
* \code
* DockManager->setStyleSheet("");
* \endcode
**/ **/
class ADS_EXPORT CDockManager : public CDockContainerWidget class ADS_EXPORT CDockManager : public CDockContainerWidget
{ {
@ -108,23 +117,22 @@ public:
MenuAlphabeticallySorted MenuAlphabeticallySorted
}; };
enum eXmlMode
{
XmlAutoFormattingDisabled,
XmlAutoFormattingEnabled
};
/** /**
* These global configuration flags configure some global dock manager * These global configuration flags configure some global dock manager
* settings. * settings.
*/ */
enum eConfigFlag enum eConfigFlag
{ {
ActiveTabHasCloseButton = 0x01, //!< If this flag is set, the active tab in a tab area has a close button ActiveTabHasCloseButton = 0x0001, //!< If this flag is set, the active tab in a tab area has a close button
DockAreaHasCloseButton = 0x02, //!< If the flag is set each dock area has a close button DockAreaHasCloseButton = 0x0002, //!< If the flag is set each dock area has a close button
DockAreaCloseButtonClosesTab = 0x04,//!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete cock area DockAreaCloseButtonClosesTab = 0x0004,//!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete cock area
OpaqueSplitterResize = 0x08, //!< See QSplitter::setOpaqueResize() documentation OpaqueSplitterResize = 0x0008, //!< See QSplitter::setOpaqueResize() documentation
DefaultConfig = ActiveTabHasCloseButton | DockAreaHasCloseButton | OpaqueSplitterResize, ///< the default configuration XmlAutoFormattingEnabled = 0x0010,//!< If enabled, the XML writer automatically adds line-breaks and indentation to empty sections between elements (ignorable whitespace).
XmlCompressionEnabled = 0x0020,//!< If enabled, the XML output will be compressed and is not human readable anymore
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
DefaultConfig = ActiveTabHasCloseButton | DockAreaHasCloseButton | OpaqueSplitterResize | XmlCompressionEnabled, ///< the default configuration
}; };
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag) Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
@ -133,7 +141,7 @@ public:
* If the given parent is a QMainWindow, the dock manager sets itself as the * If the given parent is a QMainWindow, the dock manager sets itself as the
* central widget. * central widget.
* Before you create any dock widgets, you should properly setup the * Before you create any dock widgets, you should properly setup the
* configuration flags via setConfigFlags() * configuration flags via setConfigFlags().
*/ */
CDockManager(QWidget* parent = 0); CDockManager(QWidget* parent = 0);
@ -145,13 +153,18 @@ public:
/** /**
* This function returns the global configuration flags * This function returns the global configuration flags
*/ */
ConfigFlags configFlags() const; static ConfigFlags configFlags();
/** /**
* Sets the global configuration flags for the whole docking system. * Sets the global configuration flags for the whole docking system.
* Call this function before you create your first dock widget. * Call this function before you create your first dock widget.
*/ */
void setConfigFlags(const ConfigFlags Flags); static void setConfigFlags(const ConfigFlags Flags);
/**
* Set a certain config flag
*/
static void setConfigFlag(eConfigFlag Flag, bool On = true);
/** /**
* Adds dockwidget into the given area. * Adds dockwidget into the given area.
@ -192,6 +205,11 @@ public:
*/ */
CDockWidget* findDockWidget(const QString& ObjectName) const; CDockWidget* findDockWidget(const QString& ObjectName) const;
/**
* Remove the given Dock from the dock manager
*/
void removeDockWidget(CDockWidget* Dockwidget);
/** /**
* This function returns a readable reference to the internal dock * This function returns a readable reference to the internal dock
* widgets map so that it is possible to iterate over all dock widgets * widgets map so that it is possible to iterate over all dock widgets
@ -223,7 +241,7 @@ public:
* The XmlMode XmlAutoFormattingDisabled is better if you would like to have * The XmlMode XmlAutoFormattingDisabled is better if you would like to have
* a more compact XML output - i.e. for storage in ini files. * a more compact XML output - i.e. for storage in ini files.
*/ */
QByteArray saveState(eXmlMode XmlMode = XmlAutoFormattingDisabled, int version = 0) const; QByteArray saveState(int version = 0) const;
/** /**
* Restores the state of this dockmanagers dockwidgets. * Restores the state of this dockmanagers dockwidgets.

View File

@ -132,6 +132,21 @@ struct DockOverlayCrossPrivate
return Color; return Color;
} }
//============================================================================
/**
* Helper function that returns the drop indicator width depending on the
* operating system
*/
qreal dropIndicatiorWidth(QLabel* l) const
{
#ifdef Q_OS_LINUX
Q_UNUSED(l)
return 40;
#else
return static_cast<qreal>(l->fontMetrics().height()) * 3.f;
#endif
}
//============================================================================ //============================================================================
QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea, QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea,
@ -140,7 +155,7 @@ struct DockOverlayCrossPrivate
QLabel* l = new QLabel(); QLabel* l = new QLabel();
l->setObjectName("DockWidgetAreaLabel"); l->setObjectName("DockWidgetAreaLabel");
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 3.f; const qreal metric = dropIndicatiorWidth(l);
const QSizeF size(metric, metric); const QSizeF size(metric, metric);
l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode)); l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode));
@ -154,7 +169,7 @@ struct DockOverlayCrossPrivate
void updateDropIndicatorIcon(QWidget* DropIndicatorWidget) void updateDropIndicatorIcon(QWidget* DropIndicatorWidget)
{ {
QLabel* l = qobject_cast<QLabel*>(DropIndicatorWidget); QLabel* l = qobject_cast<QLabel*>(DropIndicatorWidget);
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 3.f; const qreal metric = dropIndicatiorWidth(l);
const QSizeF size(metric, metric); const QSizeF size(metric, metric);
int Area = l->property("dockWidgetArea").toInt(); int Area = l->property("dockWidgetArea").toInt();

View File

@ -245,9 +245,6 @@ public:
protected: protected:
virtual void showEvent(QShowEvent* e) override; virtual void showEvent(QShowEvent* e) override;
void setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets); void setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets);
private:
}; // CDockOverlayCross }; // CDockOverlayCross
} // namespace ads } // namespace ads

View File

@ -68,7 +68,7 @@ CDockSplitter::CDockSplitter(Qt::Orientation orientation, QWidget *parent)
//============================================================================ //============================================================================
CDockSplitter::~CDockSplitter() CDockSplitter::~CDockSplitter()
{ {
qDebug() << "~CDockSplitter"; ADS_PRINT("~CDockSplitter");
delete d; delete d;
} }

View File

@ -38,7 +38,8 @@ namespace ads
struct DockSplitterPrivate; struct DockSplitterPrivate;
/** /**
* Splitter used internally instead of QSplitter * Splitter used internally instead of QSplitter with some additional
* fuctionality.
*/ */
class ADS_EXPORT CDockSplitter : public QSplitter class ADS_EXPORT CDockSplitter : public QSplitter
{ {

View File

@ -214,7 +214,7 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
setObjectName(title); setObjectName(title);
d->TabWidget = new CDockWidgetTab(this); d->TabWidget = new CDockWidgetTab(this);
d->ToggleViewAction = new QAction(title, nullptr); d->ToggleViewAction = new QAction(title, this);
d->ToggleViewAction->setCheckable(true); d->ToggleViewAction->setCheckable(true);
connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this, connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this,
SLOT(toggleView(bool))); SLOT(toggleView(bool)));
@ -224,7 +224,7 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
//============================================================================ //============================================================================
CDockWidget::~CDockWidget() CDockWidget::~CDockWidget()
{ {
qDebug() << "~CDockWidget()"; ADS_PRINT("~CDockWidget()");
delete d; delete d;
} }
@ -243,21 +243,35 @@ void CDockWidget::setToggleViewActionChecked(bool Checked)
void CDockWidget::setWidget(QWidget* widget, eInsertMode InsertMode) void CDockWidget::setWidget(QWidget* widget, eInsertMode InsertMode)
{ {
QScrollArea* ScrollAreaWidget = qobject_cast<QScrollArea*>(widget); QScrollArea* ScrollAreaWidget = qobject_cast<QScrollArea*>(widget);
if (ScrollAreaWidget || ForceNoScrollArea != InsertMode) if (ScrollAreaWidget || ForceNoScrollArea == InsertMode)
{
d->Layout->addWidget(widget);
if (ScrollAreaWidget && ScrollAreaWidget->viewport())
{
ScrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
}
}
else
{ {
d->setupScrollArea(); d->setupScrollArea();
d->ScrollArea->setWidget(widget); d->ScrollArea->setWidget(widget);
} }
else
{
d->Layout->addWidget(widget);
}
d->Widget = widget; d->Widget = widget;
d->Widget->setProperty("dockWidgetContent", true); d->Widget->setProperty("dockWidgetContent", true);
} }
//============================================================================
QWidget* CDockWidget::takeWidget()
{
d->ScrollArea->takeWidget();
d->Layout->removeWidget(d->Widget);
d->Widget->setParent(nullptr);
return d->Widget;
}
//============================================================================ //============================================================================
QWidget* CDockWidget::widget() const QWidget* CDockWidget::widget() const
{ {
@ -275,25 +289,21 @@ CDockWidgetTab* CDockWidget::tabWidget() const
//============================================================================ //============================================================================
void CDockWidget::setFeatures(DockWidgetFeatures features) void CDockWidget::setFeatures(DockWidgetFeatures features)
{ {
if (d->Features == features)
{
return;
}
d->Features = features; d->Features = features;
d->TabWidget->onDockWidgetFeaturesChanged();
} }
//============================================================================ //============================================================================
void CDockWidget::setFeature(DockWidgetFeature flag, bool on) void CDockWidget::setFeature(DockWidgetFeature flag, bool on)
{ {
#if QT_VERSION >= 0x050700 auto Features = features();
d->Features.setFlag(flag, on); internal::setFlag(Features, flag, on);
#else setFeatures(Features);
if(on)
{
d->Features |= flag;
}
else
{
d->Features &= ~flag;
}
#endif
} }

View File

@ -141,7 +141,7 @@ public:
enum DockWidgetFeature enum DockWidgetFeature
{ {
DockWidgetClosable = 0x01, DockWidgetClosable = 0x01,
DockWidgetMovable = 0x02, DockWidgetMovable = 0x02,///< this feature is not properly implemented yet and is ignored
DockWidgetFloatable = 0x04, DockWidgetFloatable = 0x04,
AllDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable, AllDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable,
NoDockWidgetFeatures = 0x00 NoDockWidgetFeatures = 0x00
@ -238,6 +238,11 @@ public:
*/ */
void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea); void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea);
/**
* Remove the widget from the dock and give ownership back to the caller
*/
QWidget* takeWidget();
/** /**
* Returns the widget for the dock widget. This function returns zero if * Returns the widget for the dock widget. This function returns zero if
* the widget has not been set. * the widget has not been set.

View File

@ -55,7 +55,6 @@ namespace ads
{ {
using tTabLabel = CElidingLabel; using tTabLabel = CElidingLabel;
using tCloseButton = QPushButton;
/** /**
* Private data class of CDockWidgetTab class (pimpl) * Private data class of CDockWidgetTab class (pimpl)
@ -72,7 +71,7 @@ struct DockWidgetTabPrivate
eDragState DragState = DraggingInactive; eDragState DragState = DraggingInactive;
CFloatingDockContainer* FloatingWidget = nullptr; CFloatingDockContainer* FloatingWidget = nullptr;
QIcon Icon; QIcon Icon;
tCloseButton* CloseButton = nullptr; QAbstractButton* CloseButton = nullptr;
QSpacerItem* IconTextSpacer; QSpacerItem* IconTextSpacer;
/** /**
@ -120,7 +119,24 @@ struct DockWidgetTabPrivate
*/ */
bool testConfigFlag(CDockManager::eConfigFlag Flag) const bool testConfigFlag(CDockManager::eConfigFlag Flag) const
{ {
return DockArea->dockManager()->configFlags().testFlag(Flag); return CDockManager::configFlags().testFlag(Flag);
}
/**
* Creates the close button as QPushButton or as QToolButton
*/
QAbstractButton* createCloseButton() const
{
if (testConfigFlag(CDockManager::TabCloseButtonIsToolButton))
{
auto Button = new QToolButton();
Button->setAutoRaise(true);
return Button;
}
else
{
return new QPushButton();
}
} }
}; };
// struct DockWidgetTabPrivate // struct DockWidgetTabPrivate
@ -143,19 +159,19 @@ void DockWidgetTabPrivate::createLayout()
TitleLabel->setObjectName("dockWidgetTabLabel"); TitleLabel->setObjectName("dockWidgetTabLabel");
TitleLabel->setAlignment(Qt::AlignCenter); TitleLabel->setAlignment(Qt::AlignCenter);
CloseButton = new tCloseButton(); CloseButton = createCloseButton();
CloseButton->setObjectName("tabCloseButton"); CloseButton->setObjectName("tabCloseButton");
// The standard icons do does not look good on high DPI screens // The standard icons do does not look good on high DPI screens
QIcon CloseIcon = _this->style()->standardIcon(QStyle::SP_TitleBarCloseButton); QIcon CloseIcon;
QPixmap normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton); QPixmap normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton);
QPixmap disabledPixmap = internal::createTransparentPixmap(normalPixmap, 0.25); CloseIcon.addPixmap(normalPixmap, QIcon::Normal);
CloseIcon.addPixmap(disabledPixmap, QIcon::Disabled); CloseIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
CloseButton->setIcon(CloseIcon); CloseButton->setIcon(CloseIcon);
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
CloseButton->setVisible(false); _this->onDockWidgetFeaturesChanged();
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
CloseButton->setToolTip(QObject::tr("Close Tab")); CloseButton->setToolTip(QObject::tr("Close Tab"));
#endif #endif
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested())); _this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
QFontMetrics fm(TitleLabel->font()); QFontMetrics fm(TitleLabel->font());
@ -192,9 +208,9 @@ void DockWidgetTabPrivate::moveTab(QMouseEvent* ev)
bool DockWidgetTabPrivate::startFloating(eDragState DraggingState) bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
{ {
auto dockContainer = DockWidget->dockContainer(); auto dockContainer = DockWidget->dockContainer();
qDebug() << "isFloating " << dockContainer->isFloating(); ADS_PRINT("isFloating " << dockContainer->isFloating());
qDebug() << "areaCount " << dockContainer->dockAreaCount(); ADS_PRINT("areaCount " << dockContainer->dockAreaCount());
qDebug() << "widgetCount " << DockWidget->dockAreaWidget()->dockWidgetsCount(); ADS_PRINT("widgetCount " << DockWidget->dockAreaWidget()->dockWidgetsCount());
// if this is the last dock widget inside of this floating widget, // if this is the last dock widget inside of this floating widget,
// then it does not make any sense, to make it floating because // then it does not make any sense, to make it floating because
// it is already floating // it is already floating
@ -205,7 +221,7 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
return false; return false;
} }
qDebug() << "startFloating"; ADS_PRINT("startFloating");
DragState = DraggingState; DragState = DraggingState;
QSize Size = DockArea->size(); QSize Size = DockArea->size();
CFloatingDockContainer* FloatingWidget = nullptr; CFloatingDockContainer* FloatingWidget = nullptr;
@ -223,7 +239,7 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
if (DraggingFloatingWidget == DraggingState) if (DraggingFloatingWidget == DraggingState)
{ {
FloatingWidget->startDragging(DragStartMousePosition, Size); FloatingWidget->startDragging(DragStartMousePosition, Size, _this);
auto Overlay = DockWidget->dockManager()->containerOverlay(); auto Overlay = DockWidget->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas); Overlay->setAllowedAreas(OuterDockAreas);
this->FloatingWidget = FloatingWidget; this->FloatingWidget = FloatingWidget;
@ -250,7 +266,7 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) :
//============================================================================ //============================================================================
CDockWidgetTab::~CDockWidgetTab() CDockWidgetTab::~CDockWidgetTab()
{ {
qDebug() << "~CDockWidgetTab()"; ADS_PRINT("~CDockWidgetTab()");
delete d; delete d;
} }
@ -327,7 +343,7 @@ void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev)
} }
// Floating is only allowed for widgets that are movable // Floating is only allowed for widgets that are movable
if (d->DockWidget->features().testFlag(CDockWidget::DockWidgetMovable)) if (d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{ {
d->startFloating(); d->startFloating();
} }
@ -351,9 +367,10 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev)
d->DragStartMousePosition = ev->pos(); d->DragStartMousePosition = ev->pos();
QMenu Menu(this); QMenu Menu(this);
Menu.addAction(tr("Detach"), this, SLOT(onDetachActionTriggered())); auto Action = Menu.addAction(tr("Detach"), this, SLOT(onDetachActionTriggered()));
Action->setEnabled(d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable));
Menu.addSeparator(); Menu.addSeparator();
auto Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested())); Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested()));
Action->setEnabled(isClosable()); Action->setEnabled(isClosable());
Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested()));
Menu.exec(mapToGlobal(ev->pos())); Menu.exec(mapToGlobal(ev->pos()));
@ -371,8 +388,10 @@ bool CDockWidgetTab::isActiveTab() const
void CDockWidgetTab::setActiveTab(bool active) void CDockWidgetTab::setActiveTab(bool active)
{ {
bool DockWidgetClosable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable); bool DockWidgetClosable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable);
bool TabHasCloseButton = d->testConfigFlag(CDockManager::ActiveTabHasCloseButton); bool ActiveTabHasCloseButton = d->testConfigFlag(CDockManager::ActiveTabHasCloseButton);
d->CloseButton->setVisible(active && DockWidgetClosable && TabHasCloseButton); bool AllTabsHaveCloseButton = d->testConfigFlag(CDockManager::AllTabsHaveCloseButton);
bool TabHasCloseButton = (ActiveTabHasCloseButton && active) | AllTabsHaveCloseButton;
d->CloseButton->setVisible(DockWidgetClosable && TabHasCloseButton);
if (d->IsActiveTab == active) if (d->IsActiveTab == active)
{ {
return; return;
@ -442,7 +461,7 @@ void CDockWidgetTab::setIcon(const QIcon& Icon)
d->Icon = Icon; d->Icon = Icon;
if (d->IconLabel) if (d->IconLabel)
{ {
d->IconLabel->setPixmap(Icon.pixmap(this->windowHandle(), QSize(16, 16))); d->IconLabel->setPixmap(Icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this)));
d->IconLabel->setVisible(true); d->IconLabel->setVisible(true);
} }
} }
@ -468,7 +487,8 @@ void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
// If this is the last dock area in a dock container it does not make // If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one // sense to move it to a new floating widget and leave this one
// empty // empty
if (!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1) if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
&& d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{ {
d->DragStartMousePosition = event->pos(); d->DragStartMousePosition = event->pos();
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
@ -504,13 +524,15 @@ bool CDockWidgetTab::isClosable() const
//=========================================================================== //===========================================================================
void CDockWidgetTab::onDetachActionTriggered() void CDockWidgetTab::onDetachActionTriggered()
{ {
if (!d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{
return;
}
d->DragStartMousePosition = mapFromGlobal(QCursor::pos()); d->DragStartMousePosition = mapFromGlobal(QCursor::pos());
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }
//============================================================================ //============================================================================
bool CDockWidgetTab::event(QEvent *e) bool CDockWidgetTab::event(QEvent *e)
{ {
@ -525,6 +547,17 @@ bool CDockWidgetTab::event(QEvent *e)
} }
//============================================================================
void CDockWidgetTab::onDockWidgetFeaturesChanged()
{
auto Features = d->DockWidget->features();
auto SizePolicy = d->CloseButton->sizePolicy();
SizePolicy.setRetainSizeWhenHidden(Features.testFlag(CDockWidget::DockWidgetClosable)
&& d->testConfigFlag(CDockManager::RetainTabSizeWhenCloseButtonHidden));
d->CloseButton->setSizePolicy(SizePolicy);
}
} // namespace ads } // namespace ads

View File

@ -53,6 +53,8 @@ class ADS_EXPORT CDockWidgetTab : public QFrame
private: private:
DockWidgetTabPrivate* d; ///< private data (pimpl) DockWidgetTabPrivate* d; ///< private data (pimpl)
friend struct DockWidgetTabPrivate; friend struct DockWidgetTabPrivate;
friend class CDockWidget;
void onDockWidgetFeaturesChanged();
private slots: private slots:
void onDetachActionTriggered(); void onDetachActionTriggered();
@ -125,7 +127,6 @@ public:
*/ */
QString text() const; QString text() const;
/** /**
* Sets the tab text * Sets the tab text
*/ */
@ -136,16 +137,14 @@ public:
*/ */
bool isClosable() const; bool isClosable() const;
/** /**
* Track event ToolTipChange and set child ToolTip * Track event ToolTipChange and set child ToolTip
*/ */
virtual bool event(QEvent *e) override; virtual bool event(QEvent *e) override;
public slots: public slots:
virtual void setVisible(bool visible) override;
virtual void setVisible(bool visible) override;
signals: signals:
void activeTabChanged(); void activeTabChanged();

View File

@ -157,7 +157,11 @@ QSize CElidingLabel::minimumSizeHint() const
return QLabel::minimumSizeHint(); return QLabel::minimumSizeHint();
} }
const QFontMetrics &fm = fontMetrics(); const QFontMetrics &fm = fontMetrics();
QSize size(fm.width(d->Text.left(2) + ""), fm.height()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
QSize size(fm.horizontalAdvance(d->Text.left(2) + ""), fm.height());
#else
QSize size(fm.width(d->Text.left(2) + ""), fm.height());
#endif
return size; return size;
} }
@ -170,7 +174,11 @@ QSize CElidingLabel::sizeHint() const
return QLabel::sizeHint(); return QLabel::sizeHint();
} }
const QFontMetrics& fm = fontMetrics(); const QFontMetrics& fm = fontMetrics();
QSize size(fm.width(d->Text), QLabel::sizeHint().height()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
QSize size(fm.horizontalAdvance(d->Text), QLabel::sizeHint().height());
#else
QSize size(fm.width(d->Text), QLabel::sizeHint().height());
#endif
return size; return size;
} }

View File

@ -1,21 +1,20 @@
/******************************************************************************* /*******************************************************************************
** Qt Advanced Docking System ** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler ** Copyright (C) 2017 Uwe Kindler
** **
** This library is free software; you can redistribute it and/or ** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public ** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either ** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version. ** 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, ** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of ** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details. ** Lesser General Public License for more details.
** **
** You should have received a copy of the GNU Lesser General Public ** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>. ** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/ ******************************************************************************/
//============================================================================ //============================================================================
/// \file FloatingDockContainer.cpp /// \file FloatingDockContainer.cpp
@ -24,7 +23,6 @@
/// \brief Implementation of CFloatingDockContainer class /// \brief Implementation of CFloatingDockContainer class
//============================================================================ //============================================================================
//============================================================================ //============================================================================
// INCLUDES // INCLUDES
//============================================================================ //============================================================================
@ -45,7 +43,10 @@
#include "DockWidget.h" #include "DockWidget.h"
#include "DockOverlay.h" #include "DockOverlay.h"
#include <iostream> #ifdef Q_OS_LINUX
#include "linux/FloatingWidgetTitleBar.h"
#include <xcb/xcb.h>
#endif
namespace ads namespace ads
{ {
@ -55,22 +56,26 @@ static unsigned int zOrderCounter = 0;
*/ */
struct FloatingDockContainerPrivate struct FloatingDockContainerPrivate
{ {
CFloatingDockContainer* _this; CFloatingDockContainer *_this;
CDockContainerWidget* DockContainer; CDockContainerWidget *DockContainer;
unsigned int zOrderIndex = ++zOrderCounter; unsigned int zOrderIndex = ++zOrderCounter;
QPointer<CDockManager> DockManager; QPointer<CDockManager> DockManager;
eDragState DraggingState = DraggingInactive; eDragState DraggingState = DraggingInactive;
QPoint DragStartMousePosition; QPoint DragStartMousePosition;
CDockContainerWidget* DropContainer = nullptr; CDockContainerWidget *DropContainer = nullptr;
CDockAreaWidget* SingleDockArea = nullptr; CDockAreaWidget *SingleDockArea = nullptr;
#ifdef Q_OS_LINUX
QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr;
#endif
/** /**
* Private data constructor * Private data constructor
*/ */
FloatingDockContainerPrivate(CFloatingDockContainer* _public); FloatingDockContainerPrivate(CFloatingDockContainer *_public);
void titleMouseReleaseEvent(); void titleMouseReleaseEvent();
void updateDropOverlays(const QPoint& GlobalPos); void updateDropOverlays(const QPoint &GlobalPos);
/** /**
* Tests is a certain state is active * Tests is a certain state is active
@ -84,20 +89,26 @@ struct FloatingDockContainerPrivate
{ {
DraggingState = StateId; DraggingState = StateId;
} }
void setWindowTitle(const QString &Text)
{
#ifdef Q_OS_LINUX
TitleBar->setTitle(Text);
#else
_this->setWindowTitle(Text);
#endif
}
}; };
// struct FloatingDockContainerPrivate // struct FloatingDockContainerPrivate
//============================================================================ //============================================================================
FloatingDockContainerPrivate::FloatingDockContainerPrivate(CFloatingDockContainer* _public) : FloatingDockContainerPrivate::FloatingDockContainerPrivate(
CFloatingDockContainer *_public) :
_this(_public) _this(_public)
{ {
} }
//============================================================================ //============================================================================
void FloatingDockContainerPrivate::titleMouseReleaseEvent() void FloatingDockContainerPrivate::titleMouseReleaseEvent()
{ {
@ -107,25 +118,31 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
return; return;
} }
if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea if (DockManager->dockAreaOverlay()->dropAreaUnderCursor()
|| DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) != InvalidDockWidgetArea
|| DockManager->containerOverlay()->dropAreaUnderCursor()
!= InvalidDockWidgetArea)
{ {
// Resize the floating widget to the size of the highlighted drop area // Resize the floating widget to the size of the highlighted drop area
// rectangle // rectangle
CDockOverlay* Overlay = DockManager->containerOverlay(); CDockOverlay *Overlay = DockManager->containerOverlay();
if (!Overlay->dropOverlayRect().isValid()) if (!Overlay->dropOverlayRect().isValid())
{ {
Overlay = DockManager->dockAreaOverlay(); Overlay = DockManager->dockAreaOverlay();
} }
QRect Rect = Overlay->dropOverlayRect(); QRect Rect = Overlay->dropOverlayRect();
int FrameWidth = (_this->frameSize().width() - _this->rect().width()) / 2; int FrameWidth = (_this->frameSize().width() - _this->rect().width())
int TitleBarHeight = _this->frameSize().height() - _this->rect().height() - FrameWidth; / 2;
int TitleBarHeight = _this->frameSize().height()
- _this->rect().height() - FrameWidth;
if (Rect.isValid()) if (Rect.isValid())
{ {
QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft()); QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft());
TopLeft.ry() += TitleBarHeight; TopLeft.ry() += TitleBarHeight;
_this->setGeometry(QRect(TopLeft, QSize(Rect.width(), Rect.height() - TitleBarHeight))); _this->setGeometry(
QRect(TopLeft,
QSize(Rect.width(), Rect.height() - TitleBarHeight)));
QApplication::processEvents(); QApplication::processEvents();
} }
DropContainer->dropFloatingWidget(_this, QCursor::pos()); DropContainer->dropFloatingWidget(_this, QCursor::pos());
@ -135,100 +152,113 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
DockManager->dockAreaOverlay()->hideOverlay(); DockManager->dockAreaOverlay()->hideOverlay();
} }
//============================================================================ //============================================================================
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos) void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &GlobalPos)
{ {
if (!_this->isVisible() || !DockManager) if (!_this->isVisible() || !DockManager)
{ {
return; return;
} }
auto Containers = DockManager->dockContainers(); auto Containers = DockManager->dockContainers();
CDockContainerWidget* TopContainer = nullptr; CDockContainerWidget *TopContainer = nullptr;
for (auto ContainerWidget : Containers) for (auto ContainerWidget : Containers)
{ {
if (!ContainerWidget->isVisible()) if (!ContainerWidget->isVisible())
{ {
continue; continue;
} }
if (DockContainer == ContainerWidget) if (DockContainer == ContainerWidget)
{ {
continue; continue;
} }
QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos); QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);
if (ContainerWidget->rect().contains(MappedPos)) if (ContainerWidget->rect().contains(MappedPos))
{ {
if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer)) if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer))
{ {
TopContainer = ContainerWidget; TopContainer = ContainerWidget;
} }
} }
} }
DropContainer = TopContainer; DropContainer = TopContainer;
auto ContainerOverlay = DockManager->containerOverlay(); auto ContainerOverlay = DockManager->containerOverlay();
auto DockAreaOverlay = DockManager->dockAreaOverlay(); auto DockAreaOverlay = DockManager->dockAreaOverlay();
if (!TopContainer) if (!TopContainer)
{ {
ContainerOverlay->hideOverlay(); ContainerOverlay->hideOverlay();
DockAreaOverlay->hideOverlay(); DockAreaOverlay->hideOverlay();
return; return;
} }
int VisibleDockAreas = TopContainer->visibleDockAreaCount(); int VisibleDockAreas = TopContainer->visibleDockAreaCount();
ContainerOverlay->setAllowedAreas(VisibleDockAreas > 1 ? ContainerOverlay->setAllowedAreas(
OuterDockAreas : AllDockAreas); VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
DockWidgetArea ContainerArea = ContainerOverlay->showOverlay(TopContainer); DockWidgetArea ContainerArea = ContainerOverlay->showOverlay(TopContainer);
ContainerOverlay->enableDropPreview(ContainerArea != InvalidDockWidgetArea); ContainerOverlay->enableDropPreview(ContainerArea != InvalidDockWidgetArea);
auto DockArea = TopContainer->dockAreaAt(GlobalPos); auto DockArea = TopContainer->dockAreaAt(GlobalPos);
if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0) if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0)
{ {
DockAreaOverlay->enableDropPreview(true); DockAreaOverlay->enableDropPreview(true);
DockAreaOverlay->setAllowedAreas((VisibleDockAreas == 1) ? DockAreaOverlay->setAllowedAreas(
NoDockWidgetArea : AllDockAreas); (VisibleDockAreas == 1) ? NoDockWidgetArea : AllDockAreas);
DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea); DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that // A CenterDockWidgetArea for the dockAreaOverlay() indicates that
// the mouse is in the title bar. If the ContainerArea is valid // the mouse is in the title bar. If the ContainerArea is valid
// then we ignore the dock area of the dockAreaOverlay() and disable // then we ignore the dock area of the dockAreaOverlay() and disable
// the drop preview // the drop preview
if ((Area == CenterDockWidgetArea) && (ContainerArea != InvalidDockWidgetArea)) if ((Area == CenterDockWidgetArea)
{ && (ContainerArea != InvalidDockWidgetArea))
DockAreaOverlay->enableDropPreview(false); {
ContainerOverlay->enableDropPreview(true); DockAreaOverlay->enableDropPreview(false);
} ContainerOverlay->enableDropPreview(true);
else }
{ else
ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area); {
} ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
} }
else }
{ else
DockAreaOverlay->hideOverlay(); {
} DockAreaOverlay->hideOverlay();
}
} }
//============================================================================ //============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) : CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) :
QWidget(DockManager, Qt::Window), tFloatingWidgetBase(DockManager),
d(new FloatingDockContainerPrivate(this)) d(new FloatingDockContainerPrivate(this))
{ {
d->DockManager = DockManager; d->DockManager = DockManager;
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom); d->DockContainer = new CDockContainerWidget(DockManager, this);
l->setContentsMargins(0, 0, 0, 0); connect(d->DockContainer, SIGNAL(dockAreasAdded()), this,
l->setSpacing(0); SLOT(onDockAreasAddedOrRemoved()));
setLayout(l); connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this,
SLOT(onDockAreasAddedOrRemoved()));
d->DockContainer = new CDockContainerWidget(DockManager, this); #ifdef Q_OS_LINUX
connect(d->DockContainer, SIGNAL(dockAreasAdded()), this, SLOT(onDockAreasAddedOrRemoved())); d->TitleBar = new CFloatingWidgetTitleBar(this);
connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this, SLOT(onDockAreasAddedOrRemoved())); setWindowFlags(windowFlags() | 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);
l->addWidget(d->DockContainer); l->addWidget(d->DockContainer);
#endif
DockManager->registerFloatingWidget(this); DockManager->registerFloatingWidget(this);
// We install an event filter to detect mouse release events because we // We install an event filter to detect mouse release events because we
@ -237,26 +267,30 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) :
qApp->installEventFilter(this); qApp->installEventFilter(this);
} }
//============================================================================ //============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget* DockArea) : CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget *DockArea) :
CFloatingDockContainer(DockArea->dockManager()) CFloatingDockContainer(DockArea->dockManager())
{ {
d->DockContainer->addDockArea(DockArea); d->DockContainer->addDockArea(DockArea);
#ifdef Q_OS_LINUX
d->TitleBar->enableCloseButton(isClosable());
#endif
} }
//============================================================================ //============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockWidget* DockWidget) : 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
} }
//============================================================================ //============================================================================
CFloatingDockContainer::~CFloatingDockContainer() CFloatingDockContainer::~CFloatingDockContainer()
{ {
qDebug() << "~CFloatingDockContainer"; ADS_PRINT("~CFloatingDockContainer");
if (d->DockManager) if (d->DockManager)
{ {
d->DockManager->removeFloatingWidget(this); d->DockManager->removeFloatingWidget(this);
@ -264,27 +298,24 @@ CFloatingDockContainer::~CFloatingDockContainer()
delete d; delete d;
} }
//============================================================================ //============================================================================
CDockContainerWidget* CFloatingDockContainer::dockContainer() const CDockContainerWidget* CFloatingDockContainer::dockContainer() const
{ {
return d->DockContainer; return d->DockContainer;
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::changeEvent(QEvent *event) void CFloatingDockContainer::changeEvent(QEvent *event)
{ {
QWidget::changeEvent(event); QWidget::changeEvent(event);
if ((event->type() == QEvent::ActivationChange) && isActiveWindow()) if ((event->type() == QEvent::ActivationChange) && isActiveWindow())
{ {
qDebug() << "FloatingWidget::changeEvent QEvent::ActivationChange "; ADS_PRINT("FloatingWidget::changeEvent QEvent::ActivationChange ");
d->zOrderIndex = ++zOrderCounter; d->zOrderIndex = ++zOrderCounter;
return; return;
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::moveEvent(QMoveEvent *event) void CFloatingDockContainer::moveEvent(QMoveEvent *event)
{ {
@ -292,47 +323,52 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event)
switch (d->DraggingState) switch (d->DraggingState)
{ {
case DraggingMousePressed: case DraggingMousePressed:
d->setState(DraggingFloatingWidget); d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
break; break;
case DraggingFloatingWidget: case DraggingFloatingWidget:
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
break; break;
default: default:
break; break;
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::closeEvent(QCloseEvent *event) void CFloatingDockContainer::closeEvent(QCloseEvent *event)
{ {
qDebug() << "CFloatingDockContainer closeEvent"; ADS_PRINT("CFloatingDockContainer closeEvent");
d->setState(DraggingInactive); d->setState(DraggingInactive);
if (isClosable()) if (isClosable())
{ {
// In Qt version after 5.9.2 there seems to be a bug that causes the // In Qt version after 5.9.2 there seems to be a bug that causes the
// QWidget::event() function to not receive any NonClientArea mouse // QWidget::event() function to not receive any NonClientArea mouse
// events anymore after a close/show cycle. The bug is reported here: // events anymore after a close/show cycle. The bug is reported here:
// https://bugreports.qt.io/browse/QTBUG-73295 // https://bugreports.qt.io/browse/QTBUG-73295
// The following code is a workaround for Qt versions > 5.9.2 that seems // The following code is a workaround for Qt versions > 5.9.2 that seems
// to work // to work
#if (QT_VERSION > 0x050902) // 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(); event->ignore();
this->hide(); this->hide();
#else #else
Super::closeEvent(event);
#endif
#else // Q_OS_LINUX
Super::closeEvent(event); Super::closeEvent(event);
#endif #endif
} }
else else
{ {
event->ignore(); event->ignore();
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::hideEvent(QHideEvent *event) void CFloatingDockContainer::hideEvent(QHideEvent *event)
{ {
@ -346,69 +382,78 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event)
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::showEvent(QShowEvent *event) void CFloatingDockContainer::showEvent(QShowEvent *event)
{ {
Super::showEvent(event); Super::showEvent(event);
/*for (auto DockArea : d->DockContainer->openedDockAreas())
{
for (auto DockWidget : DockArea->openedDockWidgets())
{
DockWidget->setToggleViewActionChecked(true);
}
}*/
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::event(QEvent *e) bool CFloatingDockContainer::event(QEvent *e)
{ {
switch (d->DraggingState) switch (d->DraggingState)
{ {
case DraggingInactive: case DraggingInactive:
if (e->type() == QEvent::NonClientAreaMouseButtonPress && QGuiApplication::mouseButtons() == Qt::LeftButton) {
// Normally we would check here, if the left mouse button is pressed.
// But from QT version 5.12.2 on the mouse events from
// QEvent::NonClientAreaMouseButtonPress return the wrong mouse button
// The event always returns Qt::RightButton even if the left button
// is clicked.
// It is really great to work around the whole NonClientMouseArea
// bugs
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2))
if (e->type()
== QEvent::NonClientAreaMouseButtonPress /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/)
{ {
qDebug() << "FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type(); ADS_PRINT("FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type());
d->setState(DraggingMousePressed); d->setState(DraggingMousePressed);
} }
break; #else
if (e->type() == QEvent::NonClientAreaMouseButtonPress && QGuiApplication::mouseButtons().testFlag(Qt::LeftButton))
{
ADS_PRINT("FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type());
d->setState(DraggingMousePressed);
}
#endif
}
break;
case DraggingMousePressed: case DraggingMousePressed:
switch (e->type()) switch (e->type())
{ {
case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::NonClientAreaMouseButtonDblClick:
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick"; ADS_PRINT("FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick");
d->setState(DraggingInactive); d->setState(DraggingInactive);
break; break;
case QEvent::Resize: case QEvent::Resize:
// If the first event after the mouse press is a resize event, then // If the first event after the mouse press is a resize event, then
// the user resizes the window instead of dragging it around. // the user resizes the window instead of dragging it around.
// But there is one exception. If the window is maximized, // But there is one exception. If the window is maximized,
// then dragging the window via title bar will cause the widget to // then dragging the window via title bar will cause the widget to
// leave the maximized state. This in turn will trigger a resize event. // leave the maximized state. This in turn will trigger a resize event.
// To know, if the resize event was triggered by user via moving a // To know, if the resize event was triggered by user via moving a
// corner of the window frame or if it was caused by a windows state // corner of the window frame or if it was caused by a windows state
// change, we check, if we are not in maximized state. // change, we check, if we are not in maximized state.
if (!isMaximized()) if (!isMaximized())
{ {
d->setState(DraggingInactive); d->setState(DraggingInactive);
} }
break; break;
default: default:
break; break;
} }
break; break;
case DraggingFloatingWidget: case DraggingFloatingWidget:
if (e->type() == QEvent::NonClientAreaMouseButtonRelease) if (e->type() == QEvent::NonClientAreaMouseButtonRelease)
{ {
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease"; ADS_PRINT("FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease");
d->titleMouseReleaseEvent(); d->titleMouseReleaseEvent();
} }
break; break;
default: default:
break; break;
@ -420,100 +465,120 @@ bool CFloatingDockContainer::event(QEvent *e)
return QWidget::event(e); return QWidget::event(e);
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event) bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event)
{ {
Q_UNUSED(watched); Q_UNUSED(watched);
if (event->type() == QEvent::MouseButtonRelease && d->isState(DraggingFloatingWidget)) if (event->type() == QEvent::MouseButtonRelease
&& d->isState(DraggingFloatingWidget))
{ {
qDebug() << "FloatingWidget::eventFilter QEvent::MouseButtonRelease"; ADS_PRINT("FloatingWidget::eventFilter QEvent::MouseButtonRelease");
finishDragging();
d->titleMouseReleaseEvent(); d->titleMouseReleaseEvent();
} }
return false; return false;
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::startFloating(const QPoint& DragStartMousePos, const QSize& Size, void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos,
eDragState DragState) const QSize &Size, eDragState DragState, QWidget *MouseEventHandler)
{ {
#ifndef Q_OS_LINUX
Q_UNUSED(MouseEventHandler)
#endif
resize(Size); resize(Size);
d->setState(DragState); d->setState(DragState);
d->DragStartMousePosition = DragStartMousePos; d->DragStartMousePosition = DragStartMousePos;
#ifdef Q_OS_LINUX
// I have not found a way on Linux to display the floating widget behind the
// dock overlay. That means if the user drags this floating widget around,
// it is always painted in front of the dock overlay and dock overlay cross.
// and the user will not see the dock overlay. To work around this issue,
// the window opacity is set to 0.6 to make the dock overlay visible
// again. If someone has an idea, how to place the dragged floating widget
// behind the dock overlay, then a pull request would be welcome.
if (DraggingFloatingWidget == DragState)
{
setAttribute(Qt::WA_X11NetWmWindowTypeDock, true);
setWindowOpacity(0.6);
d->MouseEventHandler = MouseEventHandler;
if (d->MouseEventHandler)
{
d->MouseEventHandler->grabMouse();
}
}
#endif
moveFloating(); moveFloating();
show(); show();
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::moveFloating() void CFloatingDockContainer::moveFloating()
{ {
int BorderSize = (frameSize().width() - size().width()) / 2; int BorderSize = (frameSize().width() - size().width()) / 2;
const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0); const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition
- QPoint(BorderSize, 0);
move(moveToPos); move(moveToPos);
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::isClosable() const bool CFloatingDockContainer::isClosable() const
{ {
return d->DockContainer->features().testFlag(CDockWidget::DockWidgetClosable); return d->DockContainer->features().testFlag(
CDockWidget::DockWidgetClosable);
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::onDockAreasAddedOrRemoved() void CFloatingDockContainer::onDockAreasAddedOrRemoved()
{ {
qDebug() << "CFloatingDockContainer::onDockAreasAddedOrRemoved()"; ADS_PRINT("CFloatingDockContainer::onDockAreasAddedOrRemoved()");
auto TopLevelDockArea = d->DockContainer->topLevelDockArea(); auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
if (TopLevelDockArea) if (TopLevelDockArea)
{ {
d->SingleDockArea = TopLevelDockArea; d->SingleDockArea = TopLevelDockArea;
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); d->setWindowTitle(
d->SingleDockArea->currentDockWidget()->windowTitle());
connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this, connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int))); SLOT(onDockAreaCurrentChanged(int)));
} }
else else
{ {
if (d->SingleDockArea) if (d->SingleDockArea)
{ {
disconnect(d->SingleDockArea, SIGNAL(currentChanged(int)), this, disconnect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int))); SLOT(onDockAreaCurrentChanged(int)));
d->SingleDockArea = nullptr; d->SingleDockArea = nullptr;
} }
this->setWindowTitle(qApp->applicationDisplayName()); d->setWindowTitle(qApp->applicationDisplayName());
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::updateWindowTitle() void CFloatingDockContainer::updateWindowTitle()
{ {
auto TopLevelDockArea = d->DockContainer->topLevelDockArea(); auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
if (TopLevelDockArea) if (TopLevelDockArea)
{ {
this->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle()); d->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle());
} }
else else
{ {
this->setWindowTitle(qApp->applicationDisplayName()); d->setWindowTitle(qApp->applicationDisplayName());
} }
} }
//============================================================================ //============================================================================
void CFloatingDockContainer::onDockAreaCurrentChanged(int Index) void CFloatingDockContainer::onDockAreaCurrentChanged(int Index)
{ {
Q_UNUSED(Index); Q_UNUSED(Index);
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); d->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle());
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::restoreState(QXmlStreamReader& Stream, bool Testing) bool CFloatingDockContainer::restoreState(QXmlStreamReader &Stream,
bool Testing)
{ {
if (!d->DockContainer->restoreState(Stream, Testing)) if (!d->DockContainer->restoreState(Stream, Testing))
{ {
@ -524,27 +589,39 @@ bool CFloatingDockContainer::restoreState(QXmlStreamReader& Stream, bool Testing
return true; return true;
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::hasTopLevelDockWidget() const bool CFloatingDockContainer::hasTopLevelDockWidget() const
{ {
return d->DockContainer->hasTopLevelDockWidget(); return d->DockContainer->hasTopLevelDockWidget();
} }
//============================================================================ //============================================================================
CDockWidget* CFloatingDockContainer::topLevelDockWidget() const CDockWidget* CFloatingDockContainer::topLevelDockWidget() const
{ {
return d->DockContainer->topLevelDockWidget(); return d->DockContainer->topLevelDockWidget();
} }
//============================================================================ //============================================================================
QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const
{ {
return d->DockContainer->dockWidgets(); return d->DockContainer->dockWidgets();
} }
//============================================================================
void CFloatingDockContainer::finishDragging()
{
ADS_PRINT("CFloatingDockContainer::finishDragging");
#ifdef Q_OS_LINUX
setAttribute(Qt::WA_X11NetWmWindowTypeDock, false);
setWindowOpacity(1);
activateWindow();
if (d->MouseEventHandler)
{
d->MouseEventHandler->releaseMouse();
d->MouseEventHandler = nullptr;
}
#endif
}
} // namespace ads } // namespace ads

View File

@ -29,10 +29,16 @@
//============================================================================ //============================================================================
// INCLUDES // INCLUDES
//============================================================================ //============================================================================
#include <QWidget>
#include "ads_globals.h" #include "ads_globals.h"
#ifdef Q_OS_LINUX
#include <QDockWidget>
#define tFloatingWidgetBase QDockWidget
#else
#include <QWidget>
#define tFloatingWidgetBase QWidget
#endif
class QXmlStreamReader; class QXmlStreamReader;
namespace ads namespace ads
@ -49,13 +55,15 @@ class CDockWidgetTab;
struct DockWidgetTabPrivate; struct DockWidgetTabPrivate;
class CDockAreaTitleBar; class CDockAreaTitleBar;
struct DockAreaTitleBarPrivate; struct DockAreaTitleBarPrivate;
class CFloatingWidgetTitleBar;
/** /**
* This implements a floating widget that is a dock container that accepts * 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 * docking of dock widgets like the main window and that can be docked into
* another dock container * another dock container.
* Every floating window of the docking system is a FloatingDockContainer.
*/ */
class ADS_EXPORT CFloatingDockContainer : public QWidget class ADS_EXPORT CFloatingDockContainer : public tFloatingWidgetBase
{ {
Q_OBJECT Q_OBJECT
private: private:
@ -70,6 +78,7 @@ private:
friend struct DockAreaTitleBarPrivate; friend struct DockAreaTitleBarPrivate;
friend class CDockWidget; friend class CDockWidget;
friend class CDockAreaWidget; friend class CDockAreaWidget;
friend class CFloatingWidgetTitleBar;
private slots: private slots:
void onDockAreasAddedOrRemoved(); void onDockAreasAddedOrRemoved();
@ -82,23 +91,30 @@ protected:
* depending on the start position given in Pos parameter * depending on the start position given in Pos parameter
*/ */
void startFloating(const QPoint& DragStartMousePos, const QSize& Size, void startFloating(const QPoint& DragStartMousePos, const QSize& Size,
eDragState DragState); eDragState DragState, QWidget* MouseEventHandler);
/** /**
* Call this function to start dragging the floating widget * 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 * Call this function if you just want to initialize the position
* and size of the floating widget * and size of the floating widget
*/ */
void initFloatingGeometry(const QPoint& DragStartMousePos, const QSize& Size) void initFloatingGeometry(const QPoint& DragStartMousePos, const QSize& Size)
{ {
startFloating(DragStartMousePos, Size, DraggingInactive); startFloating(DragStartMousePos, Size, DraggingInactive, nullptr);
} }
/** /**

View File

@ -3,5 +3,6 @@
<file>stylesheets/default.css</file> <file>stylesheets/default.css</file>
<file>images/close-button.svg</file> <file>images/close-button.svg</file>
<file>images/close-button-disabled.svg</file> <file>images/close-button-disabled.svg</file>
<file>stylesheets/default_linux.css</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -45,6 +45,15 @@
#define ADS_EXPORT #define ADS_EXPORT
#endif #endif
// Define ADS_DEBUG_PRINT to enable a lot of debug output
#ifdef ADS_DEBUG_PRINT
#define ADS_PRINT(s) qDebug() << s
#else
#define ADS_PRINT(s)
#endif
// Set ADS_DEBUG_LEVEL to enable additional debug output and to enable layout
// dumps to qDebug and std::cout after layout changes
#define ADS_DEBUG_LEVEL 0 #define ADS_DEBUG_LEVEL 0
class QSplitter; class QSplitter;
@ -153,6 +162,27 @@ T findParent(const QWidget* w)
*/ */
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity); QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity);
/**
* Helper function for settings flags in a QFlags instance.
*/
template <class T>
void setFlag(T& Flags, typename T::enum_type flag, bool on = true)
{
#if QT_VERSION >= 0x050700
Flags.setFlag(flag, on);
#else
if(on)
{
Flags |= flag;
}
else
{
Flags &= ~flag;
}
#endif
}
} // namespace internal } // namespace internal
} // namespace ads } // namespace ads

View File

@ -0,0 +1,183 @@
/*******************************************************************************
** 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file FloatingWidgetTitleBar.cpp
/// \author Uwe Kindler
/// \date 13.05.2019
/// \brief Implementation of CFloatingWidgetTitleBar class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "FloatingWidgetTitleBar.h"
#include <iostream>
#include <QHBoxLayout>
#include <QPushButton>
#include <QToolButton>
#include <QPixmap>
#include <QStyle>
#include <QMouseEvent>
#include "ads_globals.h"
#include "ElidingLabel.h"
#include "FloatingDockContainer.h"
namespace ads
{
using tTabLabel = CElidingLabel;
using tCloseButton = QPushButton;
/**
* @brief Private data class of public interface CFloatingWidgetTitleBar
*/
struct FloatingWidgetTitleBarPrivate
{
CFloatingWidgetTitleBar *_this; ///< public interface class
QLabel *IconLabel = nullptr;
tTabLabel *TitleLabel;
tCloseButton *CloseButton = nullptr;
CFloatingDockContainer *FloatingWidget = nullptr;
eDragState DragState = DraggingInactive;
FloatingWidgetTitleBarPrivate(CFloatingWidgetTitleBar *_public) :
_this(_public)
{
}
/**
* Creates the complete layout including all controls
*/
void createLayout();
};
//============================================================================
void FloatingWidgetTitleBarPrivate::createLayout()
{
TitleLabel = new tTabLabel();
TitleLabel->setElideMode(Qt::ElideRight);
TitleLabel->setText("DockWidget->windowTitle()");
TitleLabel->setObjectName("floatingTitleLabel");
TitleLabel->setAlignment(Qt::AlignLeft);
CloseButton = new tCloseButton();
CloseButton->setObjectName("floatingTitleCloseButton");
CloseButton->setFlat(true);
// The standard icons do does not look good on high DPI screens
QIcon CloseIcon;
QPixmap normalPixmap = _this->style()->standardPixmap(
QStyle::SP_TitleBarCloseButton, 0, CloseButton);
CloseIcon.addPixmap(normalPixmap, QIcon::Normal);
CloseIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25),
QIcon::Disabled);
CloseButton->setIcon(
_this->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
CloseButton->setVisible(true);
CloseButton->setFocusPolicy(Qt::NoFocus);
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
QFontMetrics fm(TitleLabel->font());
int Spacing = qRound(fm.height() / 4.0);
// Fill the layout
QBoxLayout *Layout = new QBoxLayout(QBoxLayout::LeftToRight);
Layout->setContentsMargins(6, 0, 0, 0);
Layout->setSpacing(0);
_this->setLayout(Layout);
Layout->addWidget(TitleLabel, 1);
Layout->addSpacing(Spacing);
Layout->addWidget(CloseButton);
Layout->setAlignment(Qt::AlignCenter);
TitleLabel->setVisible(true);
}
//============================================================================
CFloatingWidgetTitleBar::CFloatingWidgetTitleBar(CFloatingDockContainer *parent) :
QWidget(parent),
d(new FloatingWidgetTitleBarPrivate(this))
{
d->FloatingWidget = parent;
d->createLayout();
}
//============================================================================
CFloatingWidgetTitleBar::~CFloatingWidgetTitleBar()
{
delete d;
}
//============================================================================
void CFloatingWidgetTitleBar::mousePressEvent(QMouseEvent *ev)
{
if (ev->button() == Qt::LeftButton)
{
d->DragState = DraggingFloatingWidget;
d->FloatingWidget->startDragging(ev->pos(), d->FloatingWidget->size(),
this);
return;
}
Super::mousePressEvent(ev);
}
//============================================================================
void CFloatingWidgetTitleBar::mouseReleaseEvent(QMouseEvent *ev)
{
d->DragState = DraggingInactive;
Super::mouseReleaseEvent(ev);
}
//============================================================================
void CFloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *ev)
{
if (!(ev->buttons() & Qt::LeftButton) || DraggingInactive == d->DragState)
{
d->DragState = DraggingInactive;
Super::mouseMoveEvent(ev);
return;
}
// move floating window
if (DraggingFloatingWidget == d->DragState)
{
d->FloatingWidget->moveFloating();
Super::mouseMoveEvent(ev);
return;
}
Super::mouseMoveEvent(ev);
}
//============================================================================
void CFloatingWidgetTitleBar::enableCloseButton(bool Enable)
{
d->CloseButton->setEnabled(Enable);
}
//============================================================================
void CFloatingWidgetTitleBar::setTitle(const QString &Text)
{
d->TitleLabel->setText(Text);
}
} // namespace ads

View File

@ -0,0 +1,85 @@
#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 <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file FloatingWidgetTitleBar.h
/// \author Uwe Kindler
/// \date 13.05.2019
/// \brief Declaration of CFloatingWidgetTitleBar class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QWidget>
namespace ads
{
class CFloatingDockContainer;
struct FloatingWidgetTitleBarPrivate;
/**
* Titlebar for floating widgets to capture non client are mouse events.
* Linux does not support NonClieantArea mouse events like
* QEvent::NonClientAreaMouseButtonPress. Because these events are required
* for the docking system to work properly, we use our own titlebar here to
* capture the required mouse events.
*/
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();
/**
* Enables / disables the window close button.
*/
void enableCloseButton(bool Enable);
/**
* Sets the window title, that means, the text of the internal tile label.
*/
void setTitle(const QString &Text);
signals:
/**
* This signal is emitted, if the close button is clicked.
*/
void closeRequested();
};
} // namespace ads
#endif // FLOATINGWIDGETTITLEBAR_H

View File

@ -41,9 +41,8 @@ HEADERS += \
DockSplitter.h \ DockSplitter.h \
DockAreaTitleBar.h \ DockAreaTitleBar.h \
ElidingLabel.h ElidingLabel.h
SOURCES += \ SOURCES += \
ads_globals.cpp \ ads_globals.cpp \
DockAreaWidget.cpp \ DockAreaWidget.cpp \
@ -58,6 +57,12 @@ SOURCES += \
DockAreaTitleBar.cpp \ DockAreaTitleBar.cpp \
ElidingLabel.cpp ElidingLabel.cpp
unix {
HEADERS += linux/FloatingWidgetTitleBar.h
SOURCES += linux/FloatingWidgetTitleBar.cpp
}
isEmpty(PREFIX){ isEmpty(PREFIX){
PREFIX=..\installed PREFIX=..\installed
warning("Install Prefix not set") warning("Install Prefix not set")
@ -66,3 +71,5 @@ headers.path=$$PREFIX/include
headers.files=$$HEADERS headers.files=$$HEADERS
target.path=$$PREFIX/lib target.path=$$PREFIX/lib
INSTALLS += headers target INSTALLS += headers target
DISTFILES +=

View File

@ -63,7 +63,7 @@ ads--CDockWidget
#closeButton, #closeButton,
#undockButton #undockButton
{ {
padding: 0 -2px; padding: 0px -2px;
} }

View File

@ -0,0 +1,96 @@
/*
* Default style sheet on Windows Platforms
* Note: Always use CSS-classes with and without "ads--" namespace to support Qt4 & Qt5
*/
ads--CDockContainerWidget
{
background: palette(dark);
}
ads--CDockContainerWidget QSplitter::handle
{
background: palette(dark);
}
ads--CDockAreaWidget
{
background: palette(window);
border: 1px solid white;
}
ads--CDockAreaWidget #tabsMenuButton::menu-indicator
{
image: none;
}
ads--CDockWidgetTab
{
background: palette(window);
border-color: palette(light);
border-style: solid;
border-width: 0 1px 0 0;
padding: 0 0px;
}
ads--CDockWidgetTab[activeTab="true"]
{
background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:0.5, stop:0 palette(window), stop:1 palette(light));
/*background: palette(highlight);*/
}
ads--CDockWidgetTab QLabel
{
color: palette(dark);
}
ads--CDockWidgetTab[activeTab="true"] QLabel
{
color: palette(foreground);
}
ads--CDockWidget
{
background: palette(light);
border-color: palette(light);
border-style: solid;
border-width: 1px 0 0 0;
}
#tabsMenuButton,
#closeButton,
#undockButton
{
padding: 0px -2px;
}
QScrollArea#dockWidgetScrollArea
{
padding: 0px;
border: none;
}
#tabCloseButton
{
margin-top: 2px;
background: none;
border: none;
padding: 0px -2px;
}
#tabCloseButton:hover
{
border: 1px solid rgba(0, 0, 0, 32);
background: rgba(0, 0, 0, 16);
}
#tabCloseButton:pressed
{
background: rgba(0, 0, 0, 32);
}