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 moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<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">
<buildTargets>
<target name="Build all" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
@ -356,4 +347,13 @@
</target>
</buildTargets>
</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>

2
.gitignore vendored
View File

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

View File

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

View File

@ -1,6 +1,7 @@
# 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://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
integrated development environements (IDEs) such as Visual Studio.
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.
This work is based on and inspired by the
@ -60,9 +61,22 @@ main window layout.
![Perspective](doc/perspectives_dark.png)
## 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)
- Ubuntu [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
- macOS [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
### Windows
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)
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
Open the `ads.pro` with QtCreator and start the build, that's it.
@ -144,5 +158,6 @@ MainWindow::~MainWindow()
- Manuel Freiholz
## 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)

View File

@ -23,8 +23,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(ads_demo_SRCS
main.cpp
MainWindow.cpp
MainWindow.ui
main.qrc
mainwindow.ui
)
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
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)
{
@ -110,7 +136,8 @@ static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu)
QFileSystemModel* m = new QFileSystemModel(w);
m->setRootPath(QDir::currentPath());
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);
ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget;
@ -185,6 +212,8 @@ void MainWindowPrivate::createContent()
ToolBar->addAction(ui.actionSaveState);
ToolBar->addAction(ui.actionRestoreState);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
appendFeaturStringToWindowTitle(FileSystemWidget);
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
DockWidget = createCalendarDockWidget(ViewMenu);
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
@ -270,19 +299,27 @@ CMainWindow::CMainWindow(QWidget *parent) :
d->ui.setupUi(this);
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
d->DockManager = new CDockManager(this);
// Uncomment the following line to have the old style where the dock
// area close button closes the active tab
//d->DockManager->setConfigFlags({
// CDockManager::DockAreaHasCloseButton | CDockManager::DockAreaCloseButtonClosesTab});
// CDockManager::setConfigFlags({CDockManager::DockAreaHasCloseButton
// | CDockManager::DockAreaCloseButtonClosesTab});
connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)),
d->DockManager, SLOT(openPerspective(const QString&)));
d->createContent();
// Default window geometry
resize(800, 600);
resize(1280, 720);
d->restoreState();
d->restorePerspectives();

View File

@ -21,7 +21,6 @@ HEADERS += \
FORMS += \
mainwindow.ui
RESOURCES += main.qrc
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 += \
MainWindow.ui
#RESOURCES += main.qrc
LIBS += -L$${ADS_OUT_ROOT}/lib

View File

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

View File

@ -44,6 +44,10 @@ class CFloatingDockContainer;
* Custom tabbar implementation for tab area that is shown on top of a
* dock area widget.
* 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
{
@ -61,6 +65,7 @@ private slots:
protected:
virtual void wheelEvent(QWheelEvent* Event) override;
/**
* Stores mouse position to detect dragging
*/
@ -96,6 +101,7 @@ protected:
public:
using Super = QScrollArea;
/**
* Default Constructor
*/
@ -150,6 +156,21 @@ public:
*/
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:
/**
* 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
{
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
@ -112,50 +130,47 @@ DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(CDockAreaTitleBar* _public) :
//============================================================================
void DockAreaTitleBarPrivate::createButtons()
{
QSizePolicy ButtonSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
// Tabs menu button
TabsMenuButton = new tTileBarButton();
TabsMenuButton->setObjectName("tabsMenuButton");
TabsMenuButton->setAutoRaise(true);
TabsMenuButton->setPopupMode(QToolButton::InstantPopup);
TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
setTitleBarButtonIcon(TabsMenuButton, QStyle::SP_TitleBarUnshadeButton);
QMenu* TabsMenu = new QMenu(TabsMenuButton);
#ifndef QT_NO_TOOLTIP
#ifndef QT_NO_TOOLTIP
TabsMenu->setToolTipsVisible(true);
#endif
#endif
_this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow()));
TabsMenuButton->setMenu(TabsMenu);
#ifndef QT_NO_TOOLTIP
#ifndef QT_NO_TOOLTIP
TabsMenuButton->setToolTip(QObject::tr("List all tabs"));
#endif
TabsMenuButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
#endif
TabsMenuButton->setSizePolicy(ButtonSizePolicy);
TopLayout->addWidget(TabsMenuButton, 0);
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
SLOT(onTabsMenuActionTriggered(QAction*)));
// Undock button
UndockButton = new tTileBarButton();
UndockButton->setObjectName("undockButton");
UndockButton->setAutoRaise(true);
#ifndef QT_NO_TOOLTIP
#ifndef QT_NO_TOOLTIP
UndockButton->setToolTip(QObject::tr("Detach Group"));
#endif
UndockButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarNormalButton));
UndockButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
#endif
setTitleBarButtonIcon(UndockButton, QStyle::SP_TitleBarNormalButton);
UndockButton->setSizePolicy(ButtonSizePolicy);
TopLayout->addWidget(UndockButton, 0);
_this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked()));
// Close button
CloseButton = new tTileBarButton();
CloseButton->setObjectName("closeButton");
CloseButton->setAutoRaise(true);
// The standard icons do not look good on high DPI screens
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
setTitleBarButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton);
#ifndef QT_NO_TOOLTIP
if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
{
CloseButton->setToolTip(QObject::tr("Close Active Tab"));
@ -164,9 +179,13 @@ void DockAreaTitleBarPrivate::createButtons()
{
CloseButton->setToolTip(QObject::tr("Close Group"));
}
#endif
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
TopLayout->addWidget(CloseButton, 0);
#endif
CloseButton->setSizePolicy(ButtonSizePolicy);
CloseButton->setIconSize(QSize(16, 16));
if (testConfigFlag(CDockManager::DockAreaHasCloseButton))
{
TopLayout->addWidget(CloseButton, 0);
}
_this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked()));
}
@ -262,7 +281,7 @@ void CDockAreaTitleBar::onTabsMenuAboutToShow()
//============================================================================
void CDockAreaTitleBar::onCloseButtonClicked()
{
qDebug() << "CDockAreaTitleBar::onCloseButtonClicked";
ADS_PRINT("CDockAreaTitleBar::onCloseButtonClicked");
if (d->testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
{
d->TabBar->closeTab(d->TabBar->currentIndex());
@ -277,7 +296,10 @@ void CDockAreaTitleBar::onCloseButtonClicked()
//============================================================================
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)
{
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();
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));
Menu.addAction(tr("Close Other Areas"), d->DockArea, SLOT(closeOtherAreas()));
Menu.exec(mapToGlobal(pos));

View File

@ -43,7 +43,9 @@ class CDockAreaWidget;
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
{
@ -61,6 +63,10 @@ private slots:
void showContextMenu(const QPoint& pos);
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();
@ -87,7 +93,8 @@ public:
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;

View File

@ -63,8 +63,11 @@ static const char* const INDEX_PROPERTY = "index";
static const char* const ACTION_PROPERTY = "action";
/**
* New dock area layout mimics stack layout but only inserts the current
* widget into the internal QLayout object
* Internal dock area layout mimics stack layout but only inserts the current
* 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
{
@ -118,7 +121,7 @@ public:
}
/**
* Removes the given widget from the lyout
* Removes the given widget from the layout
*/
void removeWidget(QWidget* Widget)
{
@ -241,7 +244,7 @@ struct DockAreaWidgetPrivate
DockAreaLayout* ContentsLayout = nullptr;
CDockAreaTitleBar* TitleBar = nullptr;
CDockManager* DockManager = nullptr;
bool UpdateCloseButton = false;
bool UpdateTitleBarButtons = false;
/**
* 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
@ -322,17 +325,19 @@ void DockAreaWidgetPrivate::createTitleBar()
//============================================================================
void DockAreaWidgetPrivate::updateCloseButtonState()
void DockAreaWidgetPrivate::updateTitleBarButtonStates()
{
if (_this->isHidden())
{
UpdateCloseButton = true;
UpdateTitleBarButtons = true;
return;
}
TitleBar->button(TitleBarButtonClose)->setEnabled(
_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()
{
qDebug() << "~CDockAreaWidget()";
ADS_PRINT("~CDockAreaWidget()");
delete d->ContentsLayout;
delete d;
}
@ -400,28 +405,29 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
setCurrentIndex(index);
}
DockWidget->setDockArea(this);
d->updateCloseButtonState();
d->updateTitleBarButtonStates();
}
//============================================================================
void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
{
qDebug() << "CDockAreaWidget::removeDockWidget";
ADS_PRINT("CDockAreaWidget::removeDockWidget");
auto NextOpenDockWidget = nextOpenDockWidget(DockWidget);
d->ContentsLayout->removeWidget(DockWidget);
auto TabWidget = DockWidget->tabWidget();
TabWidget->hide();
d->tabBar()->removeTab(TabWidget);
CDockContainerWidget* DockContainer = dockContainer();
if (NextOpenDockWidget)
{
setCurrentDockWidget(NextOpenDockWidget);
}
else if (d->ContentsLayout->isEmpty())
else if (d->ContentsLayout->isEmpty() && DockContainer->dockAreaCount() > 1)
{
qDebug() << "Dock Area empty";
dockContainer()->removeDockArea(this);
ADS_PRINT("Dock Area empty");
DockContainer->removeDockArea(this);
this->deleteLater();
}
else
@ -432,16 +438,15 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
hideAreaWithNoVisibleContent();
}
d->updateCloseButtonState();
d->updateTitleBarButtonStates();
updateTitleBarVisibility();
auto TopLevelDockWidget = dockContainer()->topLevelDockWidget();
auto TopLevelDockWidget = DockContainer->topLevelDockWidget();
if (TopLevelDockWidget)
{
TopLevelDockWidget->emitTopLevelChanged(true);
}
#if (ADS_DEBUG_LEVEL > 0)
CDockContainerWidget* DockContainer = dockContainer();
DockContainer->dumpLayout();
#endif
}
@ -481,7 +486,7 @@ void CDockAreaWidget::hideAreaWithNoVisibleContent()
//============================================================================
void CDockAreaWidget::onTabCloseRequested(int Index)
{
qDebug() << "CDockAreaWidget::onTabCloseRequested " << Index;
ADS_PRINT("CDockAreaWidget::onTabCloseRequested " << Index);
dockWidget(Index)->toggleView(false);
}
@ -644,11 +649,11 @@ CDockWidget* CDockAreaWidget::dockWidget(int Index) const
//============================================================================
void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
{
qDebug() << "CDockAreaWidget::reorderDockWidget";
ADS_PRINT("CDockAreaWidget::reorderDockWidget");
if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0
|| 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;
}
@ -703,8 +708,8 @@ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const
auto CurrentDockWidget = currentDockWidget();
QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : "";
s.writeAttribute("Current", Name);
qDebug() << "CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
<< " Current: " << Name;
ADS_PRINT("CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
<< " Current: " << Name);
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
dockWidget(i)->saveState(s);
@ -765,9 +770,9 @@ void CDockAreaWidget::toggleView(bool Open)
void CDockAreaWidget::setVisible(bool 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;
/**
* Returns the number of dock widgets in this area
* Returns the number of open dock widgets in this area
*/
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;
@ -236,8 +236,10 @@ public:
* This functions returns the dock widget features of all dock widget in
* this area.
* 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
* dock are does not support the flag.
* means, if only one single dock widget does not support a certain 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;

View File

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

View File

@ -50,7 +50,10 @@ struct FloatingDockContainerPrivate;
/**
* 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
{
@ -166,6 +169,11 @@ public:
CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* DockAreaWidget = nullptr);
/**
* Removes dockwidget
*/
void removeDockWidget(CDockWidget* Dockwidget);
/**
* Returns the current zOrderIndex
*/

View File

@ -56,6 +56,8 @@
namespace ads
{
static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultConfig;
/**
* Private data class of CDockManager class (pimpl)
*/
@ -72,7 +74,6 @@ struct DockManagerPrivate
QMenu* ViewMenu;
CDockManager::eViewMenuInsertionOrder MenuInsertionOrder = CDockManager::MenuAlphabeticallySorted;
bool RestoringState = false;
CDockManager::ConfigFlags ConfigFlags = CDockManager::DefaultConfig;
/**
* Private data constructor
@ -145,7 +146,11 @@ DockManagerPrivate::DockManagerPrivate(CDockManager* _public) :
void DockManagerPrivate::loadStylesheet()
{
QString Result;
#ifdef Q_OS_LINUX
QFile StyleSheetFile(":ads/stylesheets/default_linux.css");
#else
QFile StyleSheetFile(":ads/stylesheets/default.css");
#endif
StyleSheetFile.open(QIODevice::ReadOnly);
QTextStream StyleSheetStream(&StyleSheetFile);
Result = StyleSheetStream.readAll();
@ -170,7 +175,7 @@ bool DockManagerPrivate::restoreContainer(int Index, QXmlStreamReader& stream, b
}
else
{
qDebug() << "d->Containers[i]->restoreState ";
ADS_PRINT("d->Containers[i]->restoreState ");
auto Container = Containers[Index];
if (Container->isFloating())
{
@ -207,7 +212,7 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versi
{
return false;
}
qDebug() << s.attributes().value("Version");
ADS_PRINT(s.attributes().value("Version"));
bool ok;
int v = s.attributes().value("Version").toInt(&ok);
if (!ok || v != version)
@ -217,7 +222,7 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versi
bool Result = true;
int DockContainers = s.attributes().value("Containers").toInt();
qDebug() << DockContainers;
ADS_PRINT(DockContainers);
int DockContainerCount = 0;
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))
{
qDebug() << "checkFormat: Error checking format!!!!!!!";
ADS_PRINT("checkFormat: Error checking format!!!!!!!");
return false;
}
@ -350,7 +356,7 @@ bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
if (!restoreStateFromXml(state, version))
{
qDebug() << "restoreState: Error restoring state!!!!!!!";
ADS_PRINT("restoreState: Error restoring state!!!!!!!");
return false;
}
@ -425,7 +431,7 @@ CDockManager::~CDockManager()
void CDockManager::registerFloatingWidget(CFloatingDockContainer* 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;
QXmlStreamWriter s(&xmldata);
s.setAutoFormatting(XmlAutoFormattingEnabled == XmlMode);
auto ConfigFlags = CDockManager::configFlags();
s.setAutoFormatting(ConfigFlags.testFlag(XmlAutoFormattingEnabled));
s.writeStartDocument();
s.writeStartElement("QtAdvancedDockingSystem");
s.writeAttribute("Version", QString::number(version));
@ -506,7 +513,8 @@ QByteArray CDockManager::saveState(eXmlMode XmlMode, int version) const
s.writeEndElement();
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);
}
else if (!openedDockAreas().isEmpty())
{
return addDockWidget(area, Dockwidget, openedDockAreas().last());
}
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);
}
//============================================================================
void CDockManager::removeDockWidget(CDockWidget* Dockwidget)
{
d->DockWidgetsMap.remove(Dockwidget->objectName());
CDockContainerWidget::removeDockWidget(Dockwidget);
}
//============================================================================
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)
{
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;
/**
* 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
{
@ -108,23 +117,22 @@ public:
MenuAlphabeticallySorted
};
enum eXmlMode
{
XmlAutoFormattingDisabled,
XmlAutoFormattingEnabled
};
/**
* These global configuration flags configure some global dock manager
* settings.
*/
enum eConfigFlag
{
ActiveTabHasCloseButton = 0x01, //!< 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
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
OpaqueSplitterResize = 0x08, //!< See QSplitter::setOpaqueResize() documentation
DefaultConfig = ActiveTabHasCloseButton | DockAreaHasCloseButton | OpaqueSplitterResize, ///< the default configuration
ActiveTabHasCloseButton = 0x0001, //!< If this flag is set, the active tab in a tab area has a close button
DockAreaHasCloseButton = 0x0002, //!< If the flag is set each dock area has a close button
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 = 0x0008, //!< See QSplitter::setOpaqueResize() documentation
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)
@ -133,7 +141,7 @@ public:
* If the given parent is a QMainWindow, the dock manager sets itself as the
* central widget.
* Before you create any dock widgets, you should properly setup the
* configuration flags via setConfigFlags()
* configuration flags via setConfigFlags().
*/
CDockManager(QWidget* parent = 0);
@ -145,13 +153,18 @@ public:
/**
* This function returns the global configuration flags
*/
ConfigFlags configFlags() const;
static ConfigFlags configFlags();
/**
* Sets the global configuration flags for the whole docking system.
* 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.
@ -192,6 +205,11 @@ public:
*/
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
* 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
* 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.

View File

@ -132,6 +132,21 @@ struct DockOverlayCrossPrivate
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,
@ -140,7 +155,7 @@ struct DockOverlayCrossPrivate
QLabel* l = new QLabel();
l->setObjectName("DockWidgetAreaLabel");
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 3.f;
const qreal metric = dropIndicatiorWidth(l);
const QSizeF size(metric, metric);
l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode));
@ -154,7 +169,7 @@ struct DockOverlayCrossPrivate
void updateDropIndicatorIcon(QWidget* 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);
int Area = l->property("dockWidgetArea").toInt();

View File

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

View File

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

View File

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

View File

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

View File

@ -141,7 +141,7 @@ public:
enum DockWidgetFeature
{
DockWidgetClosable = 0x01,
DockWidgetMovable = 0x02,
DockWidgetMovable = 0x02,///< this feature is not properly implemented yet and is ignored
DockWidgetFloatable = 0x04,
AllDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable,
NoDockWidgetFeatures = 0x00
@ -238,6 +238,11 @@ public:
*/
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
* the widget has not been set.

View File

@ -55,7 +55,6 @@ namespace ads
{
using tTabLabel = CElidingLabel;
using tCloseButton = QPushButton;
/**
* Private data class of CDockWidgetTab class (pimpl)
@ -72,7 +71,7 @@ struct DockWidgetTabPrivate
eDragState DragState = DraggingInactive;
CFloatingDockContainer* FloatingWidget = nullptr;
QIcon Icon;
tCloseButton* CloseButton = nullptr;
QAbstractButton* CloseButton = nullptr;
QSpacerItem* IconTextSpacer;
/**
@ -120,7 +119,24 @@ struct DockWidgetTabPrivate
*/
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
@ -143,19 +159,19 @@ void DockWidgetTabPrivate::createLayout()
TitleLabel->setObjectName("dockWidgetTabLabel");
TitleLabel->setAlignment(Qt::AlignCenter);
CloseButton = new tCloseButton();
CloseButton = createCloseButton();
CloseButton->setObjectName("tabCloseButton");
// 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 disabledPixmap = internal::createTransparentPixmap(normalPixmap, 0.25);
CloseIcon.addPixmap(disabledPixmap, QIcon::Disabled);
CloseIcon.addPixmap(normalPixmap, QIcon::Normal);
CloseIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
CloseButton->setIcon(CloseIcon);
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
CloseButton->setVisible(false);
#ifndef QT_NO_TOOLTIP
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
_this->onDockWidgetFeaturesChanged();
#ifndef QT_NO_TOOLTIP
CloseButton->setToolTip(QObject::tr("Close Tab"));
#endif
#endif
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
QFontMetrics fm(TitleLabel->font());
@ -192,9 +208,9 @@ void DockWidgetTabPrivate::moveTab(QMouseEvent* ev)
bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
{
auto dockContainer = DockWidget->dockContainer();
qDebug() << "isFloating " << dockContainer->isFloating();
qDebug() << "areaCount " << dockContainer->dockAreaCount();
qDebug() << "widgetCount " << DockWidget->dockAreaWidget()->dockWidgetsCount();
ADS_PRINT("isFloating " << dockContainer->isFloating());
ADS_PRINT("areaCount " << dockContainer->dockAreaCount());
ADS_PRINT("widgetCount " << DockWidget->dockAreaWidget()->dockWidgetsCount());
// if this is the last dock widget inside of this floating widget,
// then it does not make any sense, to make it floating because
// it is already floating
@ -205,7 +221,7 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
return false;
}
qDebug() << "startFloating";
ADS_PRINT("startFloating");
DragState = DraggingState;
QSize Size = DockArea->size();
CFloatingDockContainer* FloatingWidget = nullptr;
@ -223,7 +239,7 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
if (DraggingFloatingWidget == DraggingState)
{
FloatingWidget->startDragging(DragStartMousePosition, Size);
FloatingWidget->startDragging(DragStartMousePosition, Size, _this);
auto Overlay = DockWidget->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas);
this->FloatingWidget = FloatingWidget;
@ -250,7 +266,7 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) :
//============================================================================
CDockWidgetTab::~CDockWidgetTab()
{
qDebug() << "~CDockWidgetTab()";
ADS_PRINT("~CDockWidgetTab()");
delete d;
}
@ -327,7 +343,7 @@ void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev)
}
// 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();
}
@ -351,9 +367,10 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev)
d->DragStartMousePosition = ev->pos();
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();
auto Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested()));
Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested()));
Action->setEnabled(isClosable());
Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested()));
Menu.exec(mapToGlobal(ev->pos()));
@ -371,8 +388,10 @@ bool CDockWidgetTab::isActiveTab() const
void CDockWidgetTab::setActiveTab(bool active)
{
bool DockWidgetClosable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable);
bool TabHasCloseButton = d->testConfigFlag(CDockManager::ActiveTabHasCloseButton);
d->CloseButton->setVisible(active && DockWidgetClosable && TabHasCloseButton);
bool ActiveTabHasCloseButton = d->testConfigFlag(CDockManager::ActiveTabHasCloseButton);
bool AllTabsHaveCloseButton = d->testConfigFlag(CDockManager::AllTabsHaveCloseButton);
bool TabHasCloseButton = (ActiveTabHasCloseButton && active) | AllTabsHaveCloseButton;
d->CloseButton->setVisible(DockWidgetClosable && TabHasCloseButton);
if (d->IsActiveTab == active)
{
return;
@ -442,7 +461,7 @@ void CDockWidgetTab::setIcon(const QIcon& Icon)
d->Icon = Icon;
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);
}
}
@ -468,7 +487,8 @@ void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
// 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
// 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->startFloating(DraggingInactive);
@ -504,13 +524,15 @@ bool CDockWidgetTab::isClosable() const
//===========================================================================
void CDockWidgetTab::onDetachActionTriggered()
{
if (!d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{
return;
}
d->DragStartMousePosition = mapFromGlobal(QCursor::pos());
d->startFloating(DraggingInactive);
}
//============================================================================
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

View File

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

View File

@ -157,7 +157,11 @@ QSize CElidingLabel::minimumSizeHint() const
return QLabel::minimumSizeHint();
}
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;
}
@ -170,7 +174,11 @@ QSize CElidingLabel::sizeHint() const
return QLabel::sizeHint();
}
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;
}

View File

@ -1,21 +1,20 @@
/*******************************************************************************
** 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/>.
******************************************************************************/
** 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 FloatingDockContainer.cpp
@ -24,7 +23,6 @@
/// \brief Implementation of CFloatingDockContainer class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
@ -45,7 +43,10 @@
#include "DockWidget.h"
#include "DockOverlay.h"
#include <iostream>
#ifdef Q_OS_LINUX
#include "linux/FloatingWidgetTitleBar.h"
#include <xcb/xcb.h>
#endif
namespace ads
{
@ -55,22 +56,26 @@ static unsigned int zOrderCounter = 0;
*/
struct FloatingDockContainerPrivate
{
CFloatingDockContainer* _this;
CDockContainerWidget* DockContainer;
CFloatingDockContainer *_this;
CDockContainerWidget *DockContainer;
unsigned int zOrderIndex = ++zOrderCounter;
QPointer<CDockManager> DockManager;
eDragState DraggingState = DraggingInactive;
QPoint DragStartMousePosition;
CDockContainerWidget* DropContainer = nullptr;
CDockAreaWidget* SingleDockArea = nullptr;
CDockContainerWidget *DropContainer = nullptr;
CDockAreaWidget *SingleDockArea = nullptr;
#ifdef Q_OS_LINUX
QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr;
#endif
/**
* Private data constructor
*/
FloatingDockContainerPrivate(CFloatingDockContainer* _public);
FloatingDockContainerPrivate(CFloatingDockContainer *_public);
void titleMouseReleaseEvent();
void updateDropOverlays(const QPoint& GlobalPos);
void updateDropOverlays(const QPoint &GlobalPos);
/**
* Tests is a certain state is active
@ -84,20 +89,26 @@ struct FloatingDockContainerPrivate
{
DraggingState = StateId;
}
void setWindowTitle(const QString &Text)
{
#ifdef Q_OS_LINUX
TitleBar->setTitle(Text);
#else
_this->setWindowTitle(Text);
#endif
}
};
// struct FloatingDockContainerPrivate
//============================================================================
FloatingDockContainerPrivate::FloatingDockContainerPrivate(CFloatingDockContainer* _public) :
FloatingDockContainerPrivate::FloatingDockContainerPrivate(
CFloatingDockContainer *_public) :
_this(_public)
{
}
//============================================================================
void FloatingDockContainerPrivate::titleMouseReleaseEvent()
{
@ -107,25 +118,31 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
return;
}
if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea
|| DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea)
if (DockManager->dockAreaOverlay()->dropAreaUnderCursor()
!= InvalidDockWidgetArea
|| DockManager->containerOverlay()->dropAreaUnderCursor()
!= InvalidDockWidgetArea)
{
// Resize the floating widget to the size of the highlighted drop area
// rectangle
CDockOverlay* Overlay = DockManager->containerOverlay();
CDockOverlay *Overlay = DockManager->containerOverlay();
if (!Overlay->dropOverlayRect().isValid())
{
Overlay = DockManager->dockAreaOverlay();
}
QRect Rect = Overlay->dropOverlayRect();
int FrameWidth = (_this->frameSize().width() - _this->rect().width()) / 2;
int TitleBarHeight = _this->frameSize().height() - _this->rect().height() - FrameWidth;
int FrameWidth = (_this->frameSize().width() - _this->rect().width())
/ 2;
int TitleBarHeight = _this->frameSize().height()
- _this->rect().height() - FrameWidth;
if (Rect.isValid())
{
QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft());
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();
}
DropContainer->dropFloatingWidget(_this, QCursor::pos());
@ -135,100 +152,113 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
DockManager->dockAreaOverlay()->hideOverlay();
}
//============================================================================
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos)
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &GlobalPos)
{
if (!_this->isVisible() || !DockManager)
{
return;
}
auto Containers = DockManager->dockContainers();
CDockContainerWidget* TopContainer = nullptr;
for (auto ContainerWidget : Containers)
{
if (!ContainerWidget->isVisible())
{
continue;
}
auto Containers = DockManager->dockContainers();
CDockContainerWidget *TopContainer = nullptr;
for (auto ContainerWidget : Containers)
{
if (!ContainerWidget->isVisible())
{
continue;
}
if (DockContainer == ContainerWidget)
{
continue;
}
if (DockContainer == ContainerWidget)
{
continue;
}
QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);
if (ContainerWidget->rect().contains(MappedPos))
{
if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer))
{
TopContainer = ContainerWidget;
}
}
}
QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);
if (ContainerWidget->rect().contains(MappedPos))
{
if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer))
{
TopContainer = ContainerWidget;
}
}
}
DropContainer = TopContainer;
auto ContainerOverlay = DockManager->containerOverlay();
auto DockAreaOverlay = DockManager->dockAreaOverlay();
DropContainer = TopContainer;
auto ContainerOverlay = DockManager->containerOverlay();
auto DockAreaOverlay = DockManager->dockAreaOverlay();
if (!TopContainer)
{
ContainerOverlay->hideOverlay();
DockAreaOverlay->hideOverlay();
return;
}
if (!TopContainer)
{
ContainerOverlay->hideOverlay();
DockAreaOverlay->hideOverlay();
return;
}
int VisibleDockAreas = TopContainer->visibleDockAreaCount();
ContainerOverlay->setAllowedAreas(VisibleDockAreas > 1 ?
OuterDockAreas : AllDockAreas);
int VisibleDockAreas = TopContainer->visibleDockAreaCount();
ContainerOverlay->setAllowedAreas(
VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
DockWidgetArea ContainerArea = ContainerOverlay->showOverlay(TopContainer);
ContainerOverlay->enableDropPreview(ContainerArea != InvalidDockWidgetArea);
auto DockArea = TopContainer->dockAreaAt(GlobalPos);
if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0)
{
DockAreaOverlay->enableDropPreview(true);
DockAreaOverlay->setAllowedAreas((VisibleDockAreas == 1) ?
NoDockWidgetArea : AllDockAreas);
DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);
auto DockArea = TopContainer->dockAreaAt(GlobalPos);
if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0)
{
DockAreaOverlay->enableDropPreview(true);
DockAreaOverlay->setAllowedAreas(
(VisibleDockAreas == 1) ? NoDockWidgetArea : AllDockAreas);
DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that
// the mouse is in the title bar. If the ContainerArea is valid
// then we ignore the dock area of the dockAreaOverlay() and disable
// the drop preview
if ((Area == CenterDockWidgetArea) && (ContainerArea != InvalidDockWidgetArea))
{
DockAreaOverlay->enableDropPreview(false);
ContainerOverlay->enableDropPreview(true);
}
else
{
ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
}
}
else
{
DockAreaOverlay->hideOverlay();
}
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that
// the mouse is in the title bar. If the ContainerArea is valid
// then we ignore the dock area of the dockAreaOverlay() and disable
// the drop preview
if ((Area == CenterDockWidgetArea)
&& (ContainerArea != InvalidDockWidgetArea))
{
DockAreaOverlay->enableDropPreview(false);
ContainerOverlay->enableDropPreview(true);
}
else
{
ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
}
}
else
{
DockAreaOverlay->hideOverlay();
}
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) :
QWidget(DockManager, Qt::Window),
CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) :
tFloatingWidgetBase(DockManager),
d(new FloatingDockContainerPrivate(this))
{
d->DockManager = DockManager;
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
d->DockContainer = new CDockContainerWidget(DockManager, this);
connect(d->DockContainer, SIGNAL(dockAreasAdded()), this,
SLOT(onDockAreasAddedOrRemoved()));
connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this,
SLOT(onDockAreasAddedOrRemoved()));
d->DockContainer = new CDockContainerWidget(DockManager, this);
connect(d->DockContainer, SIGNAL(dockAreasAdded()), this, SLOT(onDockAreasAddedOrRemoved()));
connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this, SLOT(onDockAreasAddedOrRemoved()));
#ifdef Q_OS_LINUX
d->TitleBar = new CFloatingWidgetTitleBar(this);
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);
#endif
DockManager->registerFloatingWidget(this);
// We install an event filter to detect mouse release events because we
@ -237,26 +267,30 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) :
qApp->installEventFilter(this);
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget* DockArea) :
CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget *DockArea) :
CFloatingDockContainer(DockArea->dockManager())
{
d->DockContainer->addDockArea(DockArea);
#ifdef Q_OS_LINUX
d->TitleBar->enableCloseButton(isClosable());
#endif
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockWidget* DockWidget) :
CFloatingDockContainer::CFloatingDockContainer(CDockWidget *DockWidget) :
CFloatingDockContainer(DockWidget->dockManager())
{
d->DockContainer->addDockWidget(CenterDockWidgetArea, DockWidget);
#ifdef Q_OS_LINUX
d->TitleBar->enableCloseButton(isClosable());
#endif
}
//============================================================================
CFloatingDockContainer::~CFloatingDockContainer()
{
qDebug() << "~CFloatingDockContainer";
ADS_PRINT("~CFloatingDockContainer");
if (d->DockManager)
{
d->DockManager->removeFloatingWidget(this);
@ -264,27 +298,24 @@ CFloatingDockContainer::~CFloatingDockContainer()
delete d;
}
//============================================================================
CDockContainerWidget* CFloatingDockContainer::dockContainer() const
{
return d->DockContainer;
}
//============================================================================
void CFloatingDockContainer::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if ((event->type() == QEvent::ActivationChange) && isActiveWindow())
{
qDebug() << "FloatingWidget::changeEvent QEvent::ActivationChange ";
{
ADS_PRINT("FloatingWidget::changeEvent QEvent::ActivationChange ");
d->zOrderIndex = ++zOrderCounter;
return;
}
return;
}
}
//============================================================================
void CFloatingDockContainer::moveEvent(QMoveEvent *event)
{
@ -292,47 +323,52 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event)
switch (d->DraggingState)
{
case DraggingMousePressed:
d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos());
break;
d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos());
break;
case DraggingFloatingWidget:
d->updateDropOverlays(QCursor::pos());
break;
d->updateDropOverlays(QCursor::pos());
break;
default:
break;
}
}
//============================================================================
void CFloatingDockContainer::closeEvent(QCloseEvent *event)
{
qDebug() << "CFloatingDockContainer closeEvent";
ADS_PRINT("CFloatingDockContainer closeEvent");
d->setState(DraggingInactive);
if (isClosable())
{
// 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
// events anymore after a close/show cycle. The bug is reported here:
// https://bugreports.qt.io/browse/QTBUG-73295
// The following code is a workaround for Qt versions > 5.9.2 that seems
// to work
#if (QT_VERSION > 0x050902)
if (isClosable())
{
// 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
// events anymore after a close/show cycle. The bug is reported here:
// https://bugreports.qt.io/browse/QTBUG-73295
// The following code is a workaround for Qt versions > 5.9.2 that seems
// to work
// Starting from Qt version 5.12.2 this seems to work again. But
// now the QEvent::NonClientAreaMouseButtonPress function returns always
// Qt::RightButton even if the left button was pressed
#ifndef Q_OS_LINUX
#if (QT_VERSION > QT_VERSION_CHECK(5, 9, 2) && QT_VERSION < QT_VERSION_CHECK(5, 12, 2))
event->ignore();
this->hide();
#else
Super::closeEvent(event);
#endif
#else // Q_OS_LINUX
Super::closeEvent(event);
#endif
}
else
{
event->ignore();
}
}
else
{
event->ignore();
}
}
//============================================================================
void CFloatingDockContainer::hideEvent(QHideEvent *event)
{
@ -346,69 +382,78 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event)
}
}
//============================================================================
void CFloatingDockContainer::showEvent(QShowEvent *event)
{
Super::showEvent(event);
/*for (auto DockArea : d->DockContainer->openedDockAreas())
{
for (auto DockWidget : DockArea->openedDockWidgets())
{
DockWidget->setToggleViewActionChecked(true);
}
}*/
}
//============================================================================
bool CFloatingDockContainer::event(QEvent *e)
{
switch (d->DraggingState)
{
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);
}
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:
switch (e->type())
{
case QEvent::NonClientAreaMouseButtonDblClick:
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick";
d->setState(DraggingInactive);
break;
ADS_PRINT("FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick");
d->setState(DraggingInactive);
break;
case QEvent::Resize:
// If the first event after the mouse press is a resize event, then
// the user resizes the window instead of dragging it around.
// But there is one exception. If the window is maximized,
// then dragging the window via title bar will cause the widget to
// 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
// corner of the window frame or if it was caused by a windows state
// change, we check, if we are not in maximized state.
if (!isMaximized())
{
d->setState(DraggingInactive);
}
break;
// If the first event after the mouse press is a resize event, then
// the user resizes the window instead of dragging it around.
// But there is one exception. If the window is maximized,
// then dragging the window via title bar will cause the widget to
// 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
// corner of the window frame or if it was caused by a windows state
// change, we check, if we are not in maximized state.
if (!isMaximized())
{
d->setState(DraggingInactive);
}
break;
default:
break;
}
break;
break;
case DraggingFloatingWidget:
if (e->type() == QEvent::NonClientAreaMouseButtonRelease)
{
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease";
ADS_PRINT("FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease");
d->titleMouseReleaseEvent();
}
break;
break;
default:
break;
@ -420,100 +465,120 @@ bool CFloatingDockContainer::event(QEvent *e)
return QWidget::event(e);
}
//============================================================================
bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event)
{
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();
}
return false;
}
//============================================================================
void CFloatingDockContainer::startFloating(const QPoint& DragStartMousePos, const QSize& Size,
eDragState DragState)
void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos,
const QSize &Size, eDragState DragState, QWidget *MouseEventHandler)
{
#ifndef Q_OS_LINUX
Q_UNUSED(MouseEventHandler)
#endif
resize(Size);
d->setState(DragState);
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();
show();
show();
}
//============================================================================
void CFloatingDockContainer::moveFloating()
{
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);
}
//============================================================================
bool CFloatingDockContainer::isClosable() const
{
return d->DockContainer->features().testFlag(CDockWidget::DockWidgetClosable);
return d->DockContainer->features().testFlag(
CDockWidget::DockWidgetClosable);
}
//============================================================================
void CFloatingDockContainer::onDockAreasAddedOrRemoved()
{
qDebug() << "CFloatingDockContainer::onDockAreasAddedOrRemoved()";
ADS_PRINT("CFloatingDockContainer::onDockAreasAddedOrRemoved()");
auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
if (TopLevelDockArea)
{
d->SingleDockArea = TopLevelDockArea;
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle());
d->setWindowTitle(
d->SingleDockArea->currentDockWidget()->windowTitle());
connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int)));
SLOT(onDockAreaCurrentChanged(int)));
}
else
{
if (d->SingleDockArea)
{
disconnect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int)));
SLOT(onDockAreaCurrentChanged(int)));
d->SingleDockArea = nullptr;
}
this->setWindowTitle(qApp->applicationDisplayName());
d->setWindowTitle(qApp->applicationDisplayName());
}
}
//============================================================================
void CFloatingDockContainer::updateWindowTitle()
{
auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
if (TopLevelDockArea)
{
this->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle());
d->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle());
}
else
{
this->setWindowTitle(qApp->applicationDisplayName());
d->setWindowTitle(qApp->applicationDisplayName());
}
}
//============================================================================
void CFloatingDockContainer::onDockAreaCurrentChanged(int 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))
{
@ -524,27 +589,39 @@ bool CFloatingDockContainer::restoreState(QXmlStreamReader& Stream, bool Testing
return true;
}
//============================================================================
bool CFloatingDockContainer::hasTopLevelDockWidget() const
{
return d->DockContainer->hasTopLevelDockWidget();
}
//============================================================================
CDockWidget* CFloatingDockContainer::topLevelDockWidget() const
{
return d->DockContainer->topLevelDockWidget();
}
//============================================================================
QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const
{
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

View File

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

View File

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

View File

@ -45,6 +45,15 @@
#define ADS_EXPORT
#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
class QSplitter;
@ -153,6 +162,27 @@ T findParent(const QWidget* w)
*/
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 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 \
DockAreaTitleBar.h \
ElidingLabel.h
SOURCES += \
ads_globals.cpp \
DockAreaWidget.cpp \
@ -58,6 +57,12 @@ SOURCES += \
DockAreaTitleBar.cpp \
ElidingLabel.cpp
unix {
HEADERS += linux/FloatingWidgetTitleBar.h
SOURCES += linux/FloatingWidgetTitleBar.cpp
}
isEmpty(PREFIX){
PREFIX=..\installed
warning("Install Prefix not set")
@ -66,3 +71,5 @@ headers.path=$$PREFIX/include
headers.files=$$HEADERS
target.path=$$PREFIX/lib
INSTALLS += headers target
DISTFILES +=

View File

@ -63,7 +63,7 @@ ads--CDockWidget
#closeButton,
#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);
}