Merge branch 'githubuser0xFFFF:master' into master

This commit is contained in:
jporcher 2021-12-06 07:57:29 +01:00 committed by GitHub
commit 4b27af959b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 1592 additions and 327 deletions

View File

@ -6,7 +6,7 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, ubuntu-18.04, ubuntu-16.04] os: [ubuntu-latest, ubuntu-20.04, ubuntu-18.04]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -16,7 +16,7 @@ jobs:
run: | run: |
sudo apt-get update --fix-missing sudo apt-get update --fix-missing
sudo apt-get install qt5-default sudo apt-get install qt5-default
sudo apt-get install libqt5x11extras5-dev sudo apt-get install qtbase5-private-dev
- name: qmake - name: qmake
run: qmake run: qmake
- name: make - name: make

1
.gitignore vendored
View File

@ -383,3 +383,4 @@ FodyWeavers.xsd
/ build / build
/Settings.ini /Settings.ini
.vscode/settings.json .vscode/settings.json
/.settings

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795" name="Default">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="(g?cc)|([gc]\+\+)|(clang)" prefer-non-shared="true"/>
<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorMinGW" console="false" env-hash="-703253235712566569" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings MinGW" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
</project>

View File

@ -9,8 +9,6 @@ matrix:
os: linux os: linux
dist: trusty dist: trusty
group: stable group: stable
before_install:
- sudo apt-get -y install libqt5x11extras5-dev
addons: addons:
apt: apt:
sources: sources:
@ -20,8 +18,6 @@ matrix:
packages: packages:
- qt55base - qt55base
- qt55tools - qt55tools
- qt55x11extras
- libqt5x11extras5-dev
- gcc-9 - gcc-9
- g++-9 - g++-9
script: script:
@ -39,8 +35,6 @@ matrix:
services: services:
- xvfb - xvfb
compiler: gcc compiler: gcc
before_install:
- sudo apt-get -y install libqt5x11extras5-dev
addons: addons:
apt: apt:
sources: sources:
@ -50,8 +44,6 @@ matrix:
packages: packages:
- qt514base - qt514base
- qt514tools - qt514tools
- qt514x11extras
- libqt5x11extras5-dev
- gcc-9 - gcc-9
- g++-9 - g++-9
- libc6-i386 - libc6-i386
@ -73,8 +65,6 @@ matrix:
services: services:
- xvfb - xvfb
compiler: gcc compiler: gcc
before_install:
- sudo apt-get -y install libqt5x11extras5-dev
addons: addons:
apt: apt:
sources: sources:
@ -84,8 +74,6 @@ matrix:
packages: packages:
- qt514base - qt514base
- qt514tools - qt514tools
- qt514x11extras
- libqt5x11extras5-dev
- gcc-9 - gcc-9
- g++-9 - g++-9
- libc6-i386 - libc6-i386
@ -107,8 +95,6 @@ matrix:
services: services:
- xvfb - xvfb
compiler: gcc compiler: gcc
before_install:
- sudo apt-get -y install libqt5x11extras5-dev
addons: addons:
apt: apt:
sources: sources:
@ -118,8 +104,6 @@ matrix:
packages: packages:
- qt514base - qt514base
- qt514tools - qt514tools
- qt514x11extras
- libqt5x11extras5-dev
- gcc-9 - gcc-9
- g++-9 - g++-9
- libc6-i386 - libc6-i386
@ -147,8 +131,6 @@ matrix:
services: services:
- xvfb - xvfb
compiler: gcc compiler: gcc
before_install:
- sudo apt-get -y install libqt5x11extras5-dev
addons: addons:
apt: apt:
sources: sources:
@ -158,8 +140,6 @@ matrix:
packages: packages:
- qt514base - qt514base
- qt514tools - qt514tools
- qt514x11extras
- libqt5x11extras5-dev
- gcc-9 - gcc-9
- g++-9 - g++-9
- libc6-i386 - libc6-i386

View File

@ -15,6 +15,13 @@ integrated development environments (IDEs) such as Visual Studio.
## New and Noteworthy ## New and Noteworthy
The [release 3.8](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.8.0)
adds the following features:
- option to close tabs with the middle mouse button
- `DeleteContentOnClose` flag for dynamic deletion and creation of dock widget
content
The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.0) The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.0)
adds the following features: adds the following features:
@ -81,6 +88,8 @@ know it from Visual Studio.
- [ezEditor](#ezeditor) - [ezEditor](#ezeditor)
- [D-Tect X](#d-tect-x) - [D-Tect X](#d-tect-x)
- [HiveWE](#hivewe) - [HiveWE](#hivewe)
- [Ramses Composer](#ramses-composer)
- [Plot Juggler](#plot-juggler)
### Docking everywhere - no central widget ### Docking everywhere - no central widget
@ -219,7 +228,13 @@ Screenshot Ubuntu:
## Build ## Build
Open the `ads.pro` with QtCreator and start the build, that's it. The Linux build requires private header files. Make sure that they are installed:
```bash
sudo apt install qtbase5-private-dev
```
Open the `ads.pro` file with QtCreator and start the build, that's it.
You can run the demo project and test it yourself. You can run the demo project and test it yourself.
## Getting started / Example ## Getting started / Example
@ -391,3 +406,38 @@ of the open editor windows.
[learn more...](https://github.com/stijnherfst/HiveWE) [learn more...](https://github.com/stijnherfst/HiveWE)
![HiveWE](doc/showcase_hivewe.png) ![HiveWE](doc/showcase_hivewe.png)
### [Ramses Composer](https://github.com/GENIVI/ramses-composer)
Ramses Composer is the authoring tool for the open source [RAMSES](https://github.com/GENIVI/ramses)
rendering ecosystem.
Ramses is a low-level rendering engine which is optimized for embedded hardware
mobile devices, automotive ECUs, IoT electronics. Ramses was initially developed
at the BMW Group and open-sourced in 2018 as part of a collaboration initiative
with the Genivi Alliance. It is an important part of the BMW infotainment cluster
and digital portfolio.
[learn more...](https://github.com/GENIVI/ramses-composer)
![RamsesComposer](doc/showcase_ramses_composer.png)
### [Plot Juggler](https://github.com/facontidavide/PlotJuggler)
PlotJuggler is a fast, powerful and intuitive tool to visualize time series.
It makes it easy to visualize data but also to analyze it. You can manipulate
your time series using a simple and extendable Transform Editor. Some of the
highlights are:
- Simple Drag & Drop user interface.
- Load data from file.
- Connect to live streaming of data.
- Save the visualization layout and configurations to re-use them later.
- Fast OpenGL visualization.
- Can handle thousands of timeseries and millions of data points.
- Transform your data using a simple editor: derivative, moving average, integral, etc…
- PlotJuggler can be easily extended using plugins.
[read more...](https://github.com/facontidavide/PlotJuggler)
[![Plot Juggler](doc/showcase_plot_juggler.png)](https://vimeo.com/480588113#t=46s)

17
ads.pri
View File

@ -1,28 +1,25 @@
lessThan(QT_MAJOR_VERSION, 6) { CONFIG(debug, debug|release){
CONFIG(debug, debug|release){
win32 { win32 {
versionAtLeast(QT_VERSION, 5.15.0) {
LIBS += -lqtadvanceddocking
}
else {
LIBS += -lqtadvanceddockingd LIBS += -lqtadvanceddockingd
} }
}
else:mac { else:mac {
LIBS += -lqtadvanceddocking_debug LIBS += -lqtadvanceddocking_debug
} }
else { else {
LIBS += -lqtadvanceddocking LIBS += -lqtadvanceddocking
} }
}
else{
LIBS += -lqtadvanceddocking
}
} }
else { else{
# qt$$qtLibraryTarget(qtadvanceddocking) does not produce an advanceddockingd.dll file on Windows
# for Qt6 - I don't know if this is a bug and I have to investigate
LIBS += -lqtadvanceddocking LIBS += -lqtadvanceddocking
} }
unix:!macx { unix:!macx {
LIBS += -lxcb LIBS += -lxcb
QT += x11extras
} }

View File

@ -2,7 +2,4 @@ include(CMakeFindDependencyMacro)
find_dependency(Qt5Core ${REQUIRED_QT_VERSION} REQUIRED) find_dependency(Qt5Core ${REQUIRED_QT_VERSION} REQUIRED)
find_dependency(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED) find_dependency(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED)
find_dependency(Qt5Widgets ${REQUIRED_QT_VERSION} REQUIRED) find_dependency(Qt5Widgets ${REQUIRED_QT_VERSION} REQUIRED)
if(UNIX AND NOT APPLE)
find_dependency(Qt5X11Extras ${REQUIRED_QT_VERSION} REQUIRED)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/adsTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/adsTargets.cmake")

View File

@ -1,9 +1,10 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_demo VERSION ${VERSION_SHORT}) project(ads_demo VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
if(WIN32) find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
find_package(Qt5 5.5 COMPONENTS AxContainer REQUIRED) if(WIN32 AND QT_VERSION_MAJOR LESS 6)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AxContainer REQUIRED)
endif() endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(ads_demo_SRCS set(ads_demo_SRCS
@ -16,9 +17,11 @@ set(ads_demo_SRCS
) )
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS}) add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src") target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src")
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::Core
if(WIN32) Qt${QT_VERSION_MAJOR}::Gui
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::AxContainer) Qt${QT_VERSION_MAJOR}::Widgets)
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::AxContainer)
endif() endif()
target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking) target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking)
set_target_properties(AdvancedDockingSystemDemo PROPERTIES set_target_properties(AdvancedDockingSystemDemo PROPERTIES

View File

@ -79,7 +79,7 @@
#include "FloatingDockContainer.h" #include "FloatingDockContainer.h"
#include "DockComponentsFactory.h" #include "DockComponentsFactory.h"
#include "StatusDialog.h" #include "StatusDialog.h"
#include "DockSplitter.h"
/** /**
@ -417,6 +417,22 @@ void MainWindowPrivate::createContent()
DockWidget = createCalendarDockWidget(); DockWidget = createCalendarDockWidget();
DockWidget->setTabToolTip(QString("Tab ToolTip\nHodie est dies magna")); DockWidget->setTabToolTip(QString("Tab ToolTip\nHodie est dies magna"));
auto DockArea = DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea); auto DockArea = DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea);
// Now we create a action to test resizing of DockArea widget
auto Action = ui.menuTests->addAction(QString("Resize %1").arg(DockWidget->windowTitle()));
QObject::connect(Action, &QAction::triggered, [DockArea]()
{
// Resizing only works, if the Splitter is visible and has a valid
// sizes
auto Splitter = ads::internal::findParent<ads::CDockSplitter*>(DockArea);
if (!Splitter)
{
return;
}
// We change the sizes of the splitter that contains the Calendar 1 widget
// to resize the dock widget
int Width = Splitter->width();
Splitter->setSizes({Width * 2/3, Width * 1/3});
});
// Now we add a custom button to the dock area title bar that will create // Now we add a custom button to the dock area title bar that will create
// new editor widgets when clicked // new editor widgets when clicked
@ -443,13 +459,14 @@ void MainWindowPrivate::createContent()
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea); DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), BottomDockArea); DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), BottomDockArea);
auto Action = ui.menuTests->addAction(QString("Set %1 Floating").arg(DockWidget->windowTitle())); Action = ui.menuTests->addAction(QString("Set %1 Floating").arg(DockWidget->windowTitle()));
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setFloating())); DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setFloating()));
Action = ui.menuTests->addAction(QString("Set %1 As Current Tab").arg(DockWidget->windowTitle())); Action = ui.menuTests->addAction(QString("Set %1 As Current Tab").arg(DockWidget->windowTitle()));
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setAsCurrentTab())); DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setAsCurrentTab()));
Action = ui.menuTests->addAction(QString("Raise %1").arg(DockWidget->windowTitle())); Action = ui.menuTests->addAction(QString("Raise %1").arg(DockWidget->windowTitle()));
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(raise())); DockWidget->connect(Action, SIGNAL(triggered()), SLOT(raise()));
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking)) if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking))
@ -614,6 +631,9 @@ CMainWindow::CMainWindow(QWidget *parent) :
// available size of a splitter to all contained dock widgets // available size of a splitter to all contained dock widgets
// CDockManager::setConfigFlag(CDockManager::EqualSplitOnInsertion, true); // CDockManager::setConfigFlag(CDockManager::EqualSplitOnInsertion, true);
// uncomment if you would like to close tabs with the middle mouse button, web browser style
// CDockManager::setConfigFlag(CDockManager::MiddleMouseButtonClosesTab, true);
// Now create the dock manager and its content // Now create the dock manager and its content
d->DockManager = new CDockManager(this); d->DockManager = new CDockManager(this);

View File

@ -215,6 +215,17 @@ class MainWindow(MainWindowUI, MainWindowBase):
dock_widget = self.create_calendar_dock_widget() dock_widget = self.create_calendar_dock_widget()
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna") dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area) dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
# Now we create a action to test resizing of DockArea widget
action = self.menuTests.addAction("Resize {}".format(dock_widget.windowTitle()))
def action_triggered():
splitter = QtAds.internal.findParent(QtAds.CDockSplitter, dock_area)
if not splitter:
return
# We change the sizes of the splitter that contains the Calendar 1 widget
# to resize the dock widget
width = splitter.width()
splitter.setSizes([width * 2/3, width * 1/3])
action.triggered.connect(action_triggered)
# Now we add a custom button to the dock area title bar that will create # Now we add a custom button to the dock area title bar that will create
# new editor widgets when clicked # new editor widgets when clicked

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

View File

@ -27,6 +27,17 @@
- [`EqualSplitOnInsertion`](#equalsplitoninsertion) - [`EqualSplitOnInsertion`](#equalsplitoninsertion)
- [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only) - [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only)
- [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only) - [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only)
- [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab)
- [DockWidget Feature Flags](#dockwidget-feature-flags)
- [`DockWidgetClosable`](#dockwidgetclosable)
- [`DockWidgetMovable`](#dockwidgetmovable)
- [`DockWidgetFloatable`](#dockwidgetfloatable)
- [`DockWidgetDeleteOnClose`](#dockwidgetdeleteonclose)
- [`CustomCloseHandling`](#customclosehandling)
- [`DockWidgetFocusable`](#dockwidgetfocusable)
- [`DockWidgetForceCloseWithArea`](#dockwidgetforceclosewitharea)
- [`NoTab`](#notab)
- [`DeleteContentOnClose`](#deletecontentonclose)
- [Central Widget](#central-widget) - [Central Widget](#central-widget)
- [Empty Dock Area](#empty-dock-area) - [Empty Dock Area](#empty-dock-area)
- [Custom Close Handling](#custom-close-handling) - [Custom Close Handling](#custom-close-handling)
@ -464,6 +475,69 @@ If you would like to overwrite autodetection, then you can activate this flag
to force QWidget based title bars. You can overwrite autodetection and this to force QWidget based title bars. You can overwrite autodetection and this
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1. flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
### `MiddleMouseButtonClosesTab`
If the flag is set, the user can use the mouse middle button to close the tab
under the mouse. So you do not need to exactly hit the tab close button to
close tab. Just click with the middle mouse button on a tab like this is
possible in various web browsers.
![MiddleMouseButtonClosesTab true](cfg_flag_MiddleMouseButtonClosesTab.gif)
## DockWidget Feature Flags
### `DockWidgetClosable`
If set, the dock widget will have a close button.
### `DockWidgetMovable`
If a dock widget is movable, then it and can be moved to a new position in the
current dock container. Disable this flag to prevent moving of a dock widget
via mouse. If the `OpaqueUndocking` configuration flag is set, then dock widgets
are immediately undocked into floating widgets. That means, moving is only
possible in this case, if the dock widget is also floatable (feature flag
`DockWidgetFloatable` is set).
### `DockWidgetFloatable`
If set, a dock widget can be dragged into a floating window.
### `DockWidgetDeleteOnClose`
Deletes the dock widget and its content when it is closed.
### `CustomCloseHandling`
Clicking the close button will not close the dock widget but emits the
`closeRequested()` signal instead. This allows the application to implement
a custom close handling.
### `DockWidgetFocusable`
If this is enabled, a dock widget can get focus highlighting.
### `DockWidgetForceCloseWithArea`
A dock widget will be closed when the dock area hosting it is closed. If the
`DockWidgetDeleteOnClose` feature is enabled for a dock widget, then it will
be deleted, if the user clicks the close button of this dock widget. If the
user clicks the close button of the dock area that contains this widget,
then only the visibility of the dock widget is toggled. If this feature flag
is set, the closing the dock area also closes the dock widget. That means, if
the dock widget feature `DockWidgetDeleteOnClose` is set for the dock widgets
in a dock area, then all dock widgets will be deleted if the dock area is closed.
### `NoTab`
A dock widget tab will never be shown if this flag is set.
### `DeleteContentOnClose`
Deletes only the contained widget on close, keeping the dock widget intact and
in place. Attempts to rebuild the contents widget on show if there is a widget
factory set. See [issue #365](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/pull/365) for more details.
## Central Widget ## Central Widget
The Advanced Docking System has been developed to overcome the limitations of The Advanced Docking System has been developed to overcome the limitations of

View File

@ -5,3 +5,4 @@ add_subdirectory(sidebar)
add_subdirectory(deleteonclose) add_subdirectory(deleteonclose)
add_subdirectory(centralwidget) add_subdirectory(centralwidget)
add_subdirectory(emptydockarea) add_subdirectory(emptydockarea)
add_subdirectory(dockindock)

View File

@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_centralwidget VERSION ${VERSION_SHORT}) project(ads_example_centralwidget VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(CentralWidgetExample WIN32 add_executable(CentralWidgetExample WIN32
main.cpp main.cpp
@ -9,7 +10,9 @@ add_executable(CentralWidgetExample WIN32
) )
target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking) target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking)
target_link_libraries(CentralWidgetExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(CentralWidgetExample PROPERTIES set_target_properties(CentralWidgetExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON

View File

@ -13,18 +13,6 @@ from PyQtAds import QtAds
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui') UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
import demo_rc # pyrcc5 demo\demo.qrc -o examples\centralWidget\demo_rc.py
def svg_icon(filename: str):
'''Helper function to create an SVG icon'''
# This is a workaround, because because in item views SVG icons are not
# properly scaled and look blurry or pixelate
icon = QIcon(filename)
icon.addPixmap(icon.pixmap(92))
return icon
class MainWindow(MainWindowUI, MainWindowBase): class MainWindow(MainWindowUI, MainWindowBase):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -46,27 +34,26 @@ class MainWindow(MainWindowUI, MainWindowBase):
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas) central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
# create other dock widgets # create other dock widgets
file_tree = QTreeView()
file_tree.setFrameShape(QFrame.NoFrame)
file_model = QFileSystemModel(file_tree)
file_model.setRootPath(QDir.currentPath())
file_tree.setModel(file_model)
data_dock_widget = QtAds.CDockWidget("File system")
data_dock_widget.setWidget(file_tree)
data_dock_widget.resize(150, 250)
data_dock_widget.setMinimumSize(100, 250)
file_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, data_dock_widget, central_dock_area)
self.menuView.addAction(data_dock_widget.toggleViewAction())
table = QTableWidget() table = QTableWidget()
table.setColumnCount(3) table.setColumnCount(3)
table.setRowCount(10) table.setRowCount(10)
table_dock_widget = QtAds.CDockWidget("Table") table_dock_widget = QtAds.CDockWidget("Table 1")
table_dock_widget.setWidget(table) table_dock_widget.setWidget(table)
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
table_dock_widget.resize(250, 150) table_dock_widget.resize(250, 150)
table_dock_widget.setMinimumSize(200, 150) table_dock_widget.setMinimumSize(200, 150)
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, file_area) table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
self.menuView.addAction(table_dock_widget.toggleViewAction())
table = QTableWidget()
table.setColumnCount(5)
table.setRowCount(1020)
table_dock_widget = QtAds.CDockWidget("Table 2")
table_dock_widget.setWidget(table)
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
table_dock_widget.resize(250, 150)
table_dock_widget.setMinimumSize(200, 150)
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
self.menuView.addAction(table_dock_widget.toggleViewAction()) self.menuView.addAction(table_dock_widget.toggleViewAction())
properties_table = QTableWidget() properties_table = QTableWidget()
@ -76,7 +63,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
properties_dock_widget.setWidget(properties_table) properties_dock_widget.setWidget(properties_table)
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
properties_dock_widget.resize(250, 150) properties_dock_widget.resize(250, 150)
properties_dock_widget.setMinimumSize(200,150) properties_dock_widget.setMinimumSize(200, 150)
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area) self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
self.menuView.addAction(properties_dock_widget.toggleViewAction()) self.menuView.addAction(properties_dock_widget.toggleViewAction())
@ -84,7 +71,6 @@ class MainWindow(MainWindowUI, MainWindowBase):
def create_perspective_ui(self): def create_perspective_ui(self):
save_perspective_action = QAction("Create Perspective", self) save_perspective_action = QAction("Create Perspective", self)
save_perspective_action.setIcon(svg_icon(":/adsdemo/images/picture_in_picture.svg"))
save_perspective_action.triggered.connect(self.save_perspective) save_perspective_action.triggered.connect(self.save_perspective)
perspective_list_action = QWidgetAction(self) perspective_list_action = QWidgetAction(self)
self.perspective_combobox = QComboBox(self) self.perspective_combobox = QComboBox(self)

View File

@ -1,13 +1,16 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_deleteonclose VERSION ${VERSION_SHORT}) project(ads_example_deleteonclose VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(DeleteOnCloseTest WIN32 add_executable(DeleteOnCloseTest WIN32
main.cpp main.cpp
) )
target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(DeleteOnCloseTest PRIVATE qtadvanceddocking) target_link_libraries(DeleteOnCloseTest PRIVATE qtadvanceddocking)
target_link_libraries(DeleteOnCloseTest PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(DeleteOnCloseTest PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(DeleteOnCloseTest PROPERTIES set_target_properties(DeleteOnCloseTest PROPERTIES
AUTOMOC ON AUTOMOC ON
CXX_STANDARD 14 CXX_STANDARD 14

View File

@ -41,12 +41,12 @@ int main(int argc, char *argv[])
now->widget()->setFocus(); now->widget()->setFocus();
}); });
QAction *action = new QAction("New Delete On Close", &w); QAction *action = new QAction("New [DockWidgetDeleteOnClose]", &w);
w.menuBar()->addAction(action); w.menuBar()->addAction(action);
int i = 0; int i = 0;
QObject::connect(action, &QAction::triggered, [&]() { QObject::connect(action, &QAction::triggered, [&]() {
auto dw = new ads::CDockWidget(QStringLiteral("test doc %1").arg(i++), &w); auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DockWidgetDeleteOnClose]").arg(i++), &w);
auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw); auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw);
dw->setWidget(editor); dw->setWidget(editor);
dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true); dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
@ -54,6 +54,26 @@ int main(int argc, char *argv[])
qDebug() << "doc dock widget created!" << dw << area; qDebug() << "doc dock widget created!" << dw << area;
}); });
auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DeleteContentOnClose]").arg(i++), &w);
auto editor = new QTextEdit(QStringLiteral("recreated lorem ipsum......"), dw);
dw->setWidget(editor);
dw->setFeature(ads::CDockWidget::DeleteContentOnClose, true);
dw->setWidgetFactory([](QWidget* dw)
{
static int timesRecreated = 0;
return new QTextEdit(QStringLiteral("recreated lorem ipsum... times %1").arg(++timesRecreated), dw);
});
auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw);
qDebug() << "DeleteContentOnClose dock widget created!" << dw << area;
action = new QAction("Toggle [DeleteContentOnClose]", &w);
w.menuBar()->addAction(action);
QObject::connect(action, &QAction::triggered, [dw]() {
dw->toggleView(dw->isClosed());
qDebug() << QString("dock widget %1! contents widget %2!").arg(dw->isClosed() ? "closed" : "open", dw->widget() ? "created" : "deleted");
});
action = new QAction("New", &w); action = new QAction("New", &w);
w.menuBar()->addAction(action); w.menuBar()->addAction(action);
QObject::connect(action, &QAction::triggered, [&]() { QObject::connect(action, &QAction::triggered, [&]() {

View File

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_simple VERSION ${VERSION_SHORT}) project(ads_example_dockindock VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(DockInDock WIN32 add_executable(DockInDockExample WIN32
dockindock.cpp dockindock.cpp
dockindockmanager.cpp dockindockmanager.cpp
perspectiveactions.cpp perspectiveactions.cpp
@ -10,10 +11,12 @@ add_executable(DockInDock WIN32
main.cpp main.cpp
mainframe.cpp mainframe.cpp
) )
target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(DockInDockExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(SimpleExample PRIVATE qtadvanceddocking) target_link_libraries(DockInDockExample PRIVATE qtadvanceddocking)
target_link_libraries(SimpleExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(DockInDockExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
set_target_properties(SimpleExample PROPERTIES Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(DockInDockExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON
AUTOUIC ON AUTOUIC ON

View File

@ -0,0 +1,203 @@
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,
QInputDialog, QMenu, QLineEdit)
from PyQt5.QtGui import QIcon
from PyQtAds import QtAds
from dockindockmanager import DockInDockManager
from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction
class DockInDockWidget(QWidget):
def __init__(self, parent, perspectives_manager: 'PerspectivesManager', can_create_new_groups: bool = False, top_level_widget = None):
super().__init__(parent)
if top_level_widget is not None:
self.__can_create_new_groups = top_level_widget.can_create_new_groups
else:
self.__can_create_new_groups = can_create_new_groups
self.__top_level_dock_widget = top_level_widget if top_level_widget else self
self.__perspectives_manager = perspectives_manager
self.__new_perspective_default_name: str = ''
layout = QVBoxLayout(self)
layout.setContentsMargins(0,0,0,0)
self.__mgr = DockInDockManager(self)
layout.addWidget(self.__mgr)
def getManager(self) -> 'DockInDockManager':
return self.__mgr
def getTopLevelDockWidget(self) -> 'DockInDockWidget':
return self.__top_level_dock_widget
def canCreateNewGroups(self) -> bool:
return self.__can_create_new_groups
def getPerspectivesManager(self) -> 'PerspectivesManager':
return self.__perspectives_manager
def addTabWidget(self, widget: QWidget, name: str, after: QtAds.CDockAreaWidget, icon = QIcon()) -> QtAds.CDockAreaWidget:
for existing in self.getTopLevelDockWidget().getManager().allDockWidgets(True, True):
if existing[1].objectName() == name:
QMessageBox.critical(self, "Error", "Name '" + name + "' already in use")
return
dock_widget = QtAds.CDockWidget(name)
dock_widget.setWidget(widget)
dock_widget.setIcon(icon)
# Add the dock widget to the top dock widget area
return self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, after)
def isTopLevel(self) -> bool:
return not self.objectName()
def getGroupNameError(self, group_name: str) -> str:
if not group_name:
return "Group must have a non-empty name"
dock_managers = self.__mgr.allManagers(True, True)
for mgr in dock_managers:
if mgr.getGroupName() == group_name:
return "Group name '" + group_name + "' already used"
return ""
def createGroup(self, group_name: str, insert_pos: QtAds.CDockAreaWidget, icon = QIcon()) -> 'DockInDockWidget':
error = self.getGroupNameError(group_name)
if error:
QMessageBox.critical(None, "Error", error)
return
child = DockInDockWidget(self, self.__top_level_dock_widget, self.__perspectives_manager)
child.setObjectName(group_name)
dock_widget = QtAds.CDockWidget(group_name)
dock_widget.setWidget(child)
dock_widget.setIcon(icon)
insert_pos = self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, insert_pos)
return child, insert_pos
def destroyGroup(self, widget_to_remove: 'DockInDockWidget') -> None:
top_level_widget = widget_to_remove.getTopLevelDockWidget()
if top_level_widget and top_level_widget != widget_to_remove:
for dock_widget in widget_to_remove.getManager().getWidgetsInGUIOrder(): #don't use allDockWidgets to preserve sub-groups
MoveDockWidgetAction.move(dock_widget, top_level_widget.getManager())
assert not widget_to_remove.getManager().allDockWidgets(True, True)
# find widget's parent:
for dock_widget in top_level_widget.getManager().allDockWidgets(True, True):
if dockwidget[1].widget() == widget_to_remove:
dockwidget[0].removeDockWidget(dockwidget[1])
del dockwidget[1]
# delete widgetToRemove; automatically deleted when dockWidget is deleted
widget_to_remove = None
break
assert widget_to_remove == None
else:
assert False
def attachViewMenu(self, menu: QMenu) -> None:
menu.aboutToShow.connect(self.autoFillAttachedViewMenu)
def autoFillAttachedViewMenu(self) -> None:
menu = self.sender()
if menu:
menu.clear()
self.setupViewMenu(menu)
else:
assert False
def setupViewMenu(self, menu):
dock_managers = self.__mgr.allManagers(True, True)
has_perspectives_menu = False
if self.getTopLevelDockWidget() == self:
has_perspectives_menu = (self.__perspectives_manager != None)
else:
assert False
organize = menu
if has_perspectives_menu:
organize = menu.addMenu("Organize")
self.setupMenu(organize, dock_managers)
if has_perspectives_menu:
perspectives = menu.addMenu("Perspectives")
self.fillPerspectivesMenu(perspectives)
def setupMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
self.__mgr.fillViewMenu(menu, move_to)
menu.addSeparator()
move_menu = menu.addMenu("Move")
self.__mgr.fillMoveMenu(move_menu, move_to)
def fillPerspectivesMenu(self, menu: QMenu):
menu.addAction("Create perspective...", self.createPerspective)
perspectives_names = []
if self.__perspectives_manager:
perspectives_names = self.__perspectives_manager.perspectiveNames()
if perspectives_names:
load = menu.addMenu("Load perspective")
for name in perspectives_names:
load.addAction(LoadPerspectiveAction(load, name, self))
remove = menu.addMenu("Remove perspective")
for name in perspectives_names:
remove.addAction(RemovePerspectiveAction(remove, name, self))
def setNewPerspectiveDefaultName(default_name: str) -> None:
self.__new_perspective_default_name = default_name
def createPerspective(self) -> None:
if not self.__perspectives_manager:
return
name = self.__new_perspective_default_name
if self.__new_perspective_default_name:
index = 2
while name in self.__perspectives_manager.perspectiveNames():
name = f"{self.__new_perspective_default_name}({index})"
index += 1
while True:
name, ok = QInputDialog.getText(None, "Create perspective", "Enter perspective name", QLineEdit.Normal, name)
if ok:
if not name:
QMessageBox.critical(None, "Error", "Perspective name cannot be empty")
continue
elif name in self.__perspectives_manager.perspectiveNames():
if QMessageBox.critical(None, "Error", f"Perspective '{name}' already exists, overwrite it?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.No:
continue
self.__perspectives_manager.addPerspective(name, self)
break
else:
break
def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, tab: str = '', suffix: str = '') -> str:
if widget is not None:
as_mgr = DockInDockManager.dockInAManager(widget)
if as_mgr:
as_mgr.parent().dumpStatus(tab=tab)
else:
echo(tab + widget.objectName() + suffix)
else:
echo(tab + "Group: " + self.getManager().getGroupName())
tab += " "
visible_widgets = set()
for widget in self.getManager().getWidgetsInGUIOrder():
visible_widgets.add(widget)
self.dumpStatus(widget=widget, tab=tab)
for closed in self.getManager().dockWidgetsMap().values():
if not closed in visible_widgets:
self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)")

View File

@ -0,0 +1,214 @@
from PyQt5.QtWidgets import QAction, QMenu, QInputDialog, QLineEdit
from PyQt5.QtCore import QSettings
from PyQtAds import QtAds
CHILD_PREFIX = "Child-"
class DockInDockManager(QtAds.CDockManager):
def __init__(self, parent: 'DockInDockWidget'):
super().__init__()
self.__parent = parent
def parent(self) -> 'DockInDockWidget':
return self.__parent
def fillViewMenu(self, menu: QMenu, move_to: 'dict[DockInDockManager]') -> None:
from dockindock import DockInDockWidget # Prevent cyclic import
widgets_map = self.dockWidgetsMap()
for key, value in widgets_map.items():
widget = value.widget()
action = value.toggleViewAction()
if isinstance(widget, DockInDockWidget):
sub_menu = menu.addMenu(key)
sub_menu.addAction(action)
sub_menu.addSeparator()
widget.setupMenu(sub_menu, move_to)
else:
menu.addAction(action)
if self.parent().canCreateNewGroups():
# see how this works, to create it in the right place,
# and also to have load perspective work when some groups are missing
menu.addSeparator()
menu.addAction(CreateChildDockAction(self.__parent, menu))
if self.parent().getTopLevelDockWidget().getManager() != self:
menu.addAction(DestroyGroupAction( self.parent, menu))
def fillMoveMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
widgets_map = self.dockWidgetsMap()
for key, value in widgets_map.items():
sub_menu = menu.addMenu(key)
for mgr in move_to:
# iterate over all possible target managers
if mgr == self:
pass # if dock is already in mgr, no reason to move it there
elif mgr == DockInDockManager.dockInAManager(value):
pass # if target is the group itself, can't move it there, would make no sense
else:
sub_menu.addAction(MoveDockWidgetAction(value, mgr, sub_menu))
def addPerspectiveRec(self, name: str) -> None:
managers = self.allManagers(True, True)
for child in managers:
child.addPerspective(name)
def openPerspectiveRec(self, name: str) -> None:
managers = self.allManagers(True, True)
for child in managers:
child.openPerspective(name)
def getGroupName(self) -> str:
return self.parent().objectName()
def getPersistGroupName(self) -> str:
group = "Top"
if self.getGroupName():
group = CHILD_PREFIX + self.getGroupName()
return group
def getGroupNameFromPersistGroupName(self, persist_group_name) -> str:
if persist_group_name.startswith(CHILD_PREFIX):
persist_group_name = persist_group_name[len(CHILD_PREFIX):]
else:
assert False
return persist_group_name
def loadPerspectivesRec(self, settings: QSettings) -> None:
children = self.allManagers(True, True)
for mgr in children:
settings.beginGroup(mgr.getPersistGroupName())
mgr.loadPerspectives(settings)
settings.endGroup()
def savePerspectivesRec(self, settings: QSettings) -> None:
children = self.allManagers(True, True)
for mgr in children:
settings.beginGroup(mgr.getPersistGroupName())
mgr.savePerspectives(settings)
settings.endGroup()
def removePerspectivesRec(self, settings: QSettings) -> None:
children = self.allManagers(True, True)
for mgr in children:
child.removePerspectives(child.perspectiveNames())
@staticmethod
def dockInAManager(widget) -> 'DockInDockManager':
from dockindock import DockInDockWidget # Prevent cyclic import
dock_widget = widget.widget() if widget else None
return dock_widget.getManager() if isinstance(dock_widget, DockInDockWidget) else None
def childManagers(self, managers: 'list[DockInDockManager]', rec: bool) -> None:
widgets = self.getWidgetsInGUIOrder()
for widget in widgets:
as_mgr = DockInDockManager.dockInAManager(widget)
if as_mgr:
managers.append(as_mgr)
if rec:
as_mgr.childManagers(managers, rec)
def allManagers(self, include_self: bool, rec: bool) -> 'list[DockInDockManager]':
managers = []
if include_self:
managers.append(self)
self.childManagers(managers, rec)
return managers
def allDockWidgets(self, include_self: bool, rec: bool) -> 'list[tuple[DockInDockManager, QtAds.CDockWidget]]':
widgets = []
for mgr in self.allManagers(include_self, rec):
for widget in mgr.getWidgetsInGUIOrder():
widgets.append((mgr, widget))
return widgets
def getGroupContents(self) -> 'dict[str, list[str]]':
result = {}
managers = self.allManagers(True, True)
for mgr in managers:
result[mgr.getPersistGroupName()] = mgr.dockWidgetsMap().keys()
return result
def getInsertDefaultPos(self) -> QtAds.CDockAreaWidget:
default_pos = None
if self.dockAreaCount() != 0:
default_pos = self.dockArea(self.dockAreaCount()-1)
return default_pos
def getWidgetsInGUIOrder(self) -> 'list[QtAds.CDockWidget]':
result = []
for i in range(self.dockAreaCount()):
for widget in self.dockArea(i).dockWidgets():
result.append(widget)
return result
class CreateChildDockAction(QAction):
def __init__(self, dock_in_dock: 'DockInDockWidget', menu: QMenu):
super().__init__("New group...", menu)
self.__dock_in_dock = dock_in_dock
self.triggered.connect(self.createGroup)
def createGroup(self) -> None:
name = ""
while True:
name, ok = QInputDialog.getText(None, self.text(), "Enter group name", QLineEdit.Normal, name)
if ok:
error = ""
if self.__dock_in_dock.getTopLevelDockWidget():
error = self.__dock_in_dock.getTopLevelDockWidget().getGroupNameError(name)
else:
assert False
if not error:
self.__dock_in_dock.createGroup(name, None)
break
else:
QMessageBox.critical(None, "Error", error)
continue
else:
break
class DestroyGroupAction(QAction):
def __init__(self, widget: 'DockInDockWidget', menu: QMenu):
super().__init__("Destroy" + widget.getManager().getGroupName(), menu)
self.__widget = widget
self.triggered.connect(self.destroyGroup)
def destroyGroup(self) -> None:
self.__widget.getTopLevelDockWidget().destroyGroup(self.__widget)
class MoveDockWidgetAction(QAction):
def __init__(self, widget: 'DockInDockWidget', move_to: DockInDockManager, menu: QMenu):
super().__init__(menu)
self.__widget = widget
self.__move_to = move_to
if move_to.parent().isTopLevel():
self.setText("To top")
else:
self.setText(f"To {move_to.parent().objectName()}")
self.triggered.connect(self._move)
def _move(self) -> None:
self.move(self.__widget, self.__move_to)
def move(self, widget: QtAds.CDockWidget, move_to: QtAds.CDockManager) -> None:
if widget and move_to:
widget.dockManager().removeDockWidget(widget)
move_to.addDockWidget(QtAds.CenterDockWidgetArea, widget, move_to.getInsertDefaultPos())
else:
assert False

View File

@ -0,0 +1,72 @@
import sys
import os
import atexit
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
from PyQtAds import QtAds
from perspectives import PerspectivesManager
from dockindock import DockInDockWidget
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.perspectives_manager = PerspectivesManager("persist")
self.resize(400, 400)
self.dock_manager = DockInDockWidget(self, self.perspectives_manager, can_create_new_groups=True)
self.setCentralWidget(self.dock_manager)
self.dock_manager.attachViewMenu(self.menuBar().addMenu("View"))
previous_dock_widget = None
for i in range(3):
l = QLabel()
l.setWordWrap(True)
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
previous_dock_widget = self.dock_manager.addTabWidget(l, f"Top label {i}", previous_dock_widget)
last_top_level_dock = previous_dock_widget
for j in range(2):
group_manager, _ = self.dock_manager.createGroup(f"Group {j}", last_top_level_dock)
previous_dock_widget = None
for i in range(3):
# Create example content label - this can be any application specific widget
l = QLabel()
l.setWordWrap(True)
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
previous_dock_widget = group_manager.addTabWidget(l, f"ZInner {j}/{i}", previous_dock_widget)
# create sub-group
sub_group, _ = group_manager.createGroup(f"SubGroup {j}", previous_dock_widget)
previous_dock_widget = None
for i in range(3):
# Create example content label - this can be any application specific widget
l = QLabel()
l.setWordWrap(True)
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
previous_dock_widget = sub_group.addTabWidget(l, f"SubInner {j}/{i}", previous_dock_widget)
self.perspectives_manager.loadPerspectives()
atexit.register(self.cleanup)
def cleanup(self):
self.perspectives_manager.savePerspectives()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

View File

@ -0,0 +1,25 @@
from PyQt5.QtWidgets import QAction, QMenu
class LoadPerspectiveAction(QAction):
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
super().__init__(name, parent)
self.name = name
self.dock_manager = dock_manager
self.triggered.connect(self.load)
def load(self):
self.dock_manager.getPerspectivesManager().openPerspective(self.name, self.dock_manager)
class RemovePerspectiveAction(QAction):
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
super().__init__(name, parent)
self.name = name
self.dock_manager = dock_manager
self.triggered.connect(self.remove)
def remove(self):
self.dock_manager.getPerspectivesManager().removePerspective(self.name)

View File

@ -0,0 +1,203 @@
import os
import tempfile
import shutil
import atexit
from PyQt5.QtCore import pyqtSignal, QSettings, QObject
from PyQtAds import QtAds
from dockindockmanager import DockInDockManager
from dockindock import DockInDockWidget
GROUP_PREFIX = "Group"
def findWidget(name, managers: 'list[DockInDockManager]') -> QtAds.CDockWidget:
for mgr in managers:
widget = mgr.findDockWidget(name)
if widget:
return widget
class PerspectiveInfo:
# Partially bypass ADS perspective management, store list here
# and then ADS will only have one perspective loaded
# this is because all docking widgets must exist when a perspective is loaded
# we will guarantee that!
settings = QSettings()
groups: 'dict[str, list[str]]' = {}
class PerspectivesManager(QObject):
perspectivesListChanged = pyqtSignal()
openingPerspective = pyqtSignal()
openedPerspective = pyqtSignal()
def __init__(self, perspectives_folder):
super().__init__()
self.__perspectives_folder = perspectives_folder
self.__perspectives = {}
atexit.register(self.cleanup)
def cleanup(self):
for perspective in self.__perspectives.values():
filename = perspective.settings.fileName()
try:
os.remove(filename)
except FileNotFoundError:
pass
def perspectiveNames(self) -> 'list[str]':
return self.__perspectives.keys()
def addPerspective(self, name: str, widget: DockInDockWidget) -> None:
if self.__perspectives_folder:
self.__perspectives[name] = perspective = PerspectiveInfo()
perspective.settings = self.getSettingsObject(self.getSettingsFileName(name, True))
perspective.groups = widget.getManager().getGroupContents()
# save perspective internally
widget.getManager().addPerspectiveRec(name)
# store it in QSettings object
widget.getManager().savePerspectivesRec(perspective.settings)
# remove internal perspectives
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
self.perspectivesListChanged.emit()
def openPerspective(name: str, widget: DockInDockWidget) -> None:
assert widget.getTopLevelDockWidget() == widget
if self.__perspectives_folder:
if name in self.__perspectives:
self.openingPerspective.emit()
if widget.canCreateNewGroups():
cur_groups = widget.getManager().allManagers(True, True)
for group in self.__perspectives[name].groups.keys():
found = False
for curgroup in cur_groups:
if curgroup.getPerspectiveGroupName() == group:
found = True
break
if not found:
group = DockInDockManager.getGroupNameFromPersistGroupName(group)
# restore group in file but not in GUI yet
widget.createGroup(group, None)
cur_groups = widget.getManager().allManagers(False, True)
for curgroup in cur_groups:
if curgroup.getPersistGroupName() not in self.__perspectives[name].groups.keys():
widget.destroyGroup(curgroup.parent())
managers = widget.getManager().allManagers(True, True)
for group in self.__perspectives[name].groups().keys():
for mgr in managers:
if mgr.getPersistGroupName() == group:
for widget_name in self.__perspectives[name].groups[group]:
widget = findWidget(widget_name, [mgr])
if widget:
pass # OK, widget is already in the good manager!
else:
widget = findWidget(widget_name, managers)
if widget:
# move dock widget in the same group as it used to be when perspective was saved
# this guarantee load/open perspectives will work smartly
MoveDockWidgetAction.move(widget, mgr)
# internally load perspectives from QSettings
widget.getManager().loadPerspectivesRec(self.__perspectives[name].settings)
# load perspective (update GUI)
widget.getManager().openPerspectiveRec(name)
# remove internal perspectives
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
self.openedPerspective().emit()
else:
assert False
def removePerspectives(self) -> None:
self.__perspectives.clear()
self.perspectivesListChanged.emit()
def removePerspective(self, name: str) -> None:
del self.__perspectives[name]
self.perspectivesListChanged.emit()
def getSettingsFileName(self, perspective: str, temp: bool) -> str:
name = "perspectives.ini" if not perspective else f"perspectives_{perspective + '.tmp' if temp else perspective + '.ini'}"
return os.path.join(self.__perspectives_folder, name)
def getSettingsObject(self, file_path: str) -> QSettings:
return QSettings(file_path, QSettings.IniFormat)
def loadPerspectives(self) -> None:
if self.__perspectives_folder:
tempfile.mktemp(dir=self.__perspectives_folder)
self.__perspectives.clear()
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
debug = main_settings.fileName()
size = main_settings.beginReadArray("Perspectives")
for i in range(0, size):
main_settings.setArrayIndex(i)
perspective = main_settings.value("Name")
if perspective:
to_load = self.getSettingsFileName(perspective, False)
loaded = self.getSettingsFileName(perspective, True)
try:
os.remove(loaded)
except FileNotFoundError:
pass
if not shutil.copy(to_load, loaded):
assert False
self.__perspectives[perspective] = PerspectiveInfo()
self.__perspectives[perspective].settings = self.getSettingsObject(loaded)
# load group info:
main_settings.beginGroup(GROUP_PREFIX)
for key in main_settings.allKeys():
self.__perspectives[perspective].groups[key] = main_settings.value(key)
main_settings.endGroup()
else:
assert False
main_settings.endArray()
self.perspectivesListChanged.emit()
def savePerspectives(self) -> None:
if self.__perspectives_folder:
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
# Save list of perspective and group organization
main_settings.beginWriteArray("Perspectives", len(self.__perspectives))
for i, perspective in enumerate(self.__perspectives.keys()):
main_settings.setArrayIndex(i)
main_settings.setValue("Name", perspective)
main_settings.beginGroup(GROUP_PREFIX)
for group in self.__perspectives[perspective].groups.keys():
main_settings.setValue(group, list(self.__perspectives[perspective].groups[group]))
main_settings.endGroup()
main_settings.endArray()
# Save perspectives themselves
for perspective_name in self.__perspectives.keys():
to_save = self.getSettingsFileName(perspective_name, False)
settings = self.__perspectives[perspective_name].settings
settings.sync()
try:
os.remove(to_save)
except FileNotFoundError:
pass
if not shutil.copy(settings.fileName(), to_save):
assert False

View File

@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_centralwidget VERSION ${VERSION_SHORT}) project(ads_example_centralwidget VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(EmptyDockAreaExample WIN32 add_executable(EmptyDockAreaExample WIN32
main.cpp main.cpp
@ -9,7 +10,9 @@ add_executable(EmptyDockAreaExample WIN32
) )
target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking) target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking)
target_link_libraries(EmptyDockAreaExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(EmptyDockAreaExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(EmptyDockAreaExample PROPERTIES set_target_properties(EmptyDockAreaExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON

View File

@ -0,0 +1,108 @@
import sys
import os
from PyQt5 import uic
from PyQt5.QtCore import Qt, QSignalBlocker
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QComboBox, QTableWidget,
QAction, QWidgetAction, QSizePolicy, QInputDialog)
from PyQt5.QtGui import QCloseEvent
from PyQtAds import QtAds
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
class CMainWindow(MainWindowUI, MainWindowBase):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.OpaqueSplitterResize, True)
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.XmlCompressionEnabled, False)
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
self.dock_manager = QtAds.CDockManager(self)
# Set central widget
label = QLabel()
label.setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.")
label.setAlignment(Qt.AlignCenter)
central_dock_widget = QtAds.CDockWidget("CentralWidget")
central_dock_widget.setWidget(label)
central_dock_widget.setFeature(QtAds.CDockWidget.NoTab, True)
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
# create other dock widgets
table = QTableWidget()
table.setColumnCount(3)
table.setRowCount(10)
table_dock_widget = QtAds.CDockWidget("Table 1")
table_dock_widget.setWidget(table)
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
table_dock_widget.resize(250, 150)
table_dock_widget.setMinimumSize(200,150)
self.dock_manager.addDockWidgetTabToArea(table_dock_widget, central_dock_area)
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
self.menuView.addAction(table_dock_widget.toggleViewAction())
table = QTableWidget()
table.setColumnCount(5)
table.setRowCount(1020)
table_dock_widget = QtAds.CDockWidget("Table 2")
table_dock_widget.setWidget(table)
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
table_dock_widget.resize(250, 150)
table_dock_widget.setMinimumSize(200,150)
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
self.menuView.addAction(table_dock_widget.toggleViewAction())
properties_table = QTableWidget()
properties_table.setColumnCount(3)
properties_table.setRowCount(10)
properties_dock_widget = QtAds.CDockWidget("Properties")
properties_dock_widget.setWidget(properties_table)
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
properties_dock_widget.resize(250, 150)
properties_dock_widget.setMinimumSize(200,150)
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
self.menuView.addAction(properties_dock_widget.toggleViewAction())
self.createPerspectiveUi()
def createPerspectiveUi(self):
save_perspective_action = QAction("Create Perspective", self)
save_perspective_action.triggered.connect(self.savePerspective)
perspective_list_action = QWidgetAction(self)
self.perspective_combo_box = QComboBox(self)
self.perspective_combo_box.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.perspective_combo_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
self.perspective_combo_box.activated[str].connect(self.dock_manager.openPerspective)
perspective_list_action.setDefaultWidget(self.perspective_combo_box)
self.toolBar.addSeparator()
self.toolBar.addAction(perspective_list_action)
self.toolBar.addAction(save_perspective_action)
def savePerspective(self):
perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter unique name:")
if not perspective_name or not ok:
return
self.dock_manager.addPerspective(perspective_name)
blocker = QSignalBlocker(self.perspective_combo_box)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
self.perspective_combo_box.setCurrentText(perspective_name)
def closeEvent(self, event: QCloseEvent):
# Delete dock manager here to delete all floating widgets. This ensures
# that all top level windows of the dock manager are properly closed
self.dock_manager.deleteLater()
super().closeEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = CMainWindow()
w.show()
app.exec_()

View File

@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_sidebar VERSION ${VERSION_SHORT}) project(ads_example_sidebar VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(SidebarExample WIN32 add_executable(SidebarExample WIN32
main.cpp main.cpp
@ -9,7 +10,9 @@ add_executable(SidebarExample WIN32
) )
target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(SidebarExample PRIVATE qtadvanceddocking) target_link_libraries(SidebarExample PRIVATE qtadvanceddocking)
target_link_libraries(SidebarExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(SidebarExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(SidebarExample PROPERTIES set_target_properties(SidebarExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON

View File

@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(ads_example_simple VERSION ${VERSION_SHORT}) project(ads_example_simple VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(SimpleExample WIN32 add_executable(SimpleExample WIN32
main.cpp main.cpp
@ -9,7 +10,9 @@ add_executable(SimpleExample WIN32
) )
target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(SimpleExample PRIVATE qtadvanceddocking) target_link_libraries(SimpleExample PRIVATE qtadvanceddocking)
target_link_libraries(SimpleExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(SimpleExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(SimpleExample PROPERTIES set_target_properties(SimpleExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON

View File

@ -227,9 +227,7 @@ class build_ext(sipdistutils.build_ext):
extension.extra_link_args += ['-F' + self.qtconfig.QT_INSTALL_LIBS, extension.extra_link_args += ['-F' + self.qtconfig.QT_INSTALL_LIBS,
'-mmacosx-version-min=10.9'] '-mmacosx-version-min=10.9']
elif sys.platform == 'linux': elif sys.platform == 'linux':
extension.extra_compile_args += ['-D', 'QT_X11EXTRAS_LIB', '-std=c++11'] extension.extra_compile_args += ['-std=c++11']
extension.include_dirs += [os.path.join(self.qt_include_dir, 'QtX11Extras')]
extension.libraries += ['Qt5X11Extras' + self.qt_libinfix]
return super().swig_sources(sources, extension) return super().swig_sources(sources, extension)

View File

@ -21,6 +21,7 @@ public:
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget); void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget); void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget);
ads::CDockWidget* focusedDockWidget() const; ads::CDockWidget* focusedDockWidget() const;
void setDockWidgetTabFocused(ads::CDockWidgetTab* Tab);
public slots: public slots:
void setDockWidgetFocused(ads::CDockWidget* focusedNow); void setDockWidgetFocused(ads::CDockWidget* focusedNow);

View File

@ -33,8 +33,10 @@ public:
CustomCloseHandling, CustomCloseHandling,
DockWidgetFocusable, DockWidgetFocusable,
DockWidgetForceCloseWithArea, DockWidgetForceCloseWithArea,
NoTab,
DefaultDockWidgetFeatures, DefaultDockWidgetFeatures,
AllDockWidgetFeatures, AllDockWidgetFeatures,
DockWidgetAlwaysCloseAndDelete,
NoDockWidgetFeatures NoDockWidgetFeatures
}; };
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures; typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;

View File

@ -2,6 +2,40 @@
%If (Qt_5_0_0 -) %If (Qt_5_0_0 -)
%ModuleHeaderCode
PyObject *qtads_FindParent(PyObject* type, const QWidget *child);
%End
%ModuleCode
PyObject *qtads_FindParent(PyObject* type, const QWidget *w)
{
// Check that the types checking was successful.
if (!type)
return 0;
QWidget* parentWidget = w->parentWidget();
while (parentWidget)
{
PyObject *ParentImpl = sipConvertFromType(parentWidget, sipType_QObject, 0);
if (!ParentImpl)
{
return 0;
}
if (PyObject_IsInstance(ParentImpl, type))
return ParentImpl;
Py_DECREF(ParentImpl);
parentWidget = parentWidget->parentWidget();
}
Py_INCREF(Py_None);
return Py_None;
}
%End
namespace ads namespace ads
{ {
%TypeHeaderCode %TypeHeaderCode
@ -55,6 +89,50 @@ namespace ads
BitwiseOr BitwiseOr
}; };
namespace internal
{
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To);
void hideEmptyParentSplitters(ads::CDockSplitter* FirstParentSplitter);
class CDockInsertParam
{
%TypeHeaderCode
#include <ads_globals.h>
%End
public:
Qt::Orientation orientation() const;
bool append() const;
int insertOffset() const;
};
ads::internal::CDockInsertParam dockAreaInsertParameters(ads::DockWidgetArea Area);
SIP_PYOBJECT findParent(SIP_PYTYPE type, const QWidget *w) const /TypeHint="QObject"/;
%MethodCode
sipRes = qtads_FindParent(a0, a1);
if (!sipRes)
{
sipIsErr = 1;
}
%End
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity);
QPoint globalPositionOf(QMouseEvent* ev);
void setButtonIcon(QAbstractButton* Button, QStyle::StandardPixmap StandarPixmap, ads::eIcon CustomIconId);
enum eRepolishChildOptions
{
RepolishIgnoreChildren,
RepolishDirectChildren,
RepolishChildrenRecursively
};
void repolishStyle(QWidget* w, ads::internal::eRepolishChildOptions Options = ads::internal::RepolishIgnoreChildren);
};
}; };
%End %End

View File

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(QtAdvancedDockingSystem LANGUAGES CXX VERSION ${VERSION_SHORT}) project(QtAdvancedDockingSystem LANGUAGES CXX VERSION ${VERSION_SHORT})
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
find_package(Qt5 5.5 COMPONENTS X11Extras REQUIRED) include_directories(${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
endif() endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(BUILD_STATIC) if(BUILD_STATIC)
@ -60,11 +61,9 @@ else()
add_library(qtadvanceddocking SHARED ${ads_SRCS} ${ads_HEADERS}) add_library(qtadvanceddocking SHARED ${ads_SRCS} ${ads_HEADERS})
target_compile_definitions(qtadvanceddocking PRIVATE ADS_SHARED_EXPORT) target_compile_definitions(qtadvanceddocking PRIVATE ADS_SHARED_EXPORT)
endif() endif()
target_link_libraries(qtadvanceddocking PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) target_link_libraries(qtadvanceddocking PUBLIC Qt${QT_VERSION_MAJOR}::Core
if(UNIX AND NOT APPLE) Qt${QT_VERSION_MAJOR}::Gui
target_link_libraries(qtadvanceddocking PUBLIC Qt5::X11Extras) Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(qtadvanceddocking PRIVATE xcb)
endif()
set_target_properties(qtadvanceddocking PROPERTIES set_target_properties(qtadvanceddocking PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON

View File

@ -51,6 +51,7 @@
#include "DockAreaTabBar.h" #include "DockAreaTabBar.h"
#include "IconProvider.h" #include "IconProvider.h"
#include "DockComponentsFactory.h" #include "DockComponentsFactory.h"
#include "DockFocusController.h"
#include <iostream> #include <iostream>
@ -471,7 +472,8 @@ void CDockAreaTitleBar::mousePressEvent(QMouseEvent* ev)
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
{ {
d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason); //d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason);
d->dockManager()->dockFocusController()->setDockWidgetTabFocused(d->TabBar->currentTab());
} }
return; return;
} }

View File

@ -1030,6 +1030,21 @@ void CDockAreaWidget::onDockWidgetFeaturesChanged()
} }
#ifdef Q_OS_WIN
//============================================================================
bool CDockAreaWidget::event(QEvent *e)
{
switch (e->type())
{
case QEvent::PlatformSurface: return true;
default:
break;
}
return Super::event(e);
}
#endif
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -77,6 +77,17 @@ private Q_SLOTS:
void reorderDockWidget(int fromIndex, int toIndex); void reorderDockWidget(int fromIndex, int toIndex);
protected: protected:
#ifdef Q_OS_WIN
/**
* Reimplements QWidget::event to handle QEvent::PlatformSurface
* This is here to fix issue #294 Tab refresh problem with a QGLWidget
* that exists since Qt version 5.12.7. So this function is here to
* work around a Qt issue.
*/
virtual bool event(QEvent *event) override;
#endif
/** /**
* Inserts a dock widget into dock area. * Inserts a dock widget into dock area.
* All dockwidgets in the dock area tabified in a stacked layout with tabs. * All dockwidgets in the dock area tabified in a stacked layout with tabs.
@ -356,7 +367,8 @@ Q_SIGNALS:
*/ */
void viewToggled(bool Open); void viewToggled(bool Open);
}; // class DockAreaWidget }; // class DockAreaWidget
} } // namespace ads
// namespace ads
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockAreaWidget::DockAreaFlags)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#endif // DockAreaWidgetH #endif // DockAreaWidgetH

View File

@ -1566,7 +1566,8 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
if (Dropped) if (Dropped)
{ {
FloatingWidget->deleteLater(); // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351
FloatingWidget->hideAndDeleteLater();
// If we dropped a floating widget with only one single dock widget, then we // If we dropped a floating widget with only one single dock widget, then we
// drop a top level widget that changes from floating to docked now // drop a top level widget that changes from floating to docked now
@ -1679,9 +1680,12 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing)
if (!Testing) if (!Testing)
{ {
CFloatingDockContainer* FloatingWidget = floatingWidget(); CFloatingDockContainer* FloatingWidget = floatingWidget();
if (FloatingWidget)
{
FloatingWidget->restoreGeometry(Geometry); FloatingWidget->restoreGeometry(Geometry);
} }
} }
}
if (!d->restoreChildNodes(s, NewRootSplitter, Testing)) if (!d->restoreChildNodes(s, NewRootSplitter, Testing))
{ {

View File

@ -16,6 +16,7 @@
#include <QPointer> #include <QPointer>
#include <QApplication> #include <QApplication>
#include <QAbstractButton> #include <QAbstractButton>
#include <QWindow>
#include "DockWidget.h" #include "DockWidget.h"
#include "DockAreaWidget.h" #include "DockAreaWidget.h"
@ -31,6 +32,8 @@
namespace ads namespace ads
{ {
static const char* const FocusedDockWidgetProperty = "FocusedDockWidget";
/** /**
* Private data class of CDockFocusController class (pimpl) * Private data class of CDockFocusController class (pimpl)
*/ */
@ -56,8 +59,8 @@ struct DockFocusControllerPrivate
* the dock area that it belongs to * the dock area that it belongs to
*/ */
void updateDockWidgetFocus(CDockWidget* DockWidget); void updateDockWidgetFocus(CDockWidget* DockWidget);
}; }; // struct DockFocusControllerPrivate
// struct DockFocusControllerPrivate
//=========================================================================== //===========================================================================
@ -115,6 +118,17 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
return; return;
} }
QWindow* Window = nullptr;
auto DockContainer = DockWidget->dockContainer();
if (DockContainer)
{
Window = DockContainer->window()->windowHandle();
}
if (Window)
{
Window->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(DockWidget)));
}
CDockAreaWidget* NewFocusedDockArea = nullptr; CDockAreaWidget* NewFocusedDockArea = nullptr;
if (FocusedDockWidget) if (FocusedDockWidget)
{ {
@ -139,21 +153,25 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
} }
auto NewFloatingWidget = FocusedDockWidget->dockContainer()->floatingWidget();
CFloatingDockContainer* NewFloatingWidget = nullptr;
DockContainer = FocusedDockWidget->dockContainer();
if (DockContainer)
{
NewFloatingWidget = DockContainer->floatingWidget();
}
if (NewFloatingWidget) if (NewFloatingWidget)
{ {
NewFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(DockWidget)); NewFloatingWidget->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(DockWidget)));
} }
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// This code is required for styling the floating widget titlebar for linux // This code is required for styling the floating widget titlebar for linux
// depending on the current focus state // depending on the current focus state
if (FloatingWidget == NewFloatingWidget) if (FloatingWidget != NewFloatingWidget)
{ {
return;
}
if (FloatingWidget) if (FloatingWidget)
{ {
updateFloatingWidgetFocusStyle(FloatingWidget, false); updateFloatingWidgetFocusStyle(FloatingWidget, false);
@ -164,6 +182,7 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
{ {
updateFloatingWidgetFocusStyle(FloatingWidget, true); updateFloatingWidgetFocusStyle(FloatingWidget, true);
} }
}
#endif #endif
if (old == DockWidget && !ForceFocusChangedSignal) if (old == DockWidget && !ForceFocusChangedSignal)
@ -206,6 +225,8 @@ CDockFocusController::CDockFocusController(CDockManager* DockManager) :
d->DockManager = DockManager; d->DockManager = DockManager;
connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)), connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)),
this, SLOT(onApplicationFocusChanged(QWidget*, QWidget*))); this, SLOT(onApplicationFocusChanged(QWidget*, QWidget*)));
connect(QApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)),
this, SLOT(onFocusWindowChanged(QWindow*)));
connect(d->DockManager, SIGNAL(stateRestored()), SLOT(onStateRestored())); connect(d->DockManager, SIGNAL(stateRestored()), SLOT(onStateRestored()));
} }
@ -216,9 +237,35 @@ CDockFocusController::~CDockFocusController()
} }
//============================================================================
void CDockFocusController::onFocusWindowChanged(QWindow *focusWindow)
{
if (!focusWindow)
{
return;
}
auto vDockWidget = focusWindow->property(FocusedDockWidgetProperty);
if (!vDockWidget.isValid())
{
return;
}
auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
if (!DockWidget)
{
return;
}
d->updateDockWidgetFocus(DockWidget);
}
//=========================================================================== //===========================================================================
void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow) void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow)
{ {
Q_UNUSED(focusedOld);
if (d->DockManager->isRestoringState()) if (d->DockManager->isRestoringState())
{ {
return; return;
@ -231,47 +278,7 @@ void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidge
return; return;
} }
// If the close button in another tab steals the focus from the current CDockWidget* DockWidget = qobject_cast<CDockWidget*>(focusedNow);
// active dock widget content, i.e. if the user clicks its close button,
// then we immediately give the focus back to the previous focused widget
// focusedOld
if (CDockManager::testConfigFlag(CDockManager::AllTabsHaveCloseButton))
{
auto OtherDockWidgetTab = internal::findParent<CDockWidgetTab*>(focusedNow);
if (OtherDockWidgetTab && focusedOld)
{
auto OldFocusedDockWidget = internal::findParent<CDockWidget*>(focusedOld);
if (OldFocusedDockWidget)
{
focusedOld->setFocus();
}
return;
}
}
CDockWidget* DockWidget = nullptr;
auto DockWidgetTab = qobject_cast<CDockWidgetTab*>(focusedNow);
if (DockWidgetTab)
{
DockWidget = DockWidgetTab->dockWidget();
// If the DockWidgetTab "steals" the focus from a widget in the same
// DockWidget, then we immediately give the focus back to the previous
// focused widget focusedOld
if (focusedOld)
{
auto OldFocusedDockWidget = internal::findParent<CDockWidget*>(focusedOld);
if (OldFocusedDockWidget && OldFocusedDockWidget == DockWidget)
{
focusedOld->setFocus();
}
}
}
if (!DockWidget)
{
DockWidget = qobject_cast<CDockWidget*>(focusedNow);
}
if (!DockWidget) if (!DockWidget)
{ {
DockWidget = internal::findParent<CDockWidget*>(focusedNow); DockWidget = internal::findParent<CDockWidget*>(focusedNow);
@ -293,6 +300,17 @@ void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidge
} }
//===========================================================================
void CDockFocusController::setDockWidgetTabFocused(CDockWidgetTab* Tab)
{
auto DockWidget = Tab->dockWidget();
if (DockWidget)
{
d->updateDockWidgetFocus(DockWidget);
}
}
//=========================================================================== //===========================================================================
void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow) void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow)
{ {
@ -320,7 +338,7 @@ void CDockFocusController::onFocusedDockAreaViewToggled(bool Open)
return; return;
} }
CDockManager::setWidgetFocus(OpenedDockAreas[0]->currentDockWidget()->tabWidget()); d->updateDockWidgetFocus(OpenedDockAreas[0]->currentDockWidget());
} }
@ -348,7 +366,7 @@ void CDockFocusController::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget)
} }
d->ForceFocusChangedSignal = true; d->ForceFocusChangedSignal = true;
CDockManager::setWidgetFocus(DockWidget->tabWidget()); CDockManager::setWidgetFocus(DockWidget);
} }
@ -360,18 +378,17 @@ void CDockFocusController::notifyFloatingWidgetDrop(CFloatingDockContainer* Floa
return; return;
} }
auto vDockWidget = FloatingWidget->property("FocusedDockWidget"); auto vDockWidget = FloatingWidget->property(FocusedDockWidgetProperty);
if (!vDockWidget.isValid()) if (!vDockWidget.isValid())
{ {
return; return;
} }
auto DockWidget = vDockWidget.value<CDockWidget*>(); auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
if (DockWidget) if (DockWidget)
{ {
d->FocusedDockWidget = nullptr;
DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget); DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget);
CDockManager::setWidgetFocus(DockWidget->tabWidget()); CDockManager::setWidgetFocus(DockWidget);
} }
} }

View File

@ -32,6 +32,7 @@ private:
private Q_SLOTS: private Q_SLOTS:
void onApplicationFocusChanged(QWidget *old, QWidget *now); void onApplicationFocusChanged(QWidget *old, QWidget *now);
void onFocusWindowChanged(QWindow *focusWindow);
void onFocusedDockAreaViewToggled(bool Open); void onFocusedDockAreaViewToggled(bool Open);
void onStateRestored(); void onStateRestored();
void onDockWidgetVisibilityChanged(bool Visible); void onDockWidgetVisibilityChanged(bool Visible);
@ -48,21 +49,6 @@ public:
*/ */
virtual ~CDockFocusController(); virtual ~CDockFocusController();
/**
* Helper function to set focus depending on the configuration of the
* FocusStyling flag
*/
template <class QWidgetPtr>
static void setWidgetFocus(QWidgetPtr widget)
{
if (!CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
{
return;
}
widget->setFocus(Qt::OtherFocusReason);
}
/** /**
* A container needs to call this function if a widget has been dropped * A container needs to call this function if a widget has been dropped
* into it * into it
@ -83,6 +69,12 @@ public:
*/ */
CDockWidget* focusedDockWidget() const; CDockWidget* focusedDockWidget() const;
/**
* Request focus highlighting for the given dock widget assigned to the tab
* given in Tab parameter
*/
void setDockWidgetTabFocused(CDockWidgetTab* Tab);
public Q_SLOTS: public Q_SLOTS:
/** /**
* Request a focus change to the given dock widget * Request a focus change to the given dock widget

View File

@ -1140,6 +1140,13 @@ void CDockManager::setSplitterSizes(CDockAreaWidget *ContainedArea, const QList<
} }
} }
//===========================================================================
CDockFocusController* CDockManager::dockFocusController() const
{
return d->FocusController;
}
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -53,6 +53,7 @@ struct DockWidgetTabPrivate;
struct DockAreaWidgetPrivate; struct DockAreaWidgetPrivate;
class CIconProvider; class CIconProvider;
class CDockComponentsFactory; class CDockComponentsFactory;
class CDockFocusController;
/** /**
* The central dock manager that maintains the complete docking system. * The central dock manager that maintains the complete docking system.
@ -134,12 +135,18 @@ protected:
*/ */
void notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget); void notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget);
/** /**
* Show the floating widgets that has been created floating * Show the floating widgets that has been created floating
*/ */
virtual void showEvent(QShowEvent *event) override; virtual void showEvent(QShowEvent *event) override;
/**
* Acces for the internal dock focus controller.
* This function only returns a valid object, if the FocusHighlighting
* flag is set.
*/
CDockFocusController* dockFocusController() const;
public: public:
using Super = CDockContainerWidget; using Super = CDockContainerWidget;
@ -188,6 +195,7 @@ public:
FloatingContainerForceQWidgetTitleBar = 0x1000000,//!< Linux only ! Forces all FloatingContainer to use a QWidget based title bar. FloatingContainerForceQWidgetTitleBar = 0x1000000,//!< Linux only ! Forces all FloatingContainer to use a QWidget based title bar.
//!< If neither this nor FloatingContainerForceNativeTitleBar is set (the default) native titlebars are used except on known bad systems. //!< If neither this nor FloatingContainerForceNativeTitleBar is set (the default) native titlebars are used except on known bad systems.
//! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0". //! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0".
MiddleMouseButtonClosesTab = 0x2000000, //! If the flag is set, the user can use the mouse middle button to close the tab under the mouse
DefaultDockAreaButtons = DockAreaHasCloseButton DefaultDockAreaButtons = DockAreaHasCloseButton
| DockAreaHasUndockButton | DockAreaHasUndockButton
@ -609,5 +617,7 @@ Q_SIGNALS:
void focusedDockWidgetChanged(ads::CDockWidget* old, ads::CDockWidget* now); void focusedDockWidgetChanged(ads::CDockWidget* old, ads::CDockWidget* now);
}; // class DockManager }; // class DockManager
} // namespace ads } // namespace ads
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockManager::ConfigFlags)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#endif // DockManagerH #endif // DockManagerH

View File

@ -806,7 +806,7 @@ void CDockOverlayCross::setIconColors(const QString& Colors)
{"Arrow", CDockOverlayCross::ArrowColor}, {"Arrow", CDockOverlayCross::ArrowColor},
{"Shadow", CDockOverlayCross::ShadowColor}}; {"Shadow", CDockOverlayCross::ShadowColor}};
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
auto SkipEmptyParts = QString::SkipEmptyParts; auto SkipEmptyParts = QString::SkipEmptyParts;
#else #else
auto SkipEmptyParts = Qt::SkipEmptyParts; auto SkipEmptyParts = Qt::SkipEmptyParts;

View File

@ -66,6 +66,12 @@ namespace ads
*/ */
struct DockWidgetPrivate struct DockWidgetPrivate
{ {
struct WidgetFactory
{
CDockWidget::FactoryFunc createWidget;
CDockWidget::eInsertMode insertMode;
};
CDockWidget* _this = nullptr; CDockWidget* _this = nullptr;
QBoxLayout* Layout = nullptr; QBoxLayout* Layout = nullptr;
QWidget* Widget = nullptr; QWidget* Widget = nullptr;
@ -84,6 +90,7 @@ struct DockWidgetPrivate
bool IsFloatingTopLevel = false; bool IsFloatingTopLevel = false;
QList<QAction*> TitleBarActions; QList<QAction*> TitleBarActions;
CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget; CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget;
WidgetFactory* Factory = nullptr;
/** /**
* Private data constructor * Private data constructor
@ -116,6 +123,12 @@ struct DockWidgetPrivate
* Setup the main scroll area * Setup the main scroll area
*/ */
void setupScrollArea(); void setupScrollArea();
/**
* Creates the content widget with the registered widget factory and
* returns true on success.
*/
bool createWidgetFromFactory();
}; };
// struct DockWidgetPrivate // struct DockWidgetPrivate
@ -130,10 +143,23 @@ DockWidgetPrivate::DockWidgetPrivate(CDockWidget* _public) :
//============================================================================ //============================================================================
void DockWidgetPrivate::showDockWidget() void DockWidgetPrivate::showDockWidget()
{ {
if (!Widget)
{
if (!createWidgetFromFactory())
{
Q_ASSERT(!Features.testFlag(CDockWidget::DeleteContentOnClose)
&& "DeleteContentOnClose flag was set, but the widget "
"factory is missing or it doesn't return a valid QWidget.");
return;
}
}
if (!DockArea) if (!DockArea)
{ {
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this); CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this);
FloatingWidget->resize(_this->size()); // We use the size hint of the content widget to provide a good
// initial size
FloatingWidget->resize(Widget ? Widget->sizeHint() : _this->sizeHint());
TabWidget->show(); TabWidget->show();
FloatingWidget->show(); FloatingWidget->show();
} }
@ -165,6 +191,12 @@ void DockWidgetPrivate::hideDockWidget()
{ {
TabWidget->hide(); TabWidget->hide();
updateParentDockArea(); updateParentDockArea();
if (Features.testFlag(CDockWidget::DeleteContentOnClose))
{
Widget->deleteLater();
Widget = nullptr;
}
} }
@ -218,6 +250,30 @@ void DockWidgetPrivate::setupScrollArea()
} }
//============================================================================
bool DockWidgetPrivate::createWidgetFromFactory()
{
if (!Features.testFlag(CDockWidget::DeleteContentOnClose))
{
return false;
}
if (!Factory)
{
return false;
}
QWidget* w = Factory->createWidget(_this);
if (!w)
{
return false;
}
_this->setWidget(w, Factory->insertMode);
return true;
}
//============================================================================ //============================================================================
CDockWidget::CDockWidget(const QString &title, QWidget *parent) : CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
QFrame(parent), QFrame(parent),
@ -288,6 +344,17 @@ void CDockWidget::setWidget(QWidget* widget, eInsertMode InsertMode)
d->Widget->setProperty("dockWidgetContent", true); d->Widget->setProperty("dockWidgetContent", true);
} }
//============================================================================
void CDockWidget::setWidgetFactory(FactoryFunc createWidget, eInsertMode insertMode)
{
if (d->Factory)
{
delete d->Factory;
}
d->Factory = new DockWidgetPrivate::WidgetFactory { createWidget, insertMode };
}
//============================================================================ //============================================================================
QWidget* CDockWidget::takeWidget() QWidget* CDockWidget::takeWidget()
@ -530,7 +597,8 @@ void CDockWidget::toggleViewInternal(bool Open)
CDockWidget* TopLevelDockWidgetAfter = DockContainer CDockWidget* TopLevelDockWidgetAfter = DockContainer
? DockContainer->topLevelDockWidget() : nullptr; ? DockContainer->topLevelDockWidget() : nullptr;
CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetAfter, true); CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetAfter, true);
CFloatingDockContainer* FloatingContainer = DockContainer->floatingWidget(); CFloatingDockContainer* FloatingContainer = DockContainer
? DockContainer->floatingWidget() : nullptr;
if (FloatingContainer) if (FloatingContainer)
{ {
FloatingContainer->updateWindowTitle(); FloatingContainer->updateWindowTitle();

View File

@ -147,18 +147,19 @@ public:
enum DockWidgetFeature enum DockWidgetFeature
{ {
DockWidgetClosable = 0x01,///< dock widget has a close button DockWidgetClosable = 0x001,///< dock widget has a close button
DockWidgetMovable = 0x02,///< dock widget is movable and can be moved to a new position in the current dock container DockWidgetMovable = 0x002,///< dock widget is movable and can be moved to a new position in the current dock container
DockWidgetFloatable = 0x04,///< dock widget can be dragged into a floating window DockWidgetFloatable = 0x004,///< dock widget can be dragged into a floating window
DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed DockWidgetDeleteOnClose = 0x008, ///< deletes the dock widget when it is closed
CustomCloseHandling = 0x10, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead CustomCloseHandling = 0x010, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead
DockWidgetFocusable = 0x20, ///< if this is enabled, a dock widget can get focus highlighting DockWidgetFocusable = 0x020, ///< if this is enabled, a dock widget can get focus highlighting
DockWidgetForceCloseWithArea = 0x40, ///< dock widget will be closed when the dock area hosting it is closed DockWidgetForceCloseWithArea = 0x040, ///< dock widget will be closed when the dock area hosting it is closed
NoTab = 0x80, ///< dock widget tab will never be shown if this flag is set NoTab = 0x080, ///< dock widget tab will never be shown if this flag is set
DeleteContentOnClose = 0x100, ///< deletes only the contained widget on close, keeping the dock widget intact and in place. Attempts to rebuild the contents widget on show if there is a widget factory set.
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable, DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable,
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling, AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling,
DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose, DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose,
NoDockWidgetFeatures = 0x00 NoDockWidgetFeatures = 0x000
}; };
Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature) Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
@ -254,7 +255,7 @@ public:
/** /**
* Sets the widget for the dock widget to widget. * Sets the widget for the dock widget to widget.
* The InsertMode defines how the widget is inserted into the dock widget. * The InsertMode defines how the widget is inserted into the dock widget.
* The content of a dock widget should be resizable do a very small size to * The content of a dock widget should be resizable to a very small size to
* prevent the dock widget from blocking the resizing. To ensure, that a * prevent the dock widget from blocking the resizing. To ensure, that a
* dock widget can be resized very well, it is better to insert the content+ * dock widget can be resized very well, it is better to insert the content+
* widget into a scroll area or to provide a widget that is already a scroll * widget into a scroll area or to provide a widget that is already a scroll
@ -270,6 +271,18 @@ public:
*/ */
void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea); void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea);
/**
* Only used when the feature flag DeleteContentOnClose is set.
* Using the flag and setting a widget factory allows to free the resources
* of the widget of your application while retaining the position the next
* time you want to show your widget, unlike the flag DockWidgetDeleteOnClose
* which deletes the dock widget itself. Since we keep the dock widget, all
* regular features of ADS should work as normal, including saving and
* restoring the state of the docking system and using perspectives.
*/
using FactoryFunc = std::function<QWidget*(QWidget*)>;
void setWidgetFactory(FactoryFunc createWidget, eInsertMode InsertMode = AutoScrollArea);
/** /**
* Remove the widget from the dock and give ownership back to the caller * Remove the widget from the dock and give ownership back to the caller
*/ */
@ -587,7 +600,8 @@ Q_SIGNALS:
*/ */
void featuresChanged(ads::CDockWidget::DockWidgetFeatures features); void featuresChanged(ads::CDockWidget::DockWidgetFeatures features);
}; // class DockWidget }; // class DockWidget
} } // namespace ads
// namespace ads
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockWidget::DockWidgetFeatures)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#endif // DockWidgetH #endif // DockWidgetH

View File

@ -50,6 +50,7 @@
#include "DockOverlay.h" #include "DockOverlay.h"
#include "DockManager.h" #include "DockManager.h"
#include "IconProvider.h" #include "IconProvider.h"
#include "DockFocusController.h"
namespace ads namespace ads
@ -207,6 +208,14 @@ struct DockWidgetTabPrivate
IconLabel->setVisible(true); IconLabel->setVisible(true);
} }
/**
* Convenience function for access to the dock manager dock focus controller
*/
CDockFocusController* focusController() const
{
return DockWidget->dockManager()->dockFocusController();
}
}; };
// struct DockWidgetTabPrivate // struct DockWidgetTabPrivate
@ -234,6 +243,7 @@ void DockWidgetTabPrivate::createLayout()
CloseButton->setObjectName("tabCloseButton"); CloseButton->setObjectName("tabCloseButton");
internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon); internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon);
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
CloseButton->setFocusPolicy(Qt::NoFocus);
updateCloseButtonSizePolicy(); updateCloseButtonSizePolicy();
internal::setToolTip(CloseButton, QObject::tr("Close Tab")); internal::setToolTip(CloseButton, QObject::tr("Close Tab"));
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested())); _this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
@ -331,10 +341,11 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) :
setAttribute(Qt::WA_NoMousePropagation, true); setAttribute(Qt::WA_NoMousePropagation, true);
d->DockWidget = DockWidget; d->DockWidget = DockWidget;
d->createLayout(); d->createLayout();
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) setFocusPolicy(Qt::NoFocus);
/*if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
{ {
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
} }*/
} }
//============================================================================ //============================================================================
@ -353,6 +364,10 @@ void CDockWidgetTab::mousePressEvent(QMouseEvent* ev)
ev->accept(); ev->accept();
d->saveDragStartMousePosition(internal::globalPositionOf(ev)); d->saveDragStartMousePosition(internal::globalPositionOf(ev));
d->DragState = DraggingMousePressed; d->DragState = DraggingMousePressed;
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
{
d->focusController()->setDockWidgetTabFocused(this);
}
Q_EMIT clicked(); Q_EMIT clicked();
return; return;
} }
@ -377,17 +392,31 @@ void CDockWidgetTab::mouseReleaseEvent(QMouseEvent* ev)
// End of tab moving, emit signal // End of tab moving, emit signal
if (d->DockArea) if (d->DockArea)
{ {
ev->accept();
Q_EMIT moved(internal::globalPositionOf(ev)); Q_EMIT moved(internal::globalPositionOf(ev));
} }
break; break;
case DraggingFloatingWidget: case DraggingFloatingWidget:
ev->accept();
d->FloatingWidget->finishDragging(); d->FloatingWidget->finishDragging();
break; break;
default:; // do nothing default:; // do nothing
} }
} }
else if (ev->button() == Qt::MiddleButton)
{
if (CDockManager::testConfigFlag(CDockManager::MiddleMouseButtonClosesTab) && d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable))
{
// Only attempt to close if the mouse is still
// on top of the widget, to allow the user to cancel.
if (rect().contains(mapFromGlobal(QCursor::pos()))) {
ev->accept();
Q_EMIT closeRequested();
}
}
}
Super::mouseReleaseEvent(ev); Super::mouseReleaseEvent(ev);
} }
@ -515,7 +544,8 @@ void CDockWidgetTab::setActiveTab(bool active)
bool UpdateFocusStyle = false; bool UpdateFocusStyle = false;
if (active && !hasFocus()) if (active && !hasFocus())
{ {
setFocus(Qt::OtherFocusReason); //setFocus(Qt::OtherFocusReason);
d->focusController()->setDockWidgetTabFocused(this);
UpdateFocusStyle = true; UpdateFocusStyle = true;
} }
@ -612,15 +642,19 @@ QString CDockWidgetTab::text() const
//============================================================================ //============================================================================
void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event) void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton)
{
// If this is the last dock area in a dock container it does not make // If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one // sense to move it to a new floating widget and leave this one
// empty // empty
if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1) if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
&& d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable)) && d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{ {
event->accept();
d->saveDragStartMousePosition(internal::globalPositionOf(event)); d->saveDragStartMousePosition(internal::globalPositionOf(event));
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }
}
Super::mouseDoubleClickEvent(event); Super::mouseDoubleClickEvent(event);
} }
@ -677,6 +711,9 @@ bool CDockWidgetTab::event(QEvent *e)
{ {
const auto text = toolTip(); const auto text = toolTip();
d->TitleLabel->setToolTip(text); d->TitleLabel->setToolTip(text);
if (d->IconLabel) {
d->IconLabel->setToolTip(text);
}
} }
#endif #endif
return Super::event(e); return Super::event(e);

View File

@ -163,8 +163,8 @@ void CElidingLabel::resizeEvent(QResizeEvent *event)
//============================================================================ //============================================================================
QSize CElidingLabel::minimumSizeHint() const QSize CElidingLabel::minimumSizeHint() const
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
bool HasPixmap = !pixmap().isNull(); bool HasPixmap = !pixmap(Qt::ReturnByValue).isNull();
#else #else
bool HasPixmap = (pixmap() != nullptr); bool HasPixmap = (pixmap() != nullptr);
#endif #endif
@ -185,8 +185,8 @@ QSize CElidingLabel::minimumSizeHint() const
//============================================================================ //============================================================================
QSize CElidingLabel::sizeHint() const QSize CElidingLabel::sizeHint() const
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
bool HasPixmap = !pixmap().isNull(); bool HasPixmap = !pixmap(Qt::ReturnByValue).isNull();
#else #else
bool HasPixmap = (pixmap() != nullptr); bool HasPixmap = (pixmap() != nullptr);
#endif #endif

View File

@ -373,6 +373,7 @@ struct FloatingDockContainerPrivate
CDockAreaWidget *SingleDockArea = nullptr; CDockAreaWidget *SingleDockArea = nullptr;
QPoint DragStartPos; QPoint DragStartPos;
bool Hiding = false; bool Hiding = false;
bool AutoHideChildren = true;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
QWidget* MouseEventHandler = nullptr; QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr; CFloatingWidgetTitleBar* TitleBar = nullptr;
@ -841,15 +842,18 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event)
return; return;
} }
if ( d->AutoHideChildren )
{
d->Hiding = true; d->Hiding = true;
for (auto DockArea : d->DockContainer->openedDockAreas()) for ( auto DockArea : d->DockContainer->openedDockAreas() )
{ {
for (auto DockWidget : DockArea->openedDockWidgets()) for ( auto DockWidget : DockArea->openedDockWidgets() )
{ {
DockWidget->toggleView(false); DockWidget->toggleView( false );
} }
} }
d->Hiding = false; d->Hiding = false;
}
} }
@ -1035,6 +1039,18 @@ QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const
return d->DockContainer->dockWidgets(); return d->DockContainer->dockWidgets();
} }
//============================================================================
void CFloatingDockContainer::hideAndDeleteLater()
{
// Widget has been redocked, so it must be hidden right way (see
// https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351)
// but AutoHideChildren must be set to false because "this" still contains
// dock widgets that shall not be toggled hidden.
d->AutoHideChildren = false;
hide();
deleteLater();
}
//============================================================================ //============================================================================
void CFloatingDockContainer::finishDragging() void CFloatingDockContainer::finishDragging()
{ {

View File

@ -65,7 +65,7 @@ class CDockingStateReader;
* This interface is used for opaque and non-opaque undocking. If opaque * This interface is used for opaque and non-opaque undocking. If opaque
* undocking is used, the a real CFloatingDockContainer widget will be created * undocking is used, the a real CFloatingDockContainer widget will be created
*/ */
class IFloatingWidget class ADS_EXPORT IFloatingWidget
{ {
public: public:
virtual ~IFloatingWidget() = default; virtual ~IFloatingWidget() = default;
@ -258,6 +258,11 @@ public:
*/ */
QList<CDockWidget*> dockWidgets() const; QList<CDockWidget*> dockWidgets() const;
/**
* This function hides the floating bar instantely and delete it later.
*/
void hideAndDeleteLater();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
/** /**
* This is a function that responds to FloatingWidgetTitleBar::maximizeRequest() * This is a function that responds to FloatingWidgetTitleBar::maximizeRequest()

View File

@ -39,13 +39,11 @@
#include "ads_globals.h" #include "ads_globals.h"
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include <QX11Info>
#include <QSettings> #include <QSettings>
#include <QFile> #include <QFile>
#endif
#include <QApplication> #include <QApplication>
#include <qpa/qplatformnativeinterface.h>
#endif
namespace ads namespace ads
{ {
@ -57,10 +55,31 @@ static QString _window_manager;
static QHash<QString, xcb_atom_t> _xcb_atom_cache; static QHash<QString, xcb_atom_t> _xcb_atom_cache;
//============================================================================
bool is_platform_x11()
{
return QGuiApplication::platformName() == QLatin1String("xcb");
}
//============================================================================
xcb_connection_t* x11_connection()
{
if (!qApp)
return nullptr;
QPlatformNativeInterface *native = qApp->platformNativeInterface();
if (!native)
return nullptr;
void *connection = native->nativeResourceForIntegration(QByteArray("connection"));
return reinterpret_cast<xcb_connection_t *>(connection);
}
//============================================================================ //============================================================================
xcb_atom_t xcb_get_atom(const char *name) xcb_atom_t xcb_get_atom(const char *name)
{ {
if (!QX11Info::isPlatformX11()) if (!is_platform_x11())
{ {
return XCB_ATOM_NONE; return XCB_ATOM_NONE;
} }
@ -69,7 +88,7 @@ xcb_atom_t xcb_get_atom(const char *name)
{ {
return _xcb_atom_cache[key]; return _xcb_atom_cache[key];
} }
xcb_connection_t *connection = QX11Info::connection(); xcb_connection_t *connection = x11_connection();
xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name); xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL); xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
if (!reply) if (!reply)
@ -93,7 +112,7 @@ xcb_atom_t xcb_get_atom(const char *name)
//============================================================================ //============================================================================
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2) void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
{ {
auto connection = QX11Info::connection(); auto connection = x11_connection();
xcb_atom_t type_atom = xcb_get_atom(type); xcb_atom_t type_atom = xcb_get_atom(type);
xcb_atom_t prop_atom = xcb_get_atom(prop); xcb_atom_t prop_atom = xcb_get_atom(prop);
xcb_client_message_event_t event; xcb_client_message_event_t event;
@ -118,11 +137,11 @@ void xcb_update_prop(bool set, WId window, const char *type, const char *prop, c
//============================================================================ //============================================================================
xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type) xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type)
{ {
if (!QX11Info::isPlatformX11()) if (!is_platform_x11())
{ {
return nullptr; return nullptr;
} }
xcb_connection_t *connection = QX11Info::connection(); xcb_connection_t *connection = x11_connection();
xcb_atom_t type_atom = xcb_get_atom(type); xcb_atom_t type_atom = xcb_get_atom(type);
if (type_atom == XCB_ATOM_NONE) if (type_atom == XCB_ATOM_NONE)
{ {
@ -191,7 +210,7 @@ bool xcb_dump_props(WId window, const char *type)
QVector<xcb_atom_t> atoms; QVector<xcb_atom_t> atoms;
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM); xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
qDebug() << "\n\n!!!" << type << " - " << atoms.length(); qDebug() << "\n\n!!!" << type << " - " << atoms.length();
xcb_connection_t *connection = QX11Info::connection(); xcb_connection_t *connection = x11_connection();
for (auto atom : atoms) for (auto atom : atoms)
{ {
auto foo = xcb_get_atom_name(connection, atom); auto foo = xcb_get_atom_name(connection, atom);
@ -206,7 +225,7 @@ bool xcb_dump_props(WId window, const char *type)
//============================================================================ //============================================================================
void xcb_add_prop(bool state, WId window, const char *type, const char *prop) void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
{ {
if (!QX11Info::isPlatformX11()) if (!is_platform_x11())
{ {
return; return;
} }
@ -227,7 +246,7 @@ void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
{ {
atoms.remove(index); atoms.remove(index);
} }
xcb_connection_t *connection = QX11Info::connection(); xcb_connection_t *connection = x11_connection();
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData()); xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData());
xcb_flush(connection); xcb_flush(connection);
} }
@ -238,11 +257,11 @@ QString detectWindowManagerX11()
{ {
// Tries to detect the windowmanager via X11. // Tries to detect the windowmanager via X11.
// See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000 // See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
if (!QX11Info::isPlatformX11()) if (!is_platform_x11())
{ {
return "UNKNOWN"; return "UNKNOWN";
} }
xcb_connection_t *connection = QX11Info::connection(); xcb_connection_t *connection = x11_connection();
xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data; xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
if(!first_screen) if(!first_screen)
{ {

View File

@ -308,5 +308,6 @@ void repolishStyle(QWidget* w, eRepolishChildOptions Options = RepolishIgnoreChi
} // namespace internal } // namespace internal
} // namespace ads } // namespace ads
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::DockWidgetAreas)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#endif // ads_globalsH #endif // ads_globalsH

View File

@ -73,8 +73,8 @@ SOURCES += \
unix:!macx { unix:!macx {
HEADERS += linux/FloatingWidgetTitleBar.h HEADERS += linux/FloatingWidgetTitleBar.h
SOURCES += linux/FloatingWidgetTitleBar.cpp SOURCES += linux/FloatingWidgetTitleBar.cpp
QT += x11extras
LIBS += -lxcb LIBS += -lxcb
QT += gui-private
} }
isEmpty(PREFIX){ isEmpty(PREFIX){