Compare commits
257 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab4869a0e1 | ||
|
|
adf5793ccd | ||
|
|
31e26c2c1c | ||
|
|
de05ddd203 | ||
|
|
03a8eaa44f | ||
|
|
48e79f12a7 | ||
|
|
89aa3d5251 | ||
|
|
87b0596ebc | ||
|
|
a08857804a | ||
|
|
dbca6d79cf | ||
|
|
a24221c002 | ||
|
|
5f89dd0465 | ||
|
|
0b82ff30fe | ||
|
|
130b0de646 | ||
|
|
b5b251dffb | ||
|
|
0c44accb44 | ||
|
|
bbdf0ef29d | ||
|
|
401e8cf492 | ||
|
|
83cdc69fc8 | ||
|
|
1e3af06bb0 | ||
|
|
2c8abee668 | ||
|
|
5af7492b67 | ||
|
|
8d1465a81f | ||
|
|
5ead4684f5 | ||
|
|
fc91502162 | ||
|
|
3a99bdcfb4 | ||
|
|
487e23e190 | ||
|
|
511132ee4f | ||
|
|
edc89555bc | ||
|
|
ad30211dae | ||
|
|
48406da6ea | ||
|
|
aff0bd6e25 | ||
|
|
3969d28d92 | ||
|
|
b39cd2d81b | ||
|
|
0e8e563654 | ||
|
|
2f041a0eed | ||
|
|
ffa0105d3e | ||
|
|
6179832a2b | ||
|
|
13853573ea | ||
|
|
b6b4c626e8 | ||
|
|
bd41ec1627 | ||
|
|
b54dab7df2 | ||
|
|
e66ef604a7 | ||
|
|
e03674fd4b | ||
|
|
3f69fedd1f | ||
|
|
a614e3cc3d | ||
|
|
ebde50b492 | ||
|
|
2a6bd306cb | ||
|
|
8a27a5596b | ||
|
|
f835ffd978 | ||
|
|
97215705f5 | ||
|
|
6ee97e64d7 | ||
|
|
a1c4812619 | ||
|
|
b0c8edbd82 | ||
|
|
a4190ecbf0 | ||
|
|
d8c6efaada | ||
|
|
0312682e07 | ||
|
|
8d14068df7 | ||
|
|
fe1d9a493f | ||
|
|
e55ad49db8 | ||
|
|
018ce2001e | ||
|
|
c8fe4c46dd | ||
|
|
1781fa671d | ||
|
|
75910e910e | ||
|
|
899e06be1c | ||
|
|
66687dc8b6 | ||
|
|
b8fe620276 | ||
|
|
1a50ea9892 | ||
|
|
0a096869fe | ||
|
|
44dc76bd19 | ||
|
|
aedbaec497 | ||
|
|
04aa622111 | ||
|
|
3564229482 | ||
|
|
637db7f4f9 | ||
|
|
f6d3d6d34a | ||
|
|
92369bdb26 | ||
|
|
8f95447108 | ||
|
|
0e3c3bab45 | ||
|
|
3a5c965306 | ||
|
|
0c88457037 | ||
|
|
46fa22dc6a | ||
|
|
f3d32399e5 | ||
|
|
b320bb17d1 | ||
|
|
81afe2d3cb | ||
|
|
5fad43377b | ||
|
|
f543318232 | ||
|
|
ab385a782a | ||
|
|
c370875128 | ||
|
|
1c261515db | ||
|
|
37cbae84ca | ||
|
|
f5759716b4 | ||
|
|
f645fe725a | ||
|
|
fdedd7d92a | ||
|
|
044a43d793 | ||
|
|
6846c96146 | ||
|
|
8fe9461872 | ||
|
|
fbde4edcd2 | ||
|
|
68742681f7 | ||
|
|
bbb3f99bc3 | ||
|
|
e0f6f3013f | ||
|
|
6eb497fb64 | ||
|
|
ae15757765 | ||
|
|
be294b4867 | ||
|
|
70738f7549 | ||
|
|
42dc529ce1 | ||
|
|
65058d3a48 | ||
|
|
48c4106b7f | ||
|
|
175b48569f | ||
|
|
89c6abb5ce | ||
|
|
55f23799bc | ||
|
|
646211cc4c | ||
|
|
423bab9954 | ||
|
|
831d90ebf5 | ||
|
|
abdc0dc0dd | ||
|
|
175c836c93 | ||
|
|
76304172ab | ||
|
|
0eb3978aee | ||
|
|
73f42d55ca | ||
|
|
059a055483 | ||
|
|
dcf1ee393e | ||
|
|
04aecb3693 | ||
|
|
533d174abc | ||
|
|
48fb999bd0 | ||
|
|
5443e5f998 | ||
|
|
543d226ba3 | ||
|
|
03b1848b43 | ||
|
|
05ab8d2067 | ||
|
|
c28a27c81c | ||
|
|
1b42048135 | ||
|
|
bfe6b9bd26 | ||
|
|
08a8cee1c6 | ||
|
|
835a532e75 | ||
|
|
d383ade03c | ||
|
|
6d9c4cee02 | ||
|
|
a565239c4a | ||
|
|
ba69f3e6b9 | ||
|
|
14c29f695c | ||
|
|
703a9b3e12 | ||
|
|
0eca1b0433 | ||
|
|
691c9683ce | ||
|
|
1a11e5ddcd | ||
|
|
a4d281dbb6 | ||
|
|
8361f90dce | ||
|
|
04b4ff8b4b | ||
|
|
121248c3c5 | ||
|
|
c44d0c87e3 | ||
|
|
c6cf9487ba | ||
|
|
c78cc17730 | ||
|
|
81a0234b05 | ||
|
|
f72a8568c5 | ||
|
|
11aec65967 | ||
|
|
40636d1e05 | ||
|
|
75841415a3 | ||
|
|
edc799bc54 | ||
|
|
1d0b20337f | ||
|
|
4a6d2d3514 | ||
|
|
8443414ae3 | ||
|
|
81c99745d4 | ||
|
|
d6831caea4 | ||
|
|
aa25e1fd56 | ||
|
|
0459aff34f | ||
|
|
aeb0a27401 | ||
|
|
50e3ef3dd8 | ||
|
|
9974256d71 | ||
|
|
8cf4134125 | ||
|
|
ef5b22c616 | ||
|
|
42161c807a | ||
|
|
acfc96f57c | ||
|
|
e6e83d5775 | ||
|
|
5b2bc2297b | ||
|
|
2de3e7e3be | ||
|
|
0d242297ff | ||
|
|
e2080b5cfc | ||
|
|
679fa81f6d | ||
|
|
6d0f14e1a5 | ||
|
|
281127c2c3 | ||
|
|
8e621f1f20 | ||
|
|
0948f73bf8 | ||
|
|
4bc1a18db2 | ||
|
|
80eb628ea3 | ||
|
|
d811915a0c | ||
|
|
0225563b46 | ||
|
|
da20405a6a | ||
|
|
ace6d69695 | ||
|
|
7baf0f90e8 | ||
|
|
2ad92ce958 | ||
|
|
50c4a8ed48 | ||
|
|
59d6a64098 | ||
|
|
3de877fe56 | ||
|
|
e2cebd9dcf | ||
|
|
58744408f0 | ||
|
|
e36655a7ab | ||
|
|
ffed6a9c5f | ||
|
|
38d8e6aa25 | ||
|
|
c109ef836a | ||
|
|
caa1a9f330 | ||
|
|
e71884b23d | ||
|
|
d04c386948 | ||
|
|
6a25de327c | ||
|
|
e63d1b1683 | ||
|
|
fa2ab356e1 | ||
|
|
80f92e5697 | ||
|
|
0c13402516 | ||
|
|
97e3d72566 | ||
|
|
682aaf66eb | ||
|
|
c939df73fa | ||
|
|
fdf169ce9a | ||
|
|
4b730a4949 | ||
|
|
788c357cc0 | ||
|
|
e3844b8d6c | ||
|
|
ff3fcdcacd | ||
|
|
a9268e6bf7 | ||
|
|
0227bd1786 | ||
|
|
227037e42a | ||
|
|
cd495a14ec | ||
|
|
312a8cf500 | ||
|
|
2fc8bbe9c9 | ||
|
|
f5c4b26aab | ||
|
|
c4d2d72e92 | ||
|
|
f90f0b0427 | ||
|
|
d360b4ced2 | ||
|
|
f074ea9d67 | ||
|
|
2e8137ad85 | ||
|
|
a65b1bdcaf | ||
|
|
4041aa72cc | ||
|
|
4d2de7bb2a | ||
|
|
bcb7118710 | ||
|
|
45390506dd | ||
|
|
f58a3d4401 | ||
|
|
a3e979a8ad | ||
|
|
adb72737e8 | ||
|
|
e626a7e302 | ||
|
|
8b6df4aaa5 | ||
|
|
ae999f132e | ||
|
|
9aa958e8b0 | ||
|
|
5652c8440e | ||
|
|
26f4c9b049 | ||
|
|
ce11fa9d10 | ||
|
|
333d2920db | ||
|
|
25eb02d07c | ||
|
|
3b2f940efa | ||
|
|
9dcbe91f02 | ||
|
|
4da810ba7c | ||
|
|
819f1effc5 | ||
|
|
ba94ef3493 | ||
|
|
0127fd89a3 | ||
|
|
79cb889d83 | ||
|
|
e760d3e967 | ||
|
|
c5333a2414 | ||
|
|
3a0c2a3113 | ||
|
|
789f78354a | ||
|
|
4c75168152 | ||
|
|
64a2024513 | ||
|
|
056e1ef947 | ||
|
|
f54869fbf7 | ||
|
|
835a20f03f | ||
|
|
067338ef23 |
3
.github/workflows/linux-builds.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-18.04, ubuntu-16.04]
|
||||
os: [ubuntu-latest, ubuntu-20.04, ubuntu-18.04]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -16,6 +16,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get install qt5-default
|
||||
sudo apt-get install qtbase5-private-dev
|
||||
- name: qmake
|
||||
run: qmake
|
||||
- name: make
|
||||
|
||||
2
.gitignore
vendored
@@ -382,3 +382,5 @@ MigrationBackup/
|
||||
FodyWeavers.xsd
|
||||
/ build
|
||||
/Settings.ini
|
||||
.vscode/settings.json
|
||||
/.settings
|
||||
|
||||
@@ -1,12 +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-reference id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" ref="shared-provider"/>
|
||||
<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-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
</extension>
|
||||
</configuration>
|
||||
</project>
|
||||
@@ -65,7 +65,7 @@ matrix:
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
addons:
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
162
README.md
@@ -1,32 +1,54 @@
|
||||
# Advanced Docking System for Qt
|
||||
|
||||
[](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||
[](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||
[](gnu-lgpl-v2.1.md)
|
||||
|
||||
[What's new](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) •
|
||||
[Documentation](doc/user-guide.md)
|
||||
|
||||
Qt Advanced Docking System lets you create customizable layouts using a full
|
||||
featured window docking system similar to what is found in many popular
|
||||
integrated development environments (IDEs) such as Visual Studio.
|
||||
Qt Advanced Docking System lets you create customizable layouts using a full
|
||||
featured window docking system similar to what is found in many popular
|
||||
integrated development environments (IDEs) such as Visual Studio.
|
||||
|
||||
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
||||
|
||||
Everything is implemented with standard Qt functionality without any
|
||||
platform specific code. Basic usage of QWidgets and QLayouts and using basic
|
||||
styles as much as possible.
|
||||
## New and Noteworthy
|
||||
|
||||
This work is based on and inspired by the
|
||||
[Advanced Docking System for Qt](https://github.com/mfreiholz/Qt-Advanced-Docking-System)
|
||||
from Manuel Freiholz. I did an almost complete rewrite of his code to improve
|
||||
code quality, readibility and to fix all issues from the issue tracker
|
||||
of his docking system project.
|
||||
The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.0)
|
||||
adds the following features:
|
||||
|
||||
- support for **Qt6.**
|
||||
- support for [empty dock area](doc/user-guide.md#empty-dock-area)
|
||||
|
||||
The [release 3.6](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.6.0)
|
||||
adds some nice new features:
|
||||
|
||||
- support for [central widget](doc/user-guide.md#central-widget) concept
|
||||
|
||||

|
||||
|
||||
- support for [native floating widgets](doc/user-guide.md#floatingcontainerforcenativetitlebar-linux-only) on Linux
|
||||
|
||||

|
||||
|
||||
Both features are contributions from ADS users. Read the [documentation](doc/user-guide.md)
|
||||
to learn more about both new features.
|
||||
|
||||
The [release 3.5](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.0)
|
||||
adds the new [focus highlighting](doc/user-guide.md#focushighlighting) feature.
|
||||
This optional feature enables highlighting of the focused dock widget like you
|
||||
know it from Visual Studio.
|
||||
|
||||

|
||||
|
||||
[learn more...](doc/user-guide.md#focushighlighting)
|
||||
|
||||
## Features
|
||||
|
||||
### Overview
|
||||
|
||||
- [New and Noteworthy](#new-and-noteworthy)
|
||||
- [Features](#features)
|
||||
- [Overview](#overview)
|
||||
- [Docking everywhere - no central widget](#docking-everywhere---no-central-widget)
|
||||
@@ -38,7 +60,9 @@ of his docking system project.
|
||||
- [Tab-menu for easy handling of many tabbed dock widgets](#tab-menu-for-easy-handling-of-many-tabbed-dock-widgets)
|
||||
- [Many different ways to detach dock widgets](#many-different-ways-to-detach-dock-widgets)
|
||||
- [Supports deletion of dynamically created dock widgets](#supports-deletion-of-dynamically-created-dock-widgets)
|
||||
- [Python PyQt5 Bindings](#python-pyqt5-bindings)
|
||||
- [Tested Compatible Environments](#tested-compatible-environments)
|
||||
- [Supported Qt Versions](#supported-qt-versions)
|
||||
- [Windows](#windows)
|
||||
- [macOS](#macos)
|
||||
- [Linux](#linux)
|
||||
@@ -55,6 +79,9 @@ of his docking system project.
|
||||
- [Qt Design Studio](#qt-design-studio)
|
||||
- [QmixElements](#qmixelements)
|
||||
- [ezEditor](#ezeditor)
|
||||
- [D-Tect X](#d-tect-x)
|
||||
- [HiveWE](#hivewe)
|
||||
- [Ramses Composer](#ramses-composer)
|
||||
|
||||
### Docking everywhere - no central widget
|
||||
|
||||
@@ -62,8 +89,8 @@ There is no central widget like in the Qt docking system. You can dock on every
|
||||
border of the main window or you can dock into each dock area - so you are
|
||||
free to dock almost everywhere.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||

|
||||
|
||||
### Docking inside floating windows
|
||||
@@ -71,8 +98,8 @@ free to dock almost everywhere.
|
||||
There is no difference between the main window and a floating window. Docking
|
||||
into floating windows is supported.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||

|
||||
|
||||
### Grouped dragging
|
||||
@@ -81,8 +108,8 @@ When dragging the titlebar of a dock, all the tabs that are tabbed with it are
|
||||
going to be dragged. So you can move complete groups of tabbed widgets into
|
||||
a floating widget or from one dock area to another one.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||

|
||||
|
||||
### Perspectives for fast switching of the complete main window layout
|
||||
@@ -93,13 +120,13 @@ perspective to make your own custom perspective. Later you can simply
|
||||
select a perspective from the perspective list to quickly switch the complete
|
||||
main window layout.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||

|
||||
|
||||
### Opaque and non-opaque splitter resizing
|
||||
|
||||
The advanced docking system uses standard QSplitters as resize separators and thus supports opaque and non-opaque resizing functionality of QSplitter. In some rare cases, for very complex widgets or on slow machines resizing via separator on the fly may cause flicking and glaring of rendered content inside a widget. The global dock manager flag `OpaqueSplitterResize` configures the resizing behaviour of the splitters. If this flag is set, then widgets are resized dynamically (opaquely) while interactively moving the splitters.
|
||||
The advanced docking system uses standard QSplitters as resize separators and thus supports opaque and non-opaque resizing functionality of QSplitter. In some rare cases, for very complex widgets or on slow machines resizing via separator on the fly may cause flicking and glaring of rendered content inside a widget. The global dock manager flag `OpaqueSplitterResize` configures the resizing behaviour of the splitters. If this flag is set, then widgets are resized dynamically (opaquely) while interactively moving the splitters.
|
||||
|
||||

|
||||
|
||||
@@ -136,8 +163,26 @@ You can detach dock widgets and also dock areas in the following ways:
|
||||
|
||||
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||
|
||||
### Python PyQt5 Bindings
|
||||
|
||||

|
||||
|
||||
The Advanced Docking System comes with a complete Python integration based on
|
||||
PyQt5 bindings. The package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock). The python integration has been contributed to this project
|
||||
by the following people:
|
||||
|
||||
- [n-elie](https://github.com/n-elie)
|
||||
- [Hugo Slepicka](https://github.com/hhslepicka)
|
||||
- [K Lauer](https://github.com/klauer)
|
||||
|
||||
Latest working version: [3.5.2](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.2)
|
||||
|
||||
## Tested Compatible Environments
|
||||
|
||||
### Supported Qt Versions
|
||||
|
||||
The library supports **Qt5** and **Qt6**.
|
||||
|
||||
### Windows
|
||||
|
||||
Windows 10 [](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||
@@ -154,20 +199,34 @@ The application can be compiled for macOS. A user reported, that the library wor
|
||||
|
||||
### Linux
|
||||
|
||||
Ubuntu [](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||
[](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||
|
||||
The application can be compiled for Linux and has been developed and tested with **Kubuntu 18.04** and **Kubuntu 19.10**.
|
||||
Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like this is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with the following Linux distributions:
|
||||
|
||||
- **Kubuntu 18.04 and 19.10**
|
||||
- **Ubuntu 18.04, 19.10 and 20.04**
|
||||
|
||||
There are some requirements for the Linux distribution that have to be met:
|
||||
|
||||
- an X server that supports ARGB visuals and a compositing window manager. This is required to display the translucent dock overlays ([https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows](https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows)). If your Linux distribution does not support this, or if you disable this feature, you will very likely see issue [#95](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/95).
|
||||
- Wayland is not properly supported by Qt yet. If you use Wayland, then you should set the session type to x11: `XDG_SESSION_TYPE=x11 ./AdvancedDockingSystemDemo`. You will find more details about this in issue [#288](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/288).
|
||||
|
||||
Screenshot Kubuntu:
|
||||

|
||||
|
||||
and with **Ubuntu 19.10**
|
||||
|
||||
Screenshot Ubuntu:
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||
## Getting started / Example
|
||||
@@ -247,7 +306,13 @@ MainWindow::~MainWindow()
|
||||
## Developers
|
||||
|
||||
- Uwe Kindler, Project Maintainer
|
||||
- Manuel Freiholz
|
||||
- Manuel Freiholz
|
||||
|
||||
This work is based on and inspired by the
|
||||
[Advanced Docking System for Qt](https://github.com/mfreiholz/Qt-Advanced-Docking-System)
|
||||
from Manuel Freiholz. I did an almost complete rewrite of his code to improve
|
||||
code quality, readibility and to fix all issues from the issue tracker
|
||||
of his docking system project.
|
||||
|
||||
## License information
|
||||
|
||||
@@ -288,7 +353,7 @@ If this project help you reduce time to develop or if you just like it, you can
|
||||
From version 4.12 on, Qt Creator uses the Advanced Docking Framework for its
|
||||
Qt Quick Designer. This improves the usability when using multiple screens.
|
||||
|
||||

|
||||

|
||||
|
||||
### [Qt Design Studio](https://www.qt.io/ui-design-tools)
|
||||
|
||||
@@ -296,7 +361,7 @@ Taken from the [Qt Blog](https://www.qt.io/blog/qt-design-studio-1.5-beta-releas
|
||||
|
||||
> The most obvious change in [Qt Design Studio 1.5](https://www.qt.io/blog/qt-design-studio-1.5-beta-released) is the integration of dock widgets using the Qt Advanced Docking System. This allows the user to fully customize the workspace and also to undock any view into its own top level window. This especially improves the usability when using multiple screens.
|
||||
|
||||

|
||||
[](https://youtu.be/za9KBWcFXEw?t=84)
|
||||
|
||||
### [QmixElements](https://www.cetoni.com/products/qmixelements/)
|
||||
|
||||
@@ -304,7 +369,7 @@ The QmixElements software from [CETONI](https://www.cetoni.com) is a comprehensi
|
||||
plugin-based and modular laboratory automation software for controlling CETONI devices using a joint graphical user interface. The software features a powerful script system to automate processes. This [blog post](https://www.cetoni.com/blog/qmixelements-advanced-docking-system/) gives a nice overview about the use of the Qt
|
||||
Advanced Docking System in the QmixElements sofware.
|
||||
|
||||

|
||||

|
||||
|
||||
### [ezEditor](https://github.com/ezEngine/ezEngine)
|
||||
|
||||
@@ -312,4 +377,39 @@ The ezEditor is a full blown graphical editor used for editing scenes and
|
||||
importing and authoring assets for the [ezEngine](https://github.com/ezEngine/ezEngine) -
|
||||
an open source C++ game engine in active development.
|
||||
|
||||

|
||||

|
||||
|
||||
### [D-Tect X](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||
|
||||
D-Tect X is a X-ray inspection software for industrial radiography. It is a state-of-the-art 64-bit application which supports GPU (Graphics Processing Unit) acceleration and takes full advantage of computers with multiple CPU cores. A large set of tools assist the user in image analysis and evaluation. Thanks to the Qt Advanced Docking System the flexible and intuitive user interface can be completely customized to each user’s preference.
|
||||
|
||||
[learn more...](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||
|
||||

|
||||
|
||||
### [HiveWE](https://github.com/stijnherfst/HiveWE)
|
||||
|
||||
HiveWE is a Warcraft III world editor. It focusses on speed and ease of use,
|
||||
especially for large maps where the regular World Editor is often too slow and clunky.
|
||||
It has a JASS editor with syntax hightlighting, tabs, code completion and more.
|
||||
The JASS editor uses the Qt Advanced Docking System for the management and layout
|
||||
of the open editor windows.
|
||||
|
||||
[learn more...](https://github.com/stijnherfst/HiveWE)
|
||||
|
||||

|
||||
|
||||
### [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)
|
||||
|
||||

|
||||
|
||||
25
ads.pri
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
versionAtLeast(QT_VERSION, 5.15.0) {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
|
||||
unix:!macx {
|
||||
LIBS += -lxcb
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_demo VERSION ${VERSION_SHORT})
|
||||
|
||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.5 COMPONENTS AxContainer REQUIRED)
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AxContainer REQUIRED)
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(ads_demo_SRCS
|
||||
@@ -16,9 +17,11 @@ set(ads_demo_SRCS
|
||||
)
|
||||
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
|
||||
target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src")
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
if(WIN32)
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::AxContainer)
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::AxContainer)
|
||||
endif()
|
||||
target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking)
|
||||
set_target_properties(AdvancedDockingSystemDemo PROPERTIES
|
||||
|
||||
@@ -58,11 +58,15 @@
|
||||
#include <QMessageBox>
|
||||
#include <QMenu>
|
||||
#include <QToolButton>
|
||||
#include <QToolBar>
|
||||
#include <QPointer>
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QAxWidget>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <QMap>
|
||||
#include <QElapsedTimer>
|
||||
@@ -75,36 +79,7 @@
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
#include "StatusDialog.h"
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createLongTextLabelDockWidget(QMenu* ViewMenu)
|
||||
{
|
||||
static int LabelCount = 0;
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText(QString("Label %1 %2 - Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
|
||||
"Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque "
|
||||
"penatibus et magnis dis parturient montes, nascetur ridiculus mus. "
|
||||
"Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. "
|
||||
"Nulla consequat massa quis enim. Donec pede justo, fringilla vel, "
|
||||
"aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, "
|
||||
"imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede "
|
||||
"mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum "
|
||||
"semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, "
|
||||
"porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, "
|
||||
"dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla "
|
||||
"ut metus varius laoreet.")
|
||||
.arg(LabelCount)
|
||||
.arg(QTime::currentTime().toString("hh:mm:ss:zzz")));
|
||||
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++));
|
||||
DockWidget->setWidget(l);
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
#include "DockSplitter.h"
|
||||
|
||||
|
||||
/**
|
||||
@@ -138,7 +113,7 @@ static void appendFeaturStringToWindowTitle(ads::CDockWidget* DockWidget)
|
||||
static QIcon svgIcon(const QString& File)
|
||||
{
|
||||
// This is a workaround, because in item views SVG icons are not
|
||||
// properly scaled an look blurry or pixelate
|
||||
// properly scaled and look blurry or pixelate
|
||||
QIcon SvgIcon(File);
|
||||
SvgIcon.addPixmap(SvgIcon.pixmap(92));
|
||||
return SvgIcon;
|
||||
@@ -164,71 +139,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu)
|
||||
{
|
||||
static int CalendarCount = 0;
|
||||
QCalendarWidget* w = new QCalendarWidget();
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Calendar %1").arg(CalendarCount++));
|
||||
// The following lines are for testing the setWidget() and takeWidget()
|
||||
// functionality
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setWidget(w); // what happens if we set a widget if a widget is already set
|
||||
DockWidget->takeWidget(); // we remove the widget
|
||||
DockWidget->setWidget(w); // and set the widget again - there should be no error
|
||||
DockWidget->setToggleViewActionMode(ads::CDockWidget::ActionModeShow);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/date_range.svg"));
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu)
|
||||
{
|
||||
static int FileSystemCount = 0;
|
||||
QTreeView* w = new QTreeView();
|
||||
w->setFrameShape(QFrame::NoFrame);
|
||||
QFileSystemModel* m = new QFileSystemModel(w);
|
||||
m->setRootPath(QDir::currentPath());
|
||||
w->setModel(m);
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1")
|
||||
.arg(FileSystemCount++));
|
||||
DockWidget->setWidget(w);
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createEditorWidget(QMenu* ViewMenu)
|
||||
{
|
||||
static int EditorCount = 0;
|
||||
QPlainTextEdit* w = new QPlainTextEdit();
|
||||
w->setPlaceholderText("This is an editor. If you close the editor, it will be "
|
||||
"deleted. Enter your text here.");
|
||||
w->setStyleSheet("border: none");
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Editor %1").arg(EditorCount++));
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/edit.svg"));
|
||||
DockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true);
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
|
||||
QMenu* OptionsMenu = new QMenu(DockWidget);
|
||||
OptionsMenu->setTitle(QObject::tr("Options"));
|
||||
OptionsMenu->setToolTip(OptionsMenu->title());
|
||||
OptionsMenu->setIcon(svgIcon(":/adsdemo/images/custom-menu-button.svg"));
|
||||
auto MenuAction = OptionsMenu->menuAction();
|
||||
// The object name of the action will be set for the QToolButton that
|
||||
// is created in the dock area title bar. You can use this name for CSS
|
||||
// styling
|
||||
MenuAction->setObjectName("optionsMenu");
|
||||
DockWidget->setTitleBarActions({OptionsMenu->menuAction()});
|
||||
auto a = OptionsMenu->addAction(QObject::tr("Clear Editor"));
|
||||
w->connect(a, SIGNAL(triggered()), SLOT(clear()));
|
||||
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
/**
|
||||
@@ -246,58 +156,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createTableWidget(QMenu* ViewMenu)
|
||||
{
|
||||
static int TableCount = 0;
|
||||
auto w = new CMinSizeTableWidget();
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Table %1").arg(TableCount++));
|
||||
static int colCount = 5;
|
||||
static int rowCount = 30;
|
||||
w->setColumnCount(colCount);
|
||||
w->setRowCount(rowCount);
|
||||
for (int col = 0; col < colCount; ++col)
|
||||
{
|
||||
w->setHorizontalHeaderItem(col, new QTableWidgetItem(QString("Col %1").arg(col+1)));
|
||||
for (int row = 0; row < rowCount; ++row)
|
||||
{
|
||||
w->setItem(row, col, new QTableWidgetItem(QString("T %1-%2").arg(row + 1).arg(col+1)));
|
||||
}
|
||||
}
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/grid_on.svg"));
|
||||
DockWidget->setMinimumSizeHintMode(ads::CDockWidget::MinimumSizeHintFromContent);
|
||||
auto ToolBar = DockWidget->createDefaultToolBar();
|
||||
auto Action = ToolBar->addAction(svgIcon(":/adsdemo/images/fullscreen.svg"), "Toggle Fullscreen");
|
||||
QObject::connect(Action, &QAction::triggered, [=]()
|
||||
{
|
||||
if (DockWidget->isFullScreen())
|
||||
{
|
||||
DockWidget->showNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockWidget->showFullScreen();
|
||||
}
|
||||
});
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
//============================================================================
|
||||
static ads::CDockWidget* createActiveXWidget(QMenu* ViewMenu, QWidget* parent = nullptr)
|
||||
{
|
||||
static int ActiveXCount = 0;
|
||||
QAxWidget* w = new QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent);
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Active X %1").arg(ActiveXCount++));
|
||||
DockWidget->setWidget(w);
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//============================================================================
|
||||
/**
|
||||
@@ -311,6 +169,9 @@ struct MainWindowPrivate
|
||||
QWidgetAction* PerspectiveListAction = nullptr;
|
||||
QComboBox* PerspectiveComboBox = nullptr;
|
||||
ads::CDockManager* DockManager = nullptr;
|
||||
ads::CDockWidget* WindowTitleTestDockWidget = nullptr;
|
||||
QPointer<ads::CDockWidget> LastDockedEditor;
|
||||
QPointer<ads::CDockWidget> LastCreatedFloatingEditor;
|
||||
|
||||
MainWindowPrivate(CMainWindow* _public) : _this(_public) {}
|
||||
|
||||
@@ -343,14 +204,183 @@ struct MainWindowPrivate
|
||||
* Restore the perspective listo of the dock manager
|
||||
*/
|
||||
void restorePerspectives();
|
||||
|
||||
/**
|
||||
* Creates a dock widget with a file system tree view
|
||||
*/
|
||||
ads::CDockWidget* createFileSystemTreeDockWidget()
|
||||
{
|
||||
static int FileSystemCount = 0;
|
||||
QTreeView* w = new QTreeView();
|
||||
w->setFrameShape(QFrame::NoFrame);
|
||||
QFileSystemModel* m = new QFileSystemModel(w);
|
||||
m->setRootPath(QDir::currentPath());
|
||||
w->setModel(m);
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1")
|
||||
.arg(FileSystemCount++));
|
||||
DockWidget->setWidget(w);
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
// We disable focus to test focus highlighting if the dock widget content
|
||||
// does not support focus
|
||||
w->setFocusPolicy(Qt::NoFocus);
|
||||
auto ToolBar = DockWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dock widget with a QCalendarWidget
|
||||
*/
|
||||
ads::CDockWidget* createCalendarDockWidget()
|
||||
{
|
||||
static int CalendarCount = 0;
|
||||
QCalendarWidget* w = new QCalendarWidget();
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Calendar %1").arg(CalendarCount++));
|
||||
// The following lines are for testing the setWidget() and takeWidget()
|
||||
// functionality
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setWidget(w); // what happens if we set a widget if a widget is already set
|
||||
DockWidget->takeWidget(); // we remove the widget
|
||||
DockWidget->setWidget(w); // and set the widget again - there should be no error
|
||||
DockWidget->setToggleViewActionMode(ads::CDockWidget::ActionModeShow);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/date_range.svg"));
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
auto ToolBar = DockWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create dock widget with a text label
|
||||
*/
|
||||
ads::CDockWidget* createLongTextLabelDockWidget()
|
||||
{
|
||||
static int LabelCount = 0;
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText(QString("Label %1 %2 - Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
|
||||
"Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque "
|
||||
"penatibus et magnis dis parturient montes, nascetur ridiculus mus. "
|
||||
"Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. "
|
||||
"Nulla consequat massa quis enim. Donec pede justo, fringilla vel, "
|
||||
"aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, "
|
||||
"imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede "
|
||||
"mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum "
|
||||
"semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, "
|
||||
"porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, "
|
||||
"dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla "
|
||||
"ut metus varius laoreet.")
|
||||
.arg(LabelCount)
|
||||
.arg(QTime::currentTime().toString("hh:mm:ss:zzz")));
|
||||
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++));
|
||||
DockWidget->setWidget(l);
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates as imple editor widget
|
||||
*/
|
||||
ads::CDockWidget* createEditorWidget()
|
||||
{
|
||||
static int EditorCount = 0;
|
||||
QPlainTextEdit* w = new QPlainTextEdit();
|
||||
w->setPlaceholderText("This is an editor. If you close the editor, it will be "
|
||||
"deleted. Enter your text here.");
|
||||
w->setStyleSheet("border: none");
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Editor %1").arg(EditorCount++));
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/edit.svg"));
|
||||
DockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true);
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
|
||||
QMenu* OptionsMenu = new QMenu(DockWidget);
|
||||
OptionsMenu->setTitle(QObject::tr("Options"));
|
||||
OptionsMenu->setToolTip(OptionsMenu->title());
|
||||
OptionsMenu->setIcon(svgIcon(":/adsdemo/images/custom-menu-button.svg"));
|
||||
auto MenuAction = OptionsMenu->menuAction();
|
||||
// The object name of the action will be set for the QToolButton that
|
||||
// is created in the dock area title bar. You can use this name for CSS
|
||||
// styling
|
||||
MenuAction->setObjectName("optionsMenu");
|
||||
DockWidget->setTitleBarActions({OptionsMenu->menuAction()});
|
||||
auto a = OptionsMenu->addAction(QObject::tr("Clear Editor"));
|
||||
w->connect(a, SIGNAL(triggered()), SLOT(clear()));
|
||||
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a table widget
|
||||
*/
|
||||
ads::CDockWidget* createTableWidget()
|
||||
{
|
||||
static int TableCount = 0;
|
||||
auto w = new CMinSizeTableWidget();
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Table %1").arg(TableCount++));
|
||||
static int colCount = 5;
|
||||
static int rowCount = 30;
|
||||
w->setColumnCount(colCount);
|
||||
w->setRowCount(rowCount);
|
||||
for (int col = 0; col < colCount; ++col)
|
||||
{
|
||||
w->setHorizontalHeaderItem(col, new QTableWidgetItem(QString("Col %1").arg(col+1)));
|
||||
for (int row = 0; row < rowCount; ++row)
|
||||
{
|
||||
w->setItem(row, col, new QTableWidgetItem(QString("T %1-%2").arg(row + 1).arg(col+1)));
|
||||
}
|
||||
}
|
||||
DockWidget->setWidget(w);
|
||||
DockWidget->setIcon(svgIcon(":/adsdemo/images/grid_on.svg"));
|
||||
DockWidget->setMinimumSizeHintMode(ads::CDockWidget::MinimumSizeHintFromContent);
|
||||
auto ToolBar = DockWidget->createDefaultToolBar();
|
||||
auto Action = ToolBar->addAction(svgIcon(":/adsdemo/images/fullscreen.svg"), "Toggle Fullscreen");
|
||||
QObject::connect(Action, &QAction::triggered, [=]()
|
||||
{
|
||||
if (DockWidget->isFullScreen())
|
||||
{
|
||||
DockWidget->showNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockWidget->showFullScreen();
|
||||
}
|
||||
});
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
/**
|
||||
* Creates an ActiveX widget on windows
|
||||
*/
|
||||
ads::CDockWidget* createActiveXWidget(QWidget* parent = nullptr)
|
||||
{
|
||||
static int ActiveXCount = 0;
|
||||
QAxWidget* w = new QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent);
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Active X %1").arg(ActiveXCount++));
|
||||
DockWidget->setWidget(w);
|
||||
ui.menuView->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::createContent()
|
||||
{
|
||||
// Test container docking
|
||||
QMenu* ViewMenu = ui.menuView;
|
||||
auto DockWidget = createCalendarDockWidget(ViewMenu);
|
||||
auto DockWidget = createCalendarDockWidget();
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
|
||||
auto SpecialDockArea = DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget);
|
||||
|
||||
@@ -360,19 +390,16 @@ void MainWindowPrivate::createContent()
|
||||
//SpecialDockArea->setAllowedAreas({ads::LeftDockWidgetArea, ads::RightDockWidgetArea}); // just for testing
|
||||
}
|
||||
|
||||
DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(ViewMenu));
|
||||
auto FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu);
|
||||
auto ToolBar = FileSystemWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
DockWidget = createLongTextLabelDockWidget();
|
||||
WindowTitleTestDockWidget = DockWidget;
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetFocusable, false);
|
||||
DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget);
|
||||
auto FileSystemWidget = createFileSystemTreeDockWidget();
|
||||
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
|
||||
appendFeaturStringToWindowTitle(FileSystemWidget);
|
||||
DockManager->addDockWidget(ads::BottomDockWidgetArea, FileSystemWidget);
|
||||
|
||||
FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu);
|
||||
ToolBar = FileSystemWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
FileSystemWidget = createFileSystemTreeDockWidget();
|
||||
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false);
|
||||
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
|
||||
appendFeaturStringToWindowTitle(FileSystemWidget);
|
||||
@@ -380,13 +407,32 @@ void MainWindowPrivate::createContent()
|
||||
// Test custom factory - we inject a help button into the title bar
|
||||
ads::CDockComponentsFactory::setFactory(new CCustomComponentsFactory());
|
||||
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
|
||||
// Uncomment the next line if you would like to test the
|
||||
// HideSingleWidgetTitleBar functionality
|
||||
// TopDockArea->setDockAreaFlag(ads::CDockAreaWidget::HideSingleWidgetTitleBar, true);
|
||||
ads::CDockComponentsFactory::resetDefaultFactory();
|
||||
|
||||
// We create a calendar widget and clear all flags to prevent the dock area
|
||||
// from closing
|
||||
DockWidget = createCalendarDockWidget(ViewMenu);
|
||||
DockWidget = createCalendarDockWidget();
|
||||
DockWidget->setTabToolTip(QString("Tab ToolTip\nHodie est dies magna"));
|
||||
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
|
||||
// new editor widgets when clicked
|
||||
@@ -400,31 +446,34 @@ void MainWindowPrivate::createContent()
|
||||
TitleBar->insertWidget(Index + 1, CustomButton);
|
||||
QObject::connect(CustomButton, &QToolButton::clicked, [=]()
|
||||
{
|
||||
auto DockWidget = createEditorWidget(ui.menuView);
|
||||
auto DockWidget = createEditorWidget();
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||
DockManager->addDockWidgetTabToArea(DockWidget, DockArea);
|
||||
_this->connect(DockWidget, SIGNAL(closeRequested()), SLOT(onEditorCloseRequested()));
|
||||
});
|
||||
|
||||
// Test dock area docking
|
||||
auto RighDockArea = DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), TopDockArea);
|
||||
DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), BottomDockArea);
|
||||
auto RighDockArea = DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(), TopDockArea);
|
||||
DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||
auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||
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()));
|
||||
Action = ui.menuTests->addAction(QString("Set %1 As Current Tab").arg(DockWidget->windowTitle()));
|
||||
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setAsCurrentTab()));
|
||||
Action = ui.menuTests->addAction(QString("Raise %1").arg(DockWidget->windowTitle()));
|
||||
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(raise()));
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (!DockManager->configFlags().testFlag(ads::CDockManager::OpaqueUndocking))
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking))
|
||||
{
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createActiveXWidget(ViewMenu), RighDockArea);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createActiveXWidget(), RighDockArea);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (auto DockWidget : DockManager->dockWidgetsMap())
|
||||
@@ -479,6 +528,9 @@ void MainWindowPrivate::createActions()
|
||||
ui.menuTests->addSeparator();
|
||||
a = ui.menuTests->addAction("Show Status Dialog");
|
||||
_this->connect(a, SIGNAL(triggered()), SLOT(showStatusDialog()));
|
||||
|
||||
a = ui.menuTests->addAction("Toggle Label 0 Window Title");
|
||||
_this->connect(a, SIGNAL(triggered()), SLOT(toggleDockWidgetWindowTitle()));
|
||||
ui.menuTests->addSeparator();
|
||||
}
|
||||
|
||||
@@ -571,11 +623,27 @@ CMainWindow::CMainWindow(QWidget *parent) :
|
||||
// dock widget.
|
||||
// CDockManager::setConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar, true);
|
||||
|
||||
// uncomment the following line to enable focus highlighting of the dock
|
||||
// widget that has the focus
|
||||
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||
|
||||
// uncomment if you would like to enable an equal distribution of the
|
||||
// available size of a splitter to all contained dock widgets
|
||||
// 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
|
||||
d->DockManager = new CDockManager(this);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||
d->DockManager, SLOT(openPerspective(const QString&)));
|
||||
#else
|
||||
connect(d->PerspectiveComboBox, SIGNAL(textActivated(const QString&)),
|
||||
d->DockManager, SLOT(openPerspective(const QString&)));
|
||||
#endif
|
||||
|
||||
d->createContent();
|
||||
// Default window geometry - center on screen
|
||||
@@ -601,6 +669,9 @@ CMainWindow::~CMainWindow()
|
||||
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
d->saveState();
|
||||
// Delete dock manager here to delete all floating widgets. This ensures
|
||||
// that all top level windows of the dock manager are properly closed
|
||||
d->DockManager->deleteLater();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
@@ -656,13 +727,14 @@ void CMainWindow::onViewToggled(bool Open)
|
||||
//============================================================================
|
||||
void CMainWindow::onViewVisibilityChanged(bool Visible)
|
||||
{
|
||||
Q_UNUSED(Visible);
|
||||
auto DockWidget = qobject_cast<ads::CDockWidget*>(sender());
|
||||
if (!DockWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << DockWidget->objectName() << " visibilityChanged(" << Visible << ")";
|
||||
//qDebug() << DockWidget->objectName() << " visibilityChanged(" << Visible << ")";
|
||||
}
|
||||
|
||||
|
||||
@@ -672,18 +744,41 @@ void CMainWindow::createEditor()
|
||||
QObject* Sender = sender();
|
||||
QVariant vFloating = Sender->property("Floating");
|
||||
bool Floating = vFloating.isValid() ? vFloating.toBool() : true;
|
||||
auto DockWidget = createEditorWidget(d->ui.menuView);
|
||||
auto DockWidget = d->createEditorWidget();
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetForceCloseWithArea, true);
|
||||
connect(DockWidget, SIGNAL(closeRequested()), SLOT(onEditorCloseRequested()));
|
||||
|
||||
if (Floating)
|
||||
{
|
||||
auto FloatingWidget = d->DockManager->addDockWidgetFloating(DockWidget);
|
||||
FloatingWidget->move(QPoint(20, 20));
|
||||
d->LastCreatedFloatingEditor = DockWidget;
|
||||
d->LastDockedEditor.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
d->DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||
ads::CDockAreaWidget* EditorArea = d->LastDockedEditor ? d->LastDockedEditor->dockAreaWidget() : nullptr;
|
||||
if (EditorArea)
|
||||
{
|
||||
std::cout << "DockAreaCount before: " << EditorArea->dockContainer()->dockAreaCount() << std::endl;
|
||||
d->DockManager->setConfigFlag(ads::CDockManager::EqualSplitOnInsertion, true);
|
||||
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, EditorArea);
|
||||
std::cout << "DockAreaCount after: " << DockWidget->dockContainer()->dockAreaCount() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (d->LastCreatedFloatingEditor)
|
||||
{
|
||||
std::cout << "LastCreated" << std::endl;
|
||||
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, d->LastCreatedFloatingEditor->dockAreaWidget());
|
||||
}
|
||||
else
|
||||
{
|
||||
d->DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||
}
|
||||
}
|
||||
d->LastDockedEditor = DockWidget;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +800,7 @@ void CMainWindow::onEditorCloseRequested()
|
||||
//============================================================================
|
||||
void CMainWindow::createTable()
|
||||
{
|
||||
auto DockWidget = createTableWidget(d->ui.menuView);
|
||||
auto DockWidget = d->createTableWidget();
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||
auto FloatingWidget = d->DockManager->addDockWidgetFloating(DockWidget);
|
||||
FloatingWidget->move(QPoint(40, 40));
|
||||
@@ -719,3 +814,20 @@ void CMainWindow::showStatusDialog()
|
||||
Dialog.exec();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::toggleDockWidgetWindowTitle()
|
||||
{
|
||||
QString Title = d->WindowTitleTestDockWidget->windowTitle();
|
||||
int i = Title.indexOf(" (Test)");
|
||||
if (-1 == i)
|
||||
{
|
||||
Title += " (Test)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Title = Title.left(i);
|
||||
}
|
||||
d->WindowTitleTestDockWidget->setWindowTitle(Title);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ private slots:
|
||||
void createTable();
|
||||
void onEditorCloseRequested();
|
||||
void showStatusDialog();
|
||||
void toggleDockWidgetWindowTitle();
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
@@ -4,8 +4,12 @@ TARGET = AdvancedDockingSystemDemo
|
||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||
QT += core gui widgets
|
||||
|
||||
win32 {
|
||||
QT += axcontainer
|
||||
include(../ads.pri)
|
||||
|
||||
lessThan(QT_MAJOR_VERSION, 6) {
|
||||
win32 {
|
||||
QT += axcontainer
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG += c++14
|
||||
@@ -34,21 +38,6 @@ RESOURCES += demo.qrc
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
INCLUDEPATH += ../src
|
||||
DEPENDPATH += ../src
|
||||
|
||||
@@ -35,9 +35,11 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#if QT_VERSION >= 0x050600
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
#endif
|
||||
std::shared_ptr<int> b;
|
||||
QApplication a(argc, argv);
|
||||
|
||||
@@ -10,11 +10,11 @@ from PyQt5.QtCore import (QCoreApplication, QDir, Qt, QSettings, QSignalBlocker,
|
||||
QtCriticalMsg, QtFatalMsg, QSize)
|
||||
from PyQt5.QtGui import (QGuiApplication, QIcon, QCloseEvent)
|
||||
from PyQt5.QtWidgets import (QCalendarWidget, QFileSystemModel, QFrame, QLabel,
|
||||
QMenu, QTreeView, QAction, QWidgetAction,
|
||||
QComboBox, QStyle, QSizePolicy, QInputDialog, QMenu,
|
||||
QToolButton, QWidget, QPlainTextEdit,
|
||||
QTableWidget, QTableWidgetItem, QApplication,
|
||||
QMessageBox)
|
||||
QMenu, QTreeView, QAction, QWidgetAction,
|
||||
QComboBox, QStyle, QSizePolicy, QInputDialog, QMenu,
|
||||
QToolButton, QWidget, QPlainTextEdit,
|
||||
QTableWidget, QTableWidgetItem, QApplication,
|
||||
QMessageBox)
|
||||
try:
|
||||
from PyQt5.QAxContainer import QAxWidget
|
||||
except ImportError:
|
||||
@@ -48,7 +48,7 @@ def features_string(dock_widget: QtAds.CDockWidget) -> str:
|
||||
f = dock_widget.features()
|
||||
closable = f & QtAds.CDockWidget.DockWidgetClosable
|
||||
movable = f & QtAds.CDockWidget.DockWidgetMovable
|
||||
floatable = f &QtAds.CDockWidget.DockWidgetFloatable
|
||||
floatable = f & QtAds.CDockWidget.DockWidgetFloatable
|
||||
|
||||
return "c{} m{} f{}".format("+" if closable else "-",
|
||||
"+" if movable else "-",
|
||||
@@ -65,95 +65,11 @@ def append_feature_string_to_window_title(dock_widget: QtAds.CDockWidget):
|
||||
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 an look blurry or pixelate
|
||||
# properly scaled and look blurry or pixelate
|
||||
icon = QIcon(filename)
|
||||
icon.addPixmap(icon.pixmap(92))
|
||||
return icon
|
||||
|
||||
|
||||
def create_long_text_label_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
|
||||
label = QLabel()
|
||||
label.setWordWrap(True)
|
||||
label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
label.setText('''\
|
||||
Label {} {} - Lorem ipsum dolor sit amet, consectetuer
|
||||
adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum
|
||||
sociis natoque penatibus et magnis dis parturient montes, nascetur
|
||||
ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium
|
||||
quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla
|
||||
vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut,
|
||||
imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis
|
||||
pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi.
|
||||
Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu,
|
||||
consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra
|
||||
quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet.
|
||||
'''.format(_State.label_count, datetime.datetime.now().strftime("%H:%M:%S:%f")))
|
||||
dock_widget = QtAds.CDockWidget("Label {}".format(_State.label_count))
|
||||
_State.label_count += 1
|
||||
dock_widget.setWidget(label)
|
||||
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
def create_calendar_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
|
||||
widget = QCalendarWidget()
|
||||
|
||||
dock_widget = QtAds.CDockWidget("Calendar {}".format(_State.calendar_count))
|
||||
_State.calendar_count += 1
|
||||
# The following lines are for testing the setWidget() and takeWidget()
|
||||
# functionality
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setWidget(widget) # what happens if we set a widget if a widget is already set
|
||||
dock_widget.takeWidget() # we remove the widget
|
||||
dock_widget.setWidget(widget) # and set the widget again - there should be no error
|
||||
dock_widget.setToggleViewActionMode(QtAds.CDockWidget.ActionModeShow)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/date_range.svg"))
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
def create_file_system_tree_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
|
||||
widget = QTreeView()
|
||||
widget.setFrameShape(QFrame.NoFrame)
|
||||
|
||||
m = QFileSystemModel(widget)
|
||||
m.setRootPath(QDir.currentPath())
|
||||
widget.setModel(m)
|
||||
|
||||
dock_widget = QtAds.CDockWidget("Filesystem {}".format(_State.file_system_count))
|
||||
_State.file_system_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
def create_editor_widget(view_menu: QMenu) -> QtAds.CDockWidget:
|
||||
widget = QPlainTextEdit()
|
||||
widget.setPlaceholderText("This is an editor. If you close the editor, it will be "
|
||||
"deleted. Enter your text here.")
|
||||
widget.setStyleSheet("border: none")
|
||||
dock_widget = QtAds.CDockWidget("Editor {}".format(_State.editor_count))
|
||||
_State.editor_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/edit.svg"))
|
||||
dock_widget.setFeature(QtAds.CDockWidget.CustomCloseHandling, True)
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
|
||||
options_menu = QMenu(dock_widget)
|
||||
options_menu.setTitle("Options")
|
||||
options_menu.setToolTip(options_menu.title())
|
||||
options_menu.setIcon(svg_icon(":/adsdemo/images/custom-menu-button.svg"))
|
||||
menu_action = options_menu.menuAction()
|
||||
# The object name of the action will be set for the QToolButton that
|
||||
# is created in the dock area title bar. You can use this name for CSS
|
||||
# styling
|
||||
menu_action.setObjectName("options_menu")
|
||||
dock_widget.setTitleBarActions([options_menu.menuAction()])
|
||||
a = options_menu.addAction("Clear Editor")
|
||||
a.triggered.connect(widget.clear)
|
||||
|
||||
return dock_widget
|
||||
|
||||
class CMinSizeTableWidget(QTableWidget):
|
||||
"""Custom QTableWidget with a minimum size hint to test CDockWidget
|
||||
@@ -162,43 +78,6 @@ class CMinSizeTableWidget(QTableWidget):
|
||||
def minimumSizeHint(self) -> QSize:
|
||||
return QSize(300, 100)
|
||||
|
||||
def create_table_widget(view_menu: QMenu) -> QtAds.CDockWidget:
|
||||
widget = CMinSizeTableWidget()
|
||||
dock_widget = QtAds.CDockWidget("Table {}".format(_State.table_count))
|
||||
_State.table_count += 1
|
||||
COLCOUNT = 5
|
||||
ROWCOUNT = 30
|
||||
widget.setColumnCount(COLCOUNT)
|
||||
widget.setRowCount(ROWCOUNT)
|
||||
for col in range(ROWCOUNT):
|
||||
widget.setHorizontalHeaderItem(col, QTableWidgetItem("Col {}".format(col+1)))
|
||||
for row in range(ROWCOUNT):
|
||||
widget.setItem(row, col, QTableWidgetItem("T {:}-{:}".format(row+1, col+1)))
|
||||
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/grid_on.svg"))
|
||||
dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromContent)
|
||||
toolbar = dock_widget.createDefaultToolBar()
|
||||
action = toolbar.addAction(svg_icon(":/adsdemo/images/fullscreen.svg"), "Toggle Fullscreen")
|
||||
def on_toggle_fullscreen():
|
||||
if dock_widget.isFullScreen():
|
||||
dock_widget.showNormal()
|
||||
else:
|
||||
dock_widget.showFullScreen()
|
||||
action.triggered.connect(on_toggle_fullscreen)
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
if ACTIVEX_AVAILABLE:
|
||||
def create_activex_widget(view_menu: QMenu, parent: QWidget = None) -> QtAds.CDockWidget:
|
||||
widget = QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent)
|
||||
dock_widget = QtAds.CDockWidget("Active X {}".format(_State.activex_count))
|
||||
_State.activex_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
view_menu.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
class CCustomComponentsFactory(QtAds.CDockComponentsFactory):
|
||||
|
||||
@@ -225,6 +104,8 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
self.perspective_list_action = None
|
||||
self.perspective_combo_box = None
|
||||
self.dock_manager = None
|
||||
self.window_title_test_dock_widget = None
|
||||
self.last_docked_editor = None
|
||||
|
||||
self.setupUi(self)
|
||||
self.create_actions()
|
||||
@@ -235,37 +116,45 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
|
||||
# uncomment the following line if you want to use opaque undocking and
|
||||
# opaque splitter resizing
|
||||
#QtAds.CDockManager.setConfigFlags(QtAds.CDockManager.DefaultOpaqueConfig);
|
||||
#QtAds.CDockManager.setConfigFlags(QtAds.CDockManager.DefaultOpaqueConfig)
|
||||
|
||||
# uncomment the following line if you want a fixed tab width that does
|
||||
# not change if the visibility of the close button changes
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.RetainTabSizeWhenCloseButtonHidden, True)
|
||||
|
||||
# uncomment the following line if you don't want close button on DockArea's title bar
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasCloseButton, false);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasCloseButton, False)
|
||||
|
||||
# uncomment the following line if you don't want undock button on DockArea's title bar
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasUndockButton, false);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasUndockButton, False)
|
||||
|
||||
# uncomment the following line if you don't want tabs menu button on DockArea's title bar
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasTabsMenuButton, false);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHasTabsMenuButton, False)
|
||||
|
||||
# uncomment the following line if you don't want disabled buttons to appear on DockArea's title bar
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHideDisabledButtons, true);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaHideDisabledButtons, True)
|
||||
|
||||
# uncomment the following line if you want to show tabs menu button on DockArea's title bar only when there are more than one tab and at least of them has elided title
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaDynamicTabsMenuButtonVisibility, true);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.DockAreaDynamicTabsMenuButtonVisibility, True)
|
||||
|
||||
# uncomment the following line if you want floating container to always show application title instead of active dock widget's title
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FloatingContainerHasWidgetTitle, false);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FloatingContainerHasWidgetTitle, False)
|
||||
|
||||
# uncomment the following line if you want floating container to show active dock widget's icon instead of always showing application icon
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FloatingContainerHasWidgetIcon, true);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FloatingContainerHasWidgetIcon, True)
|
||||
|
||||
# uncomment the following line if you want a central widget in the main dock container (the dock manager) without a titlebar
|
||||
# If you enable this code, you can test it in the demo with the Calendar 0
|
||||
# dock widget.
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.HideSingleCentralWidgetTitleBar, true);
|
||||
#QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.HideSingleCentralWidgetTitleBar, True)
|
||||
|
||||
# uncomment the following line to enable focus highlighting of the dock
|
||||
# widget that has the focus
|
||||
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||
|
||||
# uncomment if you would like to enable an equal distribution of the
|
||||
# available size of a splitter to all contained dock widgets
|
||||
# QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.EqualSplitOnInsertion, True)
|
||||
|
||||
# Now create the dock manager and its content
|
||||
self.dock_manager = QtAds.CDockManager(self)
|
||||
@@ -288,8 +177,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
|
||||
def create_content(self):
|
||||
# Test container docking
|
||||
view_menu = self.menuView
|
||||
dock_widget = create_calendar_dock_widget(view_menu)
|
||||
dock_widget = self.create_calendar_dock_widget()
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetClosable, False)
|
||||
special_dock_area = self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
|
||||
|
||||
@@ -297,8 +185,11 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
special_dock_area.setAllowedAreas(QtAds.OuterDockAreas)
|
||||
# special_dock_area.setAllowedAreas(QtAds.LeftDockWidgetArea | QtAds.RightDockWidgetArea) # just for testing
|
||||
|
||||
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, create_long_text_label_dock_widget(view_menu))
|
||||
file_system_widget = create_file_system_tree_dock_widget(view_menu)
|
||||
dock_widget = self.create_long_text_label_dock_widget()
|
||||
self.window_title_test_dock_widget = dock_widget
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetFocusable, False)
|
||||
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
|
||||
file_system_widget = self.create_file_system_tree_dock_widget()
|
||||
tool_bar = file_system_widget.createDefaultToolBar()
|
||||
tool_bar.addAction(self.actionSaveState)
|
||||
tool_bar.addAction(self.actionRestoreState)
|
||||
@@ -306,10 +197,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
append_feature_string_to_window_title(file_system_widget)
|
||||
self.dock_manager.addDockWidget(QtAds.BottomDockWidgetArea, file_system_widget)
|
||||
|
||||
file_system_widget = create_file_system_tree_dock_widget(view_menu)
|
||||
tool_bar = file_system_widget.createDefaultToolBar()
|
||||
tool_bar.addAction(self.actionSaveState)
|
||||
tool_bar.addAction(self.actionRestoreState)
|
||||
file_system_widget = self.create_file_system_tree_dock_widget()
|
||||
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetMovable, False)
|
||||
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetFloatable, False)
|
||||
append_feature_string_to_window_title(file_system_widget)
|
||||
@@ -317,13 +205,27 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
# Test custom factory - we inject a help button into the title bar
|
||||
QtAds.CDockComponentsFactory.setFactory(CCustomComponentsFactory())
|
||||
top_dock_area = self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, file_system_widget)
|
||||
# Uncomment the next line if you would like to test the
|
||||
# HideSingleWidgetTitleBar functionality
|
||||
# top_dock_area.setDockAreaFlag(QtAds.CDockAreaWidget.HideSingleWidgetTitleBar, True)
|
||||
QtAds.CDockComponentsFactory.resetDefaultFactory()
|
||||
|
||||
# We create a calendar widget and clear all flags to prevent the dock area
|
||||
# from closing
|
||||
dock_widget = create_calendar_dock_widget(view_menu)
|
||||
dock_widget = self.create_calendar_dock_widget()
|
||||
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
|
||||
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
|
||||
# new editor widgets when clicked
|
||||
@@ -336,7 +238,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
index = title_bar.indexOf(title_bar.tabBar())
|
||||
title_bar.insertWidget(index + 1, custom_button)
|
||||
def on_button_clicked():
|
||||
dock_widget = create_editor_widget(self.menuView)
|
||||
dock_widget = self.create_editor_widget()
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||
self.dock_manager.addDockWidgetTabToArea(dock_widget, dock_area)
|
||||
dock_widget.closeRequested.connect(self.on_editor_close_requested)
|
||||
@@ -345,21 +247,21 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
# Test dock area docking
|
||||
right_dock_area = self.dock_manager.addDockWidget(
|
||||
QtAds.RightDockWidgetArea,
|
||||
create_long_text_label_dock_widget(view_menu), top_dock_area)
|
||||
self.create_long_text_label_dock_widget(), top_dock_area)
|
||||
self.dock_manager.addDockWidget(
|
||||
QtAds.TopDockWidgetArea,
|
||||
create_long_text_label_dock_widget(view_menu), right_dock_area)
|
||||
self.create_long_text_label_dock_widget(), right_dock_area)
|
||||
|
||||
bottom_dock_area = self.dock_manager.addDockWidget(
|
||||
QtAds.BottomDockWidgetArea,
|
||||
create_long_text_label_dock_widget(view_menu), right_dock_area)
|
||||
self.create_long_text_label_dock_widget(), right_dock_area)
|
||||
|
||||
self.dock_manager.addDockWidget(
|
||||
QtAds.CenterDockWidgetArea,
|
||||
create_long_text_label_dock_widget(view_menu), right_dock_area)
|
||||
self.create_long_text_label_dock_widget(), right_dock_area)
|
||||
self.dock_manager.addDockWidget(
|
||||
QtAds.CenterDockWidgetArea,
|
||||
create_long_text_label_dock_widget(view_menu), bottom_dock_area)
|
||||
self.create_long_text_label_dock_widget(), bottom_dock_area)
|
||||
|
||||
|
||||
action = self.menuTests.addAction("Set {} Floating".format(dock_widget.windowTitle()))
|
||||
@@ -373,7 +275,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
flags = self.dock_manager.configFlags()
|
||||
if flags & QtAds.CDockManager.OpaqueUndocking:
|
||||
self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea,
|
||||
create_activex_widget(view_menu), right_dock_area)
|
||||
self.create_activex_widget(), right_dock_area)
|
||||
|
||||
for dock_widget in self.dock_manager.dockWidgetsMap().values():
|
||||
dock_widget.viewToggled.connect(self.on_view_toggled)
|
||||
@@ -425,6 +327,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
|
||||
def closeEvent(self, event: QCloseEvent):
|
||||
self.save_state()
|
||||
self.dock_manager.deleteLater()
|
||||
super().closeEvent(event)
|
||||
|
||||
def on_actionSaveState_triggered(self, state: bool):
|
||||
@@ -460,21 +363,27 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
if dock_widget is None:
|
||||
return
|
||||
|
||||
qDebug("{} visibility_changed({})".format(dock_widget.objectName(), visible))
|
||||
# qDebug("{} visibility_changed({})".format(dock_widget.objectName(), visible))
|
||||
|
||||
def create_editor(self):
|
||||
sender = self.sender()
|
||||
floating = sender.property("Floating")
|
||||
print("Floating:", floating)
|
||||
dock_widget = create_editor_widget(self.menuView)
|
||||
dock_widget = self.create_editor_widget()
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetForceCloseWithArea, True)
|
||||
dock_widget.closeRequested.connect(self.on_editor_close_requested)
|
||||
|
||||
if floating:
|
||||
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
|
||||
floating_widget.move(QPoint(20, 20))
|
||||
else:
|
||||
self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, dock_widget)
|
||||
editor_area = self.last_docked_editor.dockAreaWidget() if self.last_docked_editor is not None else None
|
||||
if editor_area is not None:
|
||||
self.dock_manager.setConfigFlag(QtAds.CDockManager.EqualSplitOnInsertion, True)
|
||||
self.dock_manager.addDockWidget(QtAds.RightDockWidgetArea, dock_widget, editor_area)
|
||||
else:
|
||||
self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, dock_widget)
|
||||
self.last_docked_editor = dock_widget
|
||||
|
||||
def on_editor_close_requested(self):
|
||||
dock_widget = self.sender()
|
||||
@@ -484,7 +393,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
dock_widget.closeDockWidget()
|
||||
|
||||
def create_table(self):
|
||||
dock_widget = create_table_widget(self.menuView)
|
||||
dock_widget = self.create_table_widget()
|
||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
|
||||
floating_widget.move(QPoint(40, 40))
|
||||
@@ -493,6 +402,16 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
dialog = CStatusDialog(self.dock_manager)
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
def toggle_dock_widget_window_title(self):
|
||||
title = self.window_title_test_dock_widget.windowTitle()
|
||||
i = title.find(" (Test) ")
|
||||
if i == -1:
|
||||
title += " (Test) "
|
||||
else:
|
||||
title = title[i]
|
||||
self.window_title_test_dock_widget.setWindowTitle(title)
|
||||
|
||||
def save_state(self):
|
||||
'''
|
||||
Saves the dock manager state and the main window geometry
|
||||
@@ -545,6 +464,124 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
self.perspective_combo_box.setCurrentText(perspective_name)
|
||||
self.save_perspectives()
|
||||
|
||||
def create_long_text_label_dock_widget(self) -> QtAds.CDockWidget:
|
||||
label = QLabel()
|
||||
label.setWordWrap(True)
|
||||
label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
label.setText('''Label {} {} - Lorem ipsum dolor sit amet, consectetuer
|
||||
adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum
|
||||
sociis natoque penatibus et magnis dis parturient montes, nascetur
|
||||
ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium
|
||||
quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla
|
||||
vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut,
|
||||
imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis
|
||||
pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi.
|
||||
Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu,
|
||||
consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra
|
||||
quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet.
|
||||
'''.format(_State.label_count, datetime.datetime.now().strftime("%H:%M:%S:%f")))
|
||||
dock_widget = QtAds.CDockWidget("Label {}".format(_State.label_count))
|
||||
_State.label_count += 1
|
||||
dock_widget.setWidget(label)
|
||||
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
def create_calendar_dock_widget(self) -> QtAds.CDockWidget:
|
||||
widget = QCalendarWidget()
|
||||
|
||||
dock_widget = QtAds.CDockWidget("Calendar {}".format(_State.calendar_count))
|
||||
_State.calendar_count += 1
|
||||
# The following lines are for testing the setWidget() and takeWidget()
|
||||
# functionality
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setWidget(widget) # what happens if we set a widget if a widget is already set
|
||||
dock_widget.takeWidget() # we remove the widget
|
||||
dock_widget.setWidget(widget) # and set the widget again - there should be no error
|
||||
dock_widget.setToggleViewActionMode(QtAds.CDockWidget.ActionModeShow)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/date_range.svg"))
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
def create_file_system_tree_dock_widget(self) -> QtAds.CDockWidget:
|
||||
widget = QTreeView()
|
||||
widget.setFrameShape(QFrame.NoFrame)
|
||||
|
||||
m = QFileSystemModel(widget)
|
||||
m.setRootPath(QDir.currentPath())
|
||||
widget.setModel(m)
|
||||
|
||||
dock_widget = QtAds.CDockWidget("Filesystem {}".format(_State.file_system_count))
|
||||
_State.file_system_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
def create_editor_widget(self) -> QtAds.CDockWidget:
|
||||
widget = QPlainTextEdit()
|
||||
widget.setPlaceholderText("This is an editor. If you close the editor, it will be "
|
||||
"deleted. Enter your text here.")
|
||||
widget.setStyleSheet("border: none")
|
||||
dock_widget = QtAds.CDockWidget("Editor {}".format(_State.editor_count))
|
||||
_State.editor_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/edit.svg"))
|
||||
dock_widget.setFeature(QtAds.CDockWidget.CustomCloseHandling, True)
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
|
||||
options_menu = QMenu(dock_widget)
|
||||
options_menu.setTitle("Options")
|
||||
options_menu.setToolTip(options_menu.title())
|
||||
options_menu.setIcon(svg_icon(":/adsdemo/images/custom-menu-button.svg"))
|
||||
menu_action = options_menu.menuAction()
|
||||
# The object name of the action will be set for the QToolButton that
|
||||
# is created in the dock area title bar. You can use this name for CSS
|
||||
# styling
|
||||
menu_action.setObjectName("options_menu")
|
||||
dock_widget.setTitleBarActions([options_menu.menuAction()])
|
||||
a = options_menu.addAction("Clear Editor")
|
||||
a.triggered.connect(widget.clear)
|
||||
|
||||
return dock_widget
|
||||
|
||||
|
||||
def create_table_widget(self) -> QtAds.CDockWidget:
|
||||
widget = CMinSizeTableWidget()
|
||||
dock_widget = QtAds.CDockWidget("Table {}".format(_State.table_count))
|
||||
_State.table_count += 1
|
||||
COLCOUNT = 5
|
||||
ROWCOUNT = 30
|
||||
widget.setColumnCount(COLCOUNT)
|
||||
widget.setRowCount(ROWCOUNT)
|
||||
for col in range(ROWCOUNT):
|
||||
widget.setHorizontalHeaderItem(col, QTableWidgetItem("Col {}".format(col + 1)))
|
||||
for row in range(ROWCOUNT):
|
||||
widget.setItem(row, col, QTableWidgetItem("T {:}-{:}".format(row + 1, col + 1)))
|
||||
|
||||
dock_widget.setWidget(widget)
|
||||
dock_widget.setIcon(svg_icon(":/adsdemo/images/grid_on.svg"))
|
||||
dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromContent)
|
||||
toolbar = dock_widget.createDefaultToolBar()
|
||||
action = toolbar.addAction(svg_icon(":/adsdemo/images/fullscreen.svg"), "Toggle Fullscreen")
|
||||
|
||||
def on_toggle_fullscreen():
|
||||
if dock_widget.isFullScreen():
|
||||
dock_widget.showNormal()
|
||||
else:
|
||||
dock_widget.showFullScreen()
|
||||
|
||||
action.triggered.connect(on_toggle_fullscreen)
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
def create_activex_widget(self, parent: QWidget = None) -> QtAds.CDockWidget:
|
||||
widget = QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent)
|
||||
dock_widget = QtAds.CDockWidget("Active X {}".format(_State.activex_count))
|
||||
_State.activex_count += 1
|
||||
dock_widget.setWidget(widget)
|
||||
self.menuView.addAction(dock_widget.toggleViewAction())
|
||||
return dock_widget
|
||||
|
||||
|
||||
def my_message_output(type, context, msg):
|
||||
if type == QtDebugMsg:
|
||||
BIN
doc/central_widget.gif
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
doc/cfg_flag_EqualSplitOnInsertion_false.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
doc/cfg_flag_EqualSplitOnInsertion_true.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
doc/cfg_flag_FloatingContainerForceNativeTitleBar_false.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/cfg_flag_FloatingContainerForceNativeTitleBar_true.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/cfg_flag_FocusHighlighting.gif
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
doc/python_logo.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/showcase_d-tect-x.jpg
Normal file
|
After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 385 KiB After Width: | Height: | Size: 385 KiB |
BIN
doc/showcase_hivewe.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 539 KiB After Width: | Height: | Size: 539 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 157 KiB |
BIN
doc/showcase_qt_design_studio_video.png
Normal file
|
After Width: | Height: | Size: 816 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
BIN
doc/showcase_ramses_composer.png
Normal file
|
After Width: | Height: | Size: 450 KiB |
@@ -23,13 +23,20 @@
|
||||
- [`FloatingContainerHasWidgetTitle`](#floatingcontainerhaswidgettitle)
|
||||
- [`FloatingContainerHasWidgetIcon`](#floatingcontainerhaswidgeticon)
|
||||
- [`HideSingleCentralWidgetTitleBar`](#hidesinglecentralwidgettitlebar)
|
||||
- [`FocusHighlighting`](#focushighlighting)
|
||||
- [`EqualSplitOnInsertion`](#equalsplitoninsertion)
|
||||
- [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only)
|
||||
- [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only)
|
||||
- [Central Widget](#central-widget)
|
||||
- [Empty Dock Area](#empty-dock-area)
|
||||
- [Custom Close Handling](#custom-close-handling)
|
||||
- [Styling](#styling)
|
||||
- [Disabling the Internal Style Sheet](#disabling-the-internal-style-sheet)
|
||||
|
||||
## Configuration Flags
|
||||
|
||||
The Advanced Docking System has a number of global configuration options to
|
||||
configure the design and the functionality of the docking system. Each
|
||||
configure the design and the functionality of the docking system. Eachs
|
||||
configuration will be explained in detail in the following sections.
|
||||
|
||||
### Setting Configuration Flags
|
||||
@@ -284,7 +291,8 @@ otherwise (default setting) it displays application icon.
|
||||
|
||||
### `HideSingleCentralWidgetTitleBar`
|
||||
|
||||
If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden.
|
||||
If there is only one single visible dock widget in the main dock container (the dock manager)
|
||||
and if this flag is set, then the titlebar of this dock widget will be hidden.
|
||||
This only makes sense for non draggable and non floatable dock widgets and enables
|
||||
the creation of some kind of "central" static widget. Because the titlebar is
|
||||
hidden, it is not possible to drag out the central widget to make it floating
|
||||
@@ -299,6 +307,233 @@ still has a titlebar to drag it out of the main window.
|
||||
|
||||

|
||||
|
||||
### `FocusHighlighting`
|
||||
|
||||
If this is enabled, the docking system is able to highlight the tab and the
|
||||
components of a dock area with a different style (i.e. a different color).
|
||||
This option is disabled by default and needs to be enabled explicitely
|
||||
because it adds some overhead. The dock manager needs to react on focus
|
||||
changes and dock widget dragging to highlight the right dock widget. You should
|
||||
enable it only, if you really need it for your application.
|
||||
|
||||
If the feature is enabled, you can also connect to the new dock manager
|
||||
signal `focusedDockWidgetChanged(CDockWidget* old, CDockWidget* now)` to
|
||||
react on focus changes and to prepare the content of the focused dock
|
||||
widget.
|
||||
|
||||
You can click into the tab, the titlebar or the content of a dock widget
|
||||
to focus it.
|
||||
|
||||

|
||||
|
||||
For the focused dock widget and dock widget tab, the property `focused` will
|
||||
be set to true and you can use this property to style the focused dock
|
||||
widget differently. The picture above uses the following styling:
|
||||
|
||||
```css
|
||||
/* Color the tab with the nhighlight color */
|
||||
ads--CDockWidgetTab[focused="true"]
|
||||
{
|
||||
background: palette(highlight);
|
||||
border-color: palette(highlight);
|
||||
}
|
||||
|
||||
/* Use a different colored close button icon to match the test color */
|
||||
ads--CDockWidgetTab[focused="true"] > #tabCloseButton
|
||||
{
|
||||
qproperty-icon: url(:/ads/images/close-button-focused.svg)
|
||||
}
|
||||
|
||||
/* Make a hovered focused close button a little bit lighter */
|
||||
ads--CDockWidgetTab[focused="true"] > #tabCloseButton:hover
|
||||
{
|
||||
background: rgba(255, 255, 255, 48);
|
||||
}
|
||||
|
||||
/* Make a pressed focused close button even more lighter */
|
||||
ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed
|
||||
{
|
||||
background: rgba(255, 255, 255, 92);
|
||||
}
|
||||
|
||||
/* Use a different color for the tab label */
|
||||
ads--CDockWidgetTab[focused="true"] QLabel
|
||||
{
|
||||
color: palette(light);
|
||||
}
|
||||
|
||||
/* Paint a nice solid line for the whole title bar to create the illusion
|
||||
of an active tab */
|
||||
ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar
|
||||
{
|
||||
background: transparent;
|
||||
border-bottom: 2px solid palette(highlight);
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
```
|
||||
|
||||
If you have a content widget that does not support focussing for some reason
|
||||
(like `QVTKOpenGLStereoWidget` from the [VTK library](https://github.com/Kitware/VTK)),
|
||||
then you can manually switch the focus by reacting on mouse events. The
|
||||
following code shows, how to install en event filter on the `QVTKOpenGLStereoWidget`
|
||||
to properly switch the focus on `QEvent::MouseButtonPress`:
|
||||
|
||||
```c++
|
||||
static ads::CDockWidget* createVTK2DWindow(QMenu* ViewMenu, QObject* EventFilter)
|
||||
{
|
||||
QVTKOpenGLStereoWidget* qvtkOpenGLStereoWidget = new QVTKOpenGLStereoWidget;
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget("2D Window");
|
||||
DockWidget->setWidget(qvtkOpenGLStereoWidget);
|
||||
qvtkOpenGLStereoWidget->installEventFilter(EventFilter);
|
||||
qvtkOpenGLStereoWidget->setProperty("DockWidget", QVariant::fromValue(DockWidget));
|
||||
return DockWidget;
|
||||
}
|
||||
```
|
||||
|
||||
Now we can use the event filter function to react on mouse events and then
|
||||
use the dock manager function `setDockWidgetFocused()` to switch the focus:
|
||||
|
||||
```c++
|
||||
bool CMainWindow::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QVTKOpenGLStereoWidget* vtkWidget = qobject_cast<QVTKOpenGLStereoWidget*>(watched);
|
||||
auto vDockWidget = vtkWidget->property("DockWidget");
|
||||
ads::CDockWidget* DockWidget = nullptr;
|
||||
if (vDockWidget.isValid())
|
||||
{
|
||||
DockWidget = qvariant_cast<ads::CDockWidget*>(vDockWidget);
|
||||
}
|
||||
|
||||
if (DockWidget)
|
||||
{
|
||||
d->DockManager->setDockWidgetFocused(DockWidget);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### `EqualSplitOnInsertion`
|
||||
|
||||
This flag configures how the space is distributed if a new dock widget is
|
||||
inserted into an existing dock area. The flag is disabled by default. If 3
|
||||
dock widgets are inserted with the following code
|
||||
|
||||
```c++
|
||||
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, EditorArea);
|
||||
```
|
||||
|
||||
then this is the result, if the flag is disabled:
|
||||
|
||||

|
||||
|
||||
If the flag is enabled, then the space is equally distributed to all widgets
|
||||
in a splitter:
|
||||
|
||||

|
||||
|
||||
|
||||
### `FloatingContainerForceNativeTitleBar` (Linux only)
|
||||
|
||||
Since release 3.6 the library supports native title bars and window decorations
|
||||
for floating widgets on Linux (thanks to a user contribution).
|
||||
Native title bars and window decorations are supported by most Linux window
|
||||
managers, such as Compiz or Xfwm. Some window managers like KWin do not properly
|
||||
support this feature. Native floating widgets look better because of the native
|
||||
styling and the support all window manager features like snapping to window
|
||||
borders or maximizing. The library tries to detect the window manager during
|
||||
runtime and activates native window decorations if possible:
|
||||
|
||||

|
||||
|
||||
If you would like to overwrite this autodetection, then you can activate this
|
||||
flag to force native window titlebars. You can overwrite autodetection and this
|
||||
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
|
||||
|
||||
### `FloatingContainerForceQWidgetTitleBar` (Linux only)
|
||||
|
||||
If your window manager (i.e. KWin) does not properly support native floating
|
||||
windows, the docking library falls back to QWidget based floating widget
|
||||
title bars.
|
||||
|
||||

|
||||
|
||||
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
|
||||
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
|
||||
|
||||
## Central Widget
|
||||
|
||||
The Advanced Docking System has been developed to overcome the limitations of
|
||||
the native Qt docking system with its central widget concept. This was the
|
||||
reason that until version 3.6 of the library, there was no support for such
|
||||
thing like a central widget. Thanks to the contribution of a user the library
|
||||
now supports a central widget.
|
||||
|
||||
In the Advanced Docking System a central widget is a docking widget that is
|
||||
neither closable nor movable or floatable. A central widget has no title bar
|
||||
and so it is not possible for the user to hide, close or drag the central
|
||||
widget. If there is a central widget, then also the distribution of the sizes
|
||||
for the dock widgets around the central widget is different:
|
||||
|
||||
- **no central widget (default)** - on resizing the available space is
|
||||
distributed to all dock widgets - the size of all dock widgets
|
||||
shrinks or grows
|
||||
- **with central widget** - on resizing only the central widget is resized - the
|
||||
dock widgets around the central widget keep their size (see the animation below)
|
||||
|
||||

|
||||
|
||||
To set a central widget, you just need to pass your central dock widget
|
||||
to the dock manager `setCentralWidget` function:
|
||||
|
||||
```c++
|
||||
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||
```
|
||||
|
||||
See the `centralwidget` example to learn how it works.
|
||||
|
||||
> ##### Note
|
||||
> The central widget needs to be the first dock widget that is added to the
|
||||
> dock manager. The function does not work and returns a `nullptr` if there
|
||||
> are already other dock widgets registered. So `setCentralWidget` should be
|
||||
> the first function that you call when adding dock widgets.
|
||||
|
||||
## Empty Dock Area
|
||||
|
||||
Some applications require a fixed DockArea that is always visible, even if it
|
||||
does not contain any DockWidgets. I.e. the DockArea is in this case a kind
|
||||
of central widget that is always visible (see this
|
||||
[issue](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/199)).
|
||||
|
||||
Since version 3.7.1 the advanced docking system supports this feature. The
|
||||
`emptydockarea` example shows how this can be implemented with the library. You
|
||||
just need to create a dock widget and set the feature flag `CDockWidget::NoTab`.
|
||||
This permanently hides the tab widget of this area and removes it from the tab
|
||||
menu. For this special dock widget you should also disable all other features
|
||||
(movable, closable and floatable) to prevent closing and moving of this widget.
|
||||
If you use the `CDockManager::setCentralWidget` function like in the example
|
||||
code below, then you don't need to disable these features because this is done
|
||||
in the `setCentralWidget` function.
|
||||
|
||||
```c++
|
||||
QLabel* label = new QLabel();
|
||||
label->setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.");
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||
CentralDockWidget->setWidget(label);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);// set the flag before adding the widget to dock manager
|
||||
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||
```
|
||||
|
||||
## Custom Close Handling
|
||||
|
||||
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the `toggleView()` action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||
|
||||
When an entire area is closed, the default behavior is to hide the dock widgets it contains regardless of the `DockWidgetDeleteOnClose` flag except if there is only one dock widget. In this special case, the `DockWidgetDeleteOnClose` flag is followed. This behavior can be changed by setting the `DockWidgetForceCloseWithArea` flag to all the dock widgets that needs to be closed with their area.
|
||||
|
||||
## Styling
|
||||
|
||||
The Advanced Docking System supports styling via [Qt Style Sheets](https://doc.qt.io/qt-5/stylesheet.html). All components like splitters, tabs, buttons, titlebar and
|
||||
|
||||
@@ -2,4 +2,7 @@ cmake_minimum_required(VERSION 3.5)
|
||||
project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT})
|
||||
add_subdirectory(simple)
|
||||
add_subdirectory(sidebar)
|
||||
add_subdirectory(deleteonclose)
|
||||
add_subdirectory(deleteonclose)
|
||||
add_subdirectory(centralwidget)
|
||||
add_subdirectory(emptydockarea)
|
||||
add_subdirectory(dockindock)
|
||||
28
examples/centralwidget/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||
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)
|
||||
add_executable(CentralWidgetExample WIN32
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.ui
|
||||
)
|
||||
target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking)
|
||||
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
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION ${VERSION_SHORT}
|
||||
EXPORT_NAME "Qt Advanced Docking System Central Widget Example"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||
)
|
||||
34
examples/centralwidget/centralwidget.pro
Normal file
@@ -0,0 +1,34 @@
|
||||
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
TARGET = CentralWidgetExample
|
||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||
TEMPLATE = app
|
||||
CONFIG += c++14
|
||||
CONFIG += debug_and_release
|
||||
adsBuildStatic {
|
||||
DEFINES += ADS_STATIC
|
||||
}
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
10
examples/centralwidget/main.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <mainwindow.h>
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
CMainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
106
examples/centralwidget/main.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt, QTimer, QDir, QSignalBlocker
|
||||
from PyQt5.QtGui import QCloseEvent, QIcon
|
||||
from PyQt5.QtWidgets import (QApplication, QLabel, QCalendarWidget, QFrame, QTreeView,
|
||||
QTableWidget, QFileSystemModel, QPlainTextEdit, QToolBar,
|
||||
QWidgetAction, QComboBox, QAction, QSizePolicy, QInputDialog)
|
||||
|
||||
from PyQtAds import QtAds
|
||||
|
||||
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||
|
||||
class MainWindow(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
|
||||
text_edit = QPlainTextEdit()
|
||||
text_edit.setPlaceholderText("This is the central editor. Enter your text here.")
|
||||
central_dock_widget = QtAds.CDockWidget("CentralWidget")
|
||||
central_dock_widget.setWidget(text_edit)
|
||||
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
|
||||
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
||||
|
||||
# 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)
|
||||
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())
|
||||
|
||||
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.create_perspective_ui()
|
||||
|
||||
def create_perspective_ui(self):
|
||||
save_perspective_action = QAction("Create Perspective", self)
|
||||
save_perspective_action.triggered.connect(self.save_perspective)
|
||||
perspective_list_action = QWidgetAction(self)
|
||||
self.perspective_combobox = QComboBox(self)
|
||||
self.perspective_combobox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||
self.perspective_combobox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
self.perspective_combobox.activated[str].connect(self.dock_manager.openPerspective)
|
||||
perspective_list_action.setDefaultWidget(self.perspective_combobox)
|
||||
self.toolBar.addSeparator()
|
||||
self.toolBar.addAction(perspective_list_action)
|
||||
self.toolBar.addAction(save_perspective_action)
|
||||
|
||||
def save_perspective(self):
|
||||
perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter Unique name:")
|
||||
if not ok or not perspective_name:
|
||||
return
|
||||
|
||||
self.dock_manager.addPerspective(perspective_name)
|
||||
blocker = QSignalBlocker(self.perspective_combobox)
|
||||
self.perspective_combobox.clear()
|
||||
self.perspective_combobox.addItems(self.dock_manager.perspectiveNames())
|
||||
self.perspective_combobox.setCurrentText(perspective_name)
|
||||
|
||||
def closeEvent(self, event: QCloseEvent):
|
||||
self.dock_manager.deleteLater()
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
w = MainWindow()
|
||||
w.show()
|
||||
app.exec_()
|
||||
133
examples/centralwidget/mainwindow.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QWidgetAction>
|
||||
#include <QLabel>
|
||||
#include <QCalendarWidget>
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
#include <QTableWidget>
|
||||
#include <QHBoxLayout>
|
||||
#include <QRadioButton>
|
||||
#include <QPushButton>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QSettings>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QToolBar>
|
||||
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockAreaTitleBar.h"
|
||||
#include "DockAreaTabBar.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
|
||||
using namespace ads;
|
||||
|
||||
|
||||
CMainWindow::CMainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::CMainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
||||
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
||||
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||
DockManager = new CDockManager(this);
|
||||
|
||||
// Set central widget
|
||||
QPlainTextEdit* w = new QPlainTextEdit();
|
||||
w->setPlaceholderText("This is the central editor. Enter your text here.");
|
||||
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||
CentralDockWidget->setWidget(w);
|
||||
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||
CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas);
|
||||
|
||||
// create other dock widgets
|
||||
QTableWidget* table = new QTableWidget();
|
||||
table->setColumnCount(3);
|
||||
table->setRowCount(10);
|
||||
CDockWidget* TableDockWidget = new CDockWidget("Table 1");
|
||||
TableDockWidget->setWidget(table);
|
||||
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
TableDockWidget->resize(250, 150);
|
||||
TableDockWidget->setMinimumSize(200,150);
|
||||
auto TableArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, TableDockWidget);
|
||||
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||
|
||||
table = new QTableWidget();
|
||||
table->setColumnCount(5);
|
||||
table->setRowCount(1020);
|
||||
TableDockWidget = new CDockWidget("Table 2");
|
||||
TableDockWidget->setWidget(table);
|
||||
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
TableDockWidget->resize(250, 150);
|
||||
TableDockWidget->setMinimumSize(200,150);
|
||||
DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, TableArea);
|
||||
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||
|
||||
QTableWidget* propertiesTable = new QTableWidget();
|
||||
propertiesTable->setColumnCount(3);
|
||||
propertiesTable->setRowCount(10);
|
||||
CDockWidget* PropertiesDockWidget = new CDockWidget("Properties");
|
||||
PropertiesDockWidget->setWidget(propertiesTable);
|
||||
PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
PropertiesDockWidget->resize(250, 150);
|
||||
PropertiesDockWidget->setMinimumSize(200,150);
|
||||
DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea);
|
||||
ui->menuView->addAction(PropertiesDockWidget->toggleViewAction());
|
||||
|
||||
createPerspectiveUi();
|
||||
}
|
||||
|
||||
CMainWindow::~CMainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
void CMainWindow::createPerspectiveUi()
|
||||
{
|
||||
SavePerspectiveAction = new QAction("Create Perspective", this);
|
||||
connect(SavePerspectiveAction, SIGNAL(triggered()), SLOT(savePerspective()));
|
||||
PerspectiveListAction = new QWidgetAction(this);
|
||||
PerspectiveComboBox = new QComboBox(this);
|
||||
PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
connect(PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||
DockManager, SLOT(openPerspective(const QString&)));
|
||||
PerspectiveListAction->setDefaultWidget(PerspectiveComboBox);
|
||||
ui->toolBar->addSeparator();
|
||||
ui->toolBar->addAction(PerspectiveListAction);
|
||||
ui->toolBar->addAction(SavePerspectiveAction);
|
||||
}
|
||||
|
||||
|
||||
void CMainWindow::savePerspective()
|
||||
{
|
||||
QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:");
|
||||
if (PerspectiveName.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DockManager->addPerspective(PerspectiveName);
|
||||
QSignalBlocker Blocker(PerspectiveComboBox);
|
||||
PerspectiveComboBox->clear();
|
||||
PerspectiveComboBox->addItems(DockManager->perspectiveNames());
|
||||
PerspectiveComboBox->setCurrentText(PerspectiveName);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
// Delete dock manager here to delete all floating widgets. This ensures
|
||||
// that all top level windows of the dock manager are properly closed
|
||||
DockManager->deleteLater();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
|
||||
43
examples/centralwidget/mainwindow.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QComboBox>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#include "DockManager.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockWidget.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class CMainWindow; }
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class CMainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CMainWindow(QWidget *parent = nullptr);
|
||||
~CMainWindow();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
QAction* SavePerspectiveAction = nullptr;
|
||||
QWidgetAction* PerspectiveListAction = nullptr;
|
||||
QComboBox* PerspectiveComboBox = nullptr;
|
||||
|
||||
Ui::CMainWindow *ui;
|
||||
|
||||
ads::CDockManager* DockManager;
|
||||
ads::CDockAreaWidget* StatusDockArea;
|
||||
ads::CDockWidget* TimelineDockWidget;
|
||||
|
||||
void createPerspectiveUi();
|
||||
|
||||
private slots:
|
||||
void savePerspective();
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
47
examples/centralwidget/mainwindow.ui
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CMainWindow</class>
|
||||
<widget class="QMainWindow" name="CMainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1284</width>
|
||||
<height>757</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1284</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,13 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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)
|
||||
add_executable(DeleteOnCloseTest WIN32
|
||||
main.cpp
|
||||
)
|
||||
target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
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
|
||||
AUTOMOC ON
|
||||
CXX_STANDARD 14
|
||||
|
||||
@@ -17,23 +17,7 @@ SOURCES += main.cpp
|
||||
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
|
||||
@@ -5,26 +5,75 @@
|
||||
#include <QMenuBar>
|
||||
#include "DockManager.h"
|
||||
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
private:
|
||||
ads::CDockManager* m_DockManager = nullptr;
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event) override
|
||||
{
|
||||
QMainWindow::closeEvent(event);
|
||||
if (m_DockManager)
|
||||
{
|
||||
m_DockManager->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setDockManager(ads::CDockManager* DockManager) {m_DockManager = DockManager;}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
QMainWindow w;
|
||||
MainWindow w;
|
||||
|
||||
ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true);
|
||||
ads::CDockManager::setConfigFlag(ads::CDockManager::AllTabsHaveCloseButton, true);
|
||||
auto dockManager = new ads::CDockManager(&w);
|
||||
w.setDockManager(dockManager);
|
||||
QObject::connect(dockManager, &ads::CDockManager::focusedDockWidgetChanged, [] (ads::CDockWidget* old, ads::CDockWidget* now) {
|
||||
static int Count = 0;
|
||||
qDebug() << Count++ << " CDockManager::focusedDockWidgetChanged old: " << (old ? old->objectName() : "-") << " now: " << now->objectName() << " visible: " << now->isVisible();
|
||||
now->widget()->setFocus();
|
||||
});
|
||||
|
||||
QAction *action = new QAction("New Delete On Close", &w);
|
||||
QAction *action = new QAction("New [DockWidgetDeleteOnClose]", &w);
|
||||
w.menuBar()->addAction(action);
|
||||
|
||||
int i = 0;
|
||||
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);
|
||||
dw->setWidget(editor);
|
||||
dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||
auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw);
|
||||
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);
|
||||
w.menuBar()->addAction(action);
|
||||
QObject::connect(action, &QAction::triggered, [&]() {
|
||||
|
||||
73
examples/deleteonclose/main.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import sys
|
||||
|
||||
from PyQtAds import QtAds
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtCore import (qDebug, pyqtSlot, QObject, pyqtSignal)
|
||||
from PyQt5.QtWidgets import (QMainWindow, QAction, QTextEdit, QApplication,
|
||||
QMenuBar)
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
dock_manager = None
|
||||
|
||||
def closeEvent(self, event: QCloseEvent):
|
||||
super().closeEvent(event)
|
||||
if self.dock_manager is not None:
|
||||
self.dock_manager.deleteLater()
|
||||
|
||||
def setDockManager(self, dock_manager: QtAds.CDockManager):
|
||||
self.dock_manager = dock_manager
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
w = MainWindow()
|
||||
|
||||
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.AllTabsHaveCloseButton, True)
|
||||
dock_manager = QtAds.CDockManager(w)
|
||||
w.setDockManager(dock_manager)
|
||||
|
||||
count = 0
|
||||
|
||||
def on_focused_dock_widget_changed(old: QtAds.CDockWidget, now: QtAds.CDockWidget):
|
||||
global count
|
||||
qDebug( "{:d} CDockManager::focusedDockWidgetChanged old: {} now: {} visible: {}".format(
|
||||
count,
|
||||
old.objectName() if old else "-",
|
||||
now.objectName(),
|
||||
now.isVisible()))
|
||||
count += 1
|
||||
now.widget().setFocus()
|
||||
|
||||
dock_manager.focusedDockWidgetChanged.connect(on_focused_dock_widget_changed)
|
||||
|
||||
action = QAction("New Delete On Close", w)
|
||||
w.menuBar().addAction(action)
|
||||
|
||||
i = 0
|
||||
def on_action_triggered():
|
||||
global i
|
||||
dw = QtAds.CDockWidget("test doc {:d}".format(i))
|
||||
i += 1
|
||||
editor = QTextEdit("lorem ipsum...", dw)
|
||||
dw.setWidget(editor)
|
||||
dw.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||
area = dock_manager.addDockWidgetTab(QtAds.CenterDockWidgetArea, dw)
|
||||
qDebug("doc dock widget created! {} {}".format(dw, area))
|
||||
action.triggered.connect(on_action_triggered)
|
||||
|
||||
action = QAction("New", w)
|
||||
w.menuBar().addAction(action)
|
||||
def on_action2_triggered():
|
||||
global i
|
||||
dw = QtAds.CDockWidget("test {:d}".format(i))
|
||||
i += 1
|
||||
editor = QTextEdit("lorem ipsum...", dw)
|
||||
dw.setWidget(editor)
|
||||
area = dock_manager.addDockWidgetTab(QtAds.CenterDockWidgetArea, dw)
|
||||
qDebug("dock widget created! {} {}".format(dw, area))
|
||||
action.triggered.connect(on_action2_triggered)
|
||||
|
||||
w.show()
|
||||
app.exec_()
|
||||
31
examples/dockindock/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_dockindock VERSION ${VERSION_SHORT})
|
||||
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)
|
||||
add_executable(DockInDockExample WIN32
|
||||
dockindock.cpp
|
||||
dockindockmanager.cpp
|
||||
perspectiveactions.cpp
|
||||
perspectives.cpp
|
||||
main.cpp
|
||||
mainframe.cpp
|
||||
)
|
||||
target_include_directories(DockInDockExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
target_link_libraries(DockInDockExample PRIVATE qtadvanceddocking)
|
||||
target_link_libraries(DockInDockExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set_target_properties(DockInDockExample PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION ${VERSION_SHORT}
|
||||
EXPORT_NAME "Qt Advanced Docking System Simple Example"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||
)
|
||||
296
examples/dockindock/dockindock.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
#include "dockindock.h"
|
||||
#include "perspectives.h"
|
||||
#include "dockindockmanager.h"
|
||||
#include "perspectiveactions.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QDir>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <set>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace QtAdsUtl;
|
||||
|
||||
DockInDockWidget::DockInDockWidget( QWidget* parent, bool canCreateNewGroups, PerspectivesManager* perspectivesManager ) :
|
||||
DockInDockWidget( parent, (DockInDockWidget*)NULL, perspectivesManager )
|
||||
{
|
||||
m_canCreateNewGroups = canCreateNewGroups;
|
||||
m_topLevelDockWidget = this;
|
||||
}
|
||||
|
||||
DockInDockWidget::DockInDockWidget( QWidget* parent, DockInDockWidget* topLevelDockWidget, PerspectivesManager* perspectivesManager ) :
|
||||
baseClass( parent ),
|
||||
m_topLevelDockWidget( topLevelDockWidget ),
|
||||
m_canCreateNewGroups( (topLevelDockWidget) ? topLevelDockWidget->m_canCreateNewGroups : false ),
|
||||
m_perspectivesManager( perspectivesManager )
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins( 0,0,0,0 );
|
||||
layout->addWidget( m_mgr = new DockInDockManager(*this) );
|
||||
}
|
||||
|
||||
DockInDockWidget::~DockInDockWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ads::CDockAreaWidget* DockInDockWidget::addTabWidget( QWidget* widget, const QString& name, ads::CDockAreaWidget* after )
|
||||
{
|
||||
return addTabWidget( widget, name, QIcon(), after );
|
||||
}
|
||||
|
||||
ads::CDockAreaWidget* DockInDockWidget::addTabWidget( QWidget* widget, const QString& name, QIcon icon, ads::CDockAreaWidget* after )
|
||||
{
|
||||
for ( auto existing : getTopLevelDockWidget()->getManager()->allDockWidgets(true,true) )
|
||||
{
|
||||
if ( existing.second->objectName() == name )
|
||||
{
|
||||
QMessageBox::critical( this, "Error", "Name '" + name + "' already in use" );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(name);
|
||||
DockWidget->setWidget(widget);
|
||||
DockWidget->setIcon( icon );
|
||||
|
||||
// Add the dock widget to the top dock widget area
|
||||
return m_mgr->addDockWidget(ads::CenterDockWidgetArea, DockWidget, after);
|
||||
}
|
||||
|
||||
bool DockInDockWidget::isTopLevel()
|
||||
{
|
||||
return objectName().isEmpty();
|
||||
}
|
||||
|
||||
QString DockInDockWidget::getGroupNameError( const QString& groupName )
|
||||
{
|
||||
if ( groupName.isEmpty() )
|
||||
{
|
||||
return "Group must have a non-empty name";
|
||||
}
|
||||
|
||||
std::vector<DockInDockManager*> dockManagers = m_mgr->allManagers( true, true );
|
||||
for ( auto mgr : dockManagers )
|
||||
{
|
||||
if ( mgr->getGroupName() == groupName )
|
||||
return "Group name '" + groupName + "' already used";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
DockInDockWidget* DockInDockWidget::createGroup( const QString& groupName, ads::CDockAreaWidget*& insertPos )
|
||||
{
|
||||
return createGroup( groupName, QIcon(), insertPos );
|
||||
}
|
||||
|
||||
DockInDockWidget* DockInDockWidget::createGroup( const QString& groupName, QIcon icon, ads::CDockAreaWidget*& insertPos )
|
||||
{
|
||||
QString error = getGroupNameError( groupName );
|
||||
if ( !error.isEmpty() )
|
||||
{
|
||||
QMessageBox::critical( NULL, "Error", error );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DockInDockWidget* child = new DockInDockWidget( this, m_topLevelDockWidget, m_perspectivesManager );
|
||||
child->setObjectName( groupName );
|
||||
|
||||
ads::CDockWidget* DockWidget = new ads::CDockWidget(groupName);
|
||||
DockWidget->setWidget(child);
|
||||
DockWidget->setIcon(icon);
|
||||
|
||||
insertPos = m_mgr->addDockWidget(ads::CenterDockWidgetArea, DockWidget, insertPos);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
void DockInDockWidget::destroyGroup( DockInDockWidget* widgetToRemove )
|
||||
{
|
||||
auto topLevelWidget = widgetToRemove->getTopLevelDockWidget();
|
||||
|
||||
if ( topLevelWidget && topLevelWidget != widgetToRemove )
|
||||
{
|
||||
// reaffect all child docks to top-level
|
||||
for ( auto dockwidget : widgetToRemove->getManager()->getWidgetsInGUIOrder() ) // don't use allDockWidgets to preserve sub-groups
|
||||
{
|
||||
MoveDockWidgetAction::move( dockwidget, topLevelWidget->getManager() );
|
||||
}
|
||||
assert( widgetToRemove->getManager()->allDockWidgets( true, true ).empty() );
|
||||
|
||||
// find widget's parent:
|
||||
for ( auto dockwidget : topLevelWidget->getManager()->allDockWidgets( true, true ) )
|
||||
{
|
||||
if ( dockwidget.second->widget() == widgetToRemove )
|
||||
{
|
||||
dockwidget.first->removeDockWidget( dockwidget.second );
|
||||
delete dockwidget.second;
|
||||
//delete widgetToRemove; automatically deleted when dockWidget is deleted
|
||||
widgetToRemove = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert( widgetToRemove == NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockWidget::attachViewMenu( QMenu* menu )
|
||||
{
|
||||
connect( menu, SIGNAL(aboutToShow()), this, SLOT(autoFillAttachedViewMenu()) );
|
||||
}
|
||||
|
||||
void DockInDockWidget::autoFillAttachedViewMenu()
|
||||
{
|
||||
QMenu* menu = dynamic_cast<QMenu*>( QObject::sender() );
|
||||
|
||||
if ( menu )
|
||||
{
|
||||
menu->clear();
|
||||
setupViewMenu( menu );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockWidget::setupViewMenu( QMenu* menu )
|
||||
{
|
||||
std::vector<DockInDockManager*> dockManagers = m_mgr->allManagers( true, true );
|
||||
|
||||
bool hasPerspectivesMenu = false;
|
||||
if ( getTopLevelDockWidget() == this )
|
||||
hasPerspectivesMenu = (m_perspectivesManager != NULL);
|
||||
else
|
||||
assert( false );
|
||||
|
||||
QMenu* organize = menu;
|
||||
if ( hasPerspectivesMenu )
|
||||
organize = menu->addMenu( "Organize" );
|
||||
|
||||
setupMenu( organize, dockManagers );
|
||||
|
||||
if ( hasPerspectivesMenu )
|
||||
{
|
||||
QMenu* perspectives = menu->addMenu( "Perspectives" );
|
||||
fillPerspectivesMenu( perspectives );
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockWidget::setupMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||
{
|
||||
m_mgr->fillViewMenu( menu, moveTo );
|
||||
menu->addSeparator();
|
||||
auto moveMenu = menu->addMenu( "Move" );
|
||||
m_mgr->fillMoveMenu( moveMenu, moveTo );
|
||||
}
|
||||
|
||||
void DockInDockWidget::fillPerspectivesMenu( QMenu* menu )
|
||||
{
|
||||
menu->addAction( "Create perspective...", this, SLOT(createPerspective()) );
|
||||
|
||||
QStringList perspectiveNames;
|
||||
if ( m_perspectivesManager )
|
||||
perspectiveNames = m_perspectivesManager->perspectiveNames();
|
||||
|
||||
if ( !perspectiveNames.isEmpty() )
|
||||
{
|
||||
QMenu* load = menu->addMenu( "Load perspective" );
|
||||
for ( auto name : perspectiveNames )
|
||||
load->addAction( new LoadPerspectiveAction( load, name, *this ) );
|
||||
QMenu* remove = menu->addMenu( "Remove perspective" );
|
||||
for ( auto name : perspectiveNames )
|
||||
remove->addAction( new RemovePerspectiveAction( remove, name, *this ) );
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockWidget::setNewPerspectiveDefaultName( const QString& defaultName )
|
||||
{
|
||||
m_newPerspectiveDefaultName = defaultName;
|
||||
}
|
||||
|
||||
void DockInDockWidget::createPerspective()
|
||||
{
|
||||
if ( !m_perspectivesManager )
|
||||
return;
|
||||
|
||||
QString name = m_newPerspectiveDefaultName;
|
||||
if ( !m_newPerspectiveDefaultName.isEmpty() )
|
||||
{
|
||||
int index = 2;
|
||||
while ( m_perspectivesManager->perspectiveNames().contains( name ) )
|
||||
{
|
||||
name = m_newPerspectiveDefaultName + " (" + QString::number(index) + ")";
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
bool ok = false;
|
||||
name = QInputDialog::getText( NULL, "Create perspective", "Enter perspective name", QLineEdit::Normal, name, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
if ( name.isEmpty() )
|
||||
{
|
||||
QMessageBox::critical( NULL, "Error", "Perspective name cannot be empty" );
|
||||
continue;
|
||||
}
|
||||
else if ( m_perspectivesManager->perspectiveNames().contains( name ) )
|
||||
{
|
||||
if ( QMessageBox::critical( NULL, "Error", "Perspective '" + name + "' already exists, overwrite it?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
|
||||
continue;
|
||||
}
|
||||
|
||||
m_perspectivesManager->addPerspective( name, *this );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpStatus( std::ostream& str, ads::CDockWidget* widget, const std::string& tab, std::string suffix )
|
||||
{
|
||||
DockInDockManager* asMgr = DockInDockManager::dockInAManager( widget );
|
||||
if ( asMgr )
|
||||
{
|
||||
asMgr->parent().dumpStatus( str, tab );
|
||||
}
|
||||
else
|
||||
{
|
||||
str << tab << widget->objectName().toStdString() << suffix << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockWidget::dumpStatus( std::ostream& str, std::string tab )
|
||||
{
|
||||
str << tab << "Group: " << getManager()->getGroupName().toStdString() << std::endl;
|
||||
tab += " ";
|
||||
std::set<ads::CDockWidget*> visibleWidgets;
|
||||
for ( auto widget : getManager()->getWidgetsInGUIOrder() )
|
||||
{
|
||||
visibleWidgets.insert( widget );
|
||||
::dumpStatus( str, widget, tab, "" );
|
||||
}
|
||||
|
||||
for ( auto closed : getManager()->dockWidgetsMap() )
|
||||
{
|
||||
if ( visibleWidgets.find( closed ) == visibleWidgets.end() )
|
||||
{
|
||||
::dumpStatus( str, closed, tab, " (closed)" );
|
||||
}
|
||||
}
|
||||
}
|
||||
80
examples/dockindock/dockindock.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
class QMenu;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class CDockAreaWidget;
|
||||
}
|
||||
|
||||
namespace QtAdsUtl
|
||||
{
|
||||
|
||||
class DockInDockManager;
|
||||
class PerspectivesManager;
|
||||
// tab of tab example for https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/306
|
||||
class DockInDockWidget : public QWidget
|
||||
{
|
||||
typedef QWidget baseClass;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DockInDockWidget( QWidget* parent, bool canCreateNewGroups, PerspectivesManager* perspectivesManager );
|
||||
~DockInDockWidget() override;
|
||||
|
||||
ads::CDockAreaWidget* addTabWidget( QWidget* widget, const QString& name, ads::CDockAreaWidget* after );
|
||||
DockInDockWidget* createGroup( const QString& groupName, ads::CDockAreaWidget*& insertPos );
|
||||
|
||||
ads::CDockAreaWidget* addTabWidget( QWidget* widget, const QString& name, QIcon icon, ads::CDockAreaWidget* after );
|
||||
DockInDockWidget* createGroup( const QString& groupName, QIcon icon, ads::CDockAreaWidget*& insertPos );
|
||||
|
||||
QString getGroupNameError( const QString& groupName );
|
||||
void destroyGroup( DockInDockWidget* widget );
|
||||
|
||||
/** Manually fill a given view menu */
|
||||
void setupViewMenu( QMenu* menu );
|
||||
|
||||
/** Attach a view menu that will be automatically fill */
|
||||
void attachViewMenu( QMenu* menu );
|
||||
|
||||
bool isTopLevel();
|
||||
void setupMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||
|
||||
inline DockInDockManager* getManager() { return m_mgr; }
|
||||
inline DockInDockWidget* getTopLevelDockWidget() { return m_topLevelDockWidget; }
|
||||
|
||||
inline bool canCreateNewGroups() const { return m_canCreateNewGroups; }
|
||||
|
||||
void dumpStatus( std::ostream& str, std::string tab = "" );
|
||||
|
||||
inline PerspectivesManager* getPerspectivesManager() { return m_perspectivesManager; }
|
||||
|
||||
void setNewPerspectiveDefaultName( const QString& defaultName );
|
||||
|
||||
private slots:
|
||||
void autoFillAttachedViewMenu();
|
||||
void createPerspective();
|
||||
|
||||
private:
|
||||
DockInDockManager* m_mgr;
|
||||
DockInDockWidget* m_topLevelDockWidget;
|
||||
|
||||
bool m_canCreateNewGroups;
|
||||
|
||||
DockInDockWidget( QWidget* parent, DockInDockWidget* topLevelDockWidget, PerspectivesManager* perspectivesManager );
|
||||
|
||||
PerspectivesManager* m_perspectivesManager;
|
||||
QString m_newPerspectiveDefaultName;
|
||||
|
||||
void fillPerspectivesMenu( QMenu* menu );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
35
examples/dockindock/dockindock.pro
Normal file
@@ -0,0 +1,35 @@
|
||||
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
TARGET = DockInDock
|
||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||
TEMPLATE = app
|
||||
CONFIG += c++14
|
||||
CONFIG += debug_and_release
|
||||
adsBuildStatic {
|
||||
DEFINES += ADS_STATIC
|
||||
}
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
SOURCES += \
|
||||
dockindock.cpp \
|
||||
dockindockmanager.cpp \
|
||||
perspectiveactions.cpp \
|
||||
perspectives.cpp \
|
||||
main.cpp \
|
||||
mainframe.cpp
|
||||
|
||||
HEADERS += \
|
||||
dockindock.h \
|
||||
dockindockmanager.h \
|
||||
perspectiveactions.h \
|
||||
perspectives.h \
|
||||
mainframe.h
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
203
examples/dockindock/dockindock.py
Normal 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)")
|
||||
334
examples/dockindock/dockindockmanager.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "dockindockmanager.h"
|
||||
#include "dockindock.h"
|
||||
|
||||
#include "DockAreaWidget.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QInputDialog>
|
||||
#include <QSettings>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
using namespace QtAdsUtl;
|
||||
|
||||
/////////////////////////////////////
|
||||
// DockInDockManager
|
||||
/////////////////////////////////////
|
||||
DockInDockManager::DockInDockManager( DockInDockWidget& parent ) :
|
||||
baseClass( &parent ),
|
||||
m_parent( parent )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DockInDockManager::~DockInDockManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DockInDockManager::fillViewMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||
{
|
||||
auto widgetsMap = dockWidgetsMap();
|
||||
for ( auto iter = widgetsMap.begin(); iter != widgetsMap.end(); ++iter )
|
||||
{
|
||||
auto widget = iter.value()->widget();
|
||||
auto action = iter.value()->toggleViewAction();
|
||||
|
||||
DockInDockWidget* asMgr = dynamic_cast<DockInDockWidget*>( widget );
|
||||
if ( asMgr )
|
||||
{
|
||||
auto subMenu = menu->addMenu( iter.key() );
|
||||
|
||||
subMenu->addAction( action );
|
||||
subMenu->addSeparator();
|
||||
|
||||
asMgr->setupMenu( subMenu, moveTo );
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
if ( 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( new QtAdsUtl::CreateChildDockAction( m_parent, menu ) );
|
||||
|
||||
if ( parent().getTopLevelDockWidget()->getManager() != this )
|
||||
menu->addAction( new QtAdsUtl::DestroyGroupAction( &m_parent, menu ) );
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockManager::fillMoveMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||
{
|
||||
auto widgetsMap = dockWidgetsMap();
|
||||
for ( auto iter = widgetsMap.begin(); iter != widgetsMap.end(); ++iter )
|
||||
{
|
||||
auto subMenu = menu->addMenu( iter.key() );
|
||||
|
||||
for ( auto mgr : moveTo )
|
||||
{
|
||||
// iterate over all possible target managers
|
||||
if ( mgr == this )
|
||||
{
|
||||
// if dock is already in mgr, no reason to move it there
|
||||
}
|
||||
else if ( mgr == dockInAManager( iter.value() ) )
|
||||
{
|
||||
// if target is the group itself, can't move it there, would make no sense
|
||||
}
|
||||
else
|
||||
{
|
||||
subMenu->addAction( new MoveDockWidgetAction( iter.value(), mgr, subMenu ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockManager::addPerspectiveRec( const QString& name )
|
||||
{
|
||||
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||
|
||||
for ( auto child : managers )
|
||||
child->addPerspective( name );
|
||||
}
|
||||
|
||||
void DockInDockManager::openPerspectiveRec( const QString& name )
|
||||
{
|
||||
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||
|
||||
for ( auto child : managers )
|
||||
child->openPerspective( name );
|
||||
}
|
||||
|
||||
QString DockInDockManager::getGroupName()
|
||||
{
|
||||
return parent().objectName();
|
||||
}
|
||||
|
||||
#define CHILD_PREFIX QString("Child-")
|
||||
QString DockInDockManager::getPersistGroupName()
|
||||
{
|
||||
QString group = "Top";
|
||||
if ( !getGroupName().isEmpty() )
|
||||
group = CHILD_PREFIX + getGroupName();
|
||||
return group;
|
||||
}
|
||||
|
||||
QString DockInDockManager::getGroupNameFromPersistGroupName( QString persistGroupName )
|
||||
{
|
||||
if ( persistGroupName.startsWith( CHILD_PREFIX ) )
|
||||
{
|
||||
persistGroupName = persistGroupName.mid( CHILD_PREFIX.size() );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
return persistGroupName;
|
||||
}
|
||||
|
||||
void DockInDockManager::loadPerspectivesRec(QSettings& Settings)
|
||||
{
|
||||
std::vector<DockInDockManager*> children = allManagers( true, true );
|
||||
|
||||
for ( auto mgr : children )
|
||||
{
|
||||
Settings.beginGroup(mgr->getPersistGroupName());
|
||||
mgr->loadPerspectives( Settings );
|
||||
Settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockManager::savePerspectivesRec(QSettings& Settings) const
|
||||
{
|
||||
std::vector<DockInDockManager*> children = allManagers( true, true );
|
||||
|
||||
for ( auto mgr : children )
|
||||
{
|
||||
Settings.beginGroup(mgr->getPersistGroupName());
|
||||
mgr->savePerspectives( Settings );
|
||||
Settings.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void DockInDockManager::removePerspectivesRec()
|
||||
{
|
||||
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||
|
||||
for ( auto child : managers )
|
||||
child->removePerspectives( child->perspectiveNames() );
|
||||
}
|
||||
|
||||
DockInDockManager* DockInDockManager::dockInAManager( ads::CDockWidget* widget )
|
||||
{
|
||||
DockInDockWidget* dockWidget = widget ? dynamic_cast<DockInDockWidget*>( widget->widget() ) : NULL;
|
||||
return ( dockWidget ) ? dockWidget->getManager() : NULL;
|
||||
}
|
||||
|
||||
void DockInDockManager::childManagers( std::vector<DockInDockManager*>& managers, bool rec ) const
|
||||
{
|
||||
auto widgets = getWidgetsInGUIOrder();
|
||||
for ( auto widget : widgets )
|
||||
{
|
||||
DockInDockManager* asMgr = dockInAManager( widget );
|
||||
if ( asMgr )
|
||||
{
|
||||
managers.push_back( asMgr );
|
||||
if ( rec )
|
||||
asMgr->childManagers( managers, rec );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DockInDockManager*> DockInDockManager::allManagers( bool includeThis, bool rec ) const
|
||||
{
|
||||
std::vector<DockInDockManager*> managers;
|
||||
if ( includeThis )
|
||||
managers.push_back( const_cast<DockInDockManager*>(this) );
|
||||
childManagers( managers, rec );
|
||||
return managers;
|
||||
}
|
||||
|
||||
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> DockInDockManager::allDockWidgets( bool includeThis, bool rec ) const
|
||||
{
|
||||
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> widgets;
|
||||
for ( auto mgr : allManagers( includeThis, rec ) )
|
||||
{
|
||||
for ( auto widget : mgr->getWidgetsInGUIOrder() )
|
||||
widgets.push_back( std::make_pair(mgr, widget) );
|
||||
}
|
||||
return widgets;
|
||||
}
|
||||
|
||||
QMap<QString,QStringList> DockInDockManager::getGroupContents()
|
||||
{
|
||||
QMap<QString,QStringList> result;
|
||||
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||
for ( auto mgr : managers )
|
||||
{
|
||||
result[mgr->getPersistGroupName()] = mgr->dockWidgetsMap().keys();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ads::CDockAreaWidget* DockInDockManager::getInsertDefaultPos()
|
||||
{
|
||||
ads::CDockAreaWidget* defaultPos = NULL;
|
||||
if ( dockAreaCount() != 0 )
|
||||
defaultPos = dockArea(dockAreaCount()-1);
|
||||
return defaultPos;
|
||||
}
|
||||
|
||||
std::vector<ads::CDockWidget*> DockInDockManager::getWidgetsInGUIOrder() const
|
||||
{
|
||||
std::vector<ads::CDockWidget*> result;
|
||||
result.reserve( dockWidgetsMap().size() );
|
||||
for ( int i = 0; i != dockAreaCount(); ++i )
|
||||
{
|
||||
for ( auto widget : dockArea(i)->dockWidgets() )
|
||||
result.push_back( widget );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
// CreateChildDockAction
|
||||
/////////////////////////////////////
|
||||
CreateChildDockAction::CreateChildDockAction( DockInDockWidget& dockInDock, QMenu* menu ) :
|
||||
QAction("New group...", menu),
|
||||
m_dockInDock( dockInDock )
|
||||
{
|
||||
connect( this, SIGNAL(triggered()), this, SLOT(createGroup()) );
|
||||
}
|
||||
|
||||
void CreateChildDockAction::createGroup()
|
||||
{
|
||||
QString name = "";
|
||||
while ( true )
|
||||
{
|
||||
bool ok = false;
|
||||
name = QInputDialog::getText( NULL, this->text(), "Enter group name", QLineEdit::Normal, name, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
QString error = "";
|
||||
if ( m_dockInDock.getTopLevelDockWidget() )
|
||||
error = m_dockInDock.getTopLevelDockWidget()->getGroupNameError( name );
|
||||
else
|
||||
assert( false );
|
||||
|
||||
if ( error.isEmpty() )
|
||||
{
|
||||
ads::CDockAreaWidget* insertPos = NULL;
|
||||
m_dockInDock.createGroup( name, insertPos );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical( NULL, "Error", error );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
// DestroyGroupAction
|
||||
/////////////////////////////////////
|
||||
DestroyGroupAction::DestroyGroupAction( DockInDockWidget* widget, QMenu* menu ) :
|
||||
QAction("Destroy " + widget->getManager()->getGroupName(), menu),
|
||||
m_widget( widget )
|
||||
{
|
||||
connect( this, SIGNAL(triggered()), this, SLOT(destroyGroup()) );
|
||||
}
|
||||
|
||||
void DestroyGroupAction::destroyGroup()
|
||||
{
|
||||
m_widget->getTopLevelDockWidget()->destroyGroup( m_widget );
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
// MoveDockWidgetAction
|
||||
/////////////////////////////////////
|
||||
MoveDockWidgetAction::MoveDockWidgetAction( ads::CDockWidget* widget, DockInDockManager* moveTo, QMenu* menu ) :
|
||||
QAction(menu),
|
||||
m_widget( widget ),
|
||||
m_moveTo( moveTo )
|
||||
{
|
||||
if ( moveTo->parent().isTopLevel() )
|
||||
{
|
||||
setText( "To top" );
|
||||
}
|
||||
else
|
||||
{
|
||||
setText( "To " + moveTo->parent().objectName() );
|
||||
}
|
||||
connect( this, SIGNAL(triggered()), this, SLOT(move()) );
|
||||
}
|
||||
|
||||
void MoveDockWidgetAction::move()
|
||||
{
|
||||
move( m_widget, m_moveTo );
|
||||
}
|
||||
|
||||
void MoveDockWidgetAction::move( ads::CDockWidget* widget, DockInDockManager* moveTo )
|
||||
{
|
||||
if ( widget && moveTo )
|
||||
{
|
||||
widget->dockManager()->removeDockWidget( widget );
|
||||
moveTo->addDockWidget(ads::CenterDockWidgetArea, widget, moveTo->getInsertDefaultPos());
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
96
examples/dockindock/dockindockmanager.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "DockManager.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMap>
|
||||
|
||||
namespace QtAdsUtl
|
||||
{
|
||||
|
||||
class DockInDockWidget;
|
||||
class DockInDockManager : public ads::CDockManager
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef ads::CDockManager baseClass;
|
||||
|
||||
public:
|
||||
DockInDockManager( DockInDockWidget& parent );
|
||||
~DockInDockManager() override;
|
||||
|
||||
void fillViewMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||
void fillMoveMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||
|
||||
void addPerspectiveRec( const QString& name );
|
||||
void openPerspectiveRec( const QString& name );
|
||||
void removePerspectivesRec();
|
||||
void loadPerspectivesRec(QSettings& Settings);
|
||||
void savePerspectivesRec(QSettings& Settings) const;
|
||||
|
||||
static DockInDockManager* dockInAManager( ads::CDockWidget* widget );
|
||||
|
||||
inline DockInDockWidget& parent() { return m_parent; }
|
||||
|
||||
void childManagers( std::vector<DockInDockManager*>& managers, bool rec ) const;
|
||||
std::vector<DockInDockManager*> allManagers( bool includeThis, bool rec ) const;
|
||||
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> allDockWidgets( bool includeThis, bool rec ) const;
|
||||
|
||||
QString getGroupName();
|
||||
QString getPersistGroupName();
|
||||
static QString getGroupNameFromPersistGroupName( QString persistGroupName );
|
||||
|
||||
QMap<QString,QStringList> getGroupContents();
|
||||
|
||||
ads::CDockAreaWidget* getInsertDefaultPos();
|
||||
|
||||
std::vector<ads::CDockWidget*> getWidgetsInGUIOrder() const;
|
||||
|
||||
private:
|
||||
DockInDockWidget& m_parent;
|
||||
};
|
||||
|
||||
class CreateChildDockAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CreateChildDockAction( DockInDockWidget& dockInDock, QMenu* menu );
|
||||
|
||||
public slots:
|
||||
void createGroup();
|
||||
|
||||
private:
|
||||
DockInDockWidget& m_dockInDock;
|
||||
};
|
||||
|
||||
class DestroyGroupAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DestroyGroupAction( DockInDockWidget* widget, QMenu* menu );
|
||||
|
||||
public slots:
|
||||
void destroyGroup();
|
||||
|
||||
private:
|
||||
DockInDockWidget* m_widget;
|
||||
};
|
||||
|
||||
class MoveDockWidgetAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MoveDockWidgetAction( ads::CDockWidget* widget, DockInDockManager* moveTo, QMenu* menu );
|
||||
|
||||
static void move( ads::CDockWidget* widget, DockInDockManager* moveTo );
|
||||
|
||||
public slots:
|
||||
void move();
|
||||
|
||||
private:
|
||||
ads::CDockWidget* m_widget;
|
||||
DockInDockManager* m_moveTo;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
214
examples/dockindock/dockindockmanager.py
Normal 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
|
||||
11
examples/dockindock/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <QApplication>
|
||||
#include "../../examples/simple/MainWindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
72
examples/dockindock/main.py
Normal 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_()
|
||||
76
examples/dockindock/mainframe.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "mainframe.h"
|
||||
|
||||
#include "dockindock.h"
|
||||
#include "perspectives.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
m_perspectivesManager( new QtAdsUtl::PerspectivesManager( "persist" ) )
|
||||
{
|
||||
resize( 400, 400 );
|
||||
|
||||
setCentralWidget( m_dockManager = new QtAdsUtl::DockInDockWidget(this,true,m_perspectivesManager.get()) );
|
||||
|
||||
m_dockManager->attachViewMenu( menuBar()->addMenu( "View" ) );
|
||||
|
||||
ads::CDockAreaWidget* previousDockWidget = NULL;
|
||||
for ( int i = 0; i != 3; ++i )
|
||||
{
|
||||
// Create example content label - this can be any application specific
|
||||
// widget
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||
|
||||
previousDockWidget = m_dockManager->addTabWidget( l, "Top label " + QString::number(i), previousDockWidget );
|
||||
}
|
||||
|
||||
auto lastTopLevelDock = previousDockWidget;
|
||||
|
||||
for ( int j = 0; j != 2; ++j )
|
||||
{
|
||||
QtAdsUtl::DockInDockWidget* groupManager = m_dockManager->createGroup( "Group " + QString::number(j), lastTopLevelDock );
|
||||
|
||||
previousDockWidget = NULL;
|
||||
for ( int i = 0; i != 3; ++i )
|
||||
{
|
||||
// Create example content label - this can be any application specific
|
||||
// widget
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||
|
||||
previousDockWidget = groupManager->addTabWidget( l, "ZInner " + QString::number(j) + "/" + QString::number(i), previousDockWidget );
|
||||
}
|
||||
|
||||
// create sub-group
|
||||
auto subGroup = groupManager->createGroup( "SubGroup " + QString::number(j), previousDockWidget );
|
||||
previousDockWidget = NULL;
|
||||
for ( int i = 0; i != 3; ++i )
|
||||
{
|
||||
// Create example content label - this can be any application specific
|
||||
// widget
|
||||
QLabel* l = new QLabel();
|
||||
l->setWordWrap(true);
|
||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||
|
||||
previousDockWidget = subGroup->addTabWidget( l, "SubInner " + QString::number(j) + "/" + QString::number(i), previousDockWidget );
|
||||
}
|
||||
}
|
||||
|
||||
m_perspectivesManager->loadPerspectives();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
m_perspectivesManager->savePerspectives();
|
||||
}
|
||||
|
||||
30
examples/dockindock/mainframe.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QSettings>
|
||||
|
||||
#include <memory>
|
||||
namespace QtAdsUtl
|
||||
{
|
||||
class DockInDockWidget;
|
||||
class PerspectivesManager;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
QtAdsUtl::DockInDockWidget* m_dockManager;
|
||||
std::unique_ptr<QtAdsUtl::PerspectivesManager> m_perspectivesManager;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
39
examples/dockindock/perspectiveactions.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "perspectiveactions.h"
|
||||
#include "dockindock.h"
|
||||
#include "perspectives.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
using namespace QtAdsUtl;
|
||||
|
||||
//////////////////////////////
|
||||
// LoadPerspectiveAction
|
||||
//////////////////////////////
|
||||
LoadPerspectiveAction::LoadPerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager ) :
|
||||
QAction( name, parent ),
|
||||
name( name ),
|
||||
dockManager( dockManager )
|
||||
{
|
||||
connect( this, SIGNAL(triggered()), this, SLOT(load()) );
|
||||
}
|
||||
|
||||
void LoadPerspectiveAction::load()
|
||||
{
|
||||
dockManager.getPerspectivesManager()->openPerspective( name, dockManager );
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// RemovePerspectiveAction
|
||||
//////////////////////////////
|
||||
RemovePerspectiveAction::RemovePerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager ) :
|
||||
QAction( name, parent ),
|
||||
name( name ),
|
||||
dockManager( dockManager )
|
||||
{
|
||||
connect( this, SIGNAL(triggered()), this, SLOT(remove()) );
|
||||
}
|
||||
|
||||
void RemovePerspectiveAction::remove()
|
||||
{
|
||||
dockManager.getPerspectivesManager()->removePerspective( name );
|
||||
}
|
||||
38
examples/dockindock/perspectiveactions.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAction>
|
||||
|
||||
namespace QtAdsUtl
|
||||
{
|
||||
|
||||
class DockInDockWidget;
|
||||
class LoadPerspectiveAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadPerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager );
|
||||
|
||||
public slots:
|
||||
void load();
|
||||
|
||||
private:
|
||||
QString name;
|
||||
QtAdsUtl::DockInDockWidget& dockManager;
|
||||
};
|
||||
|
||||
class RemovePerspectiveAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RemovePerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager );
|
||||
|
||||
public slots:
|
||||
void remove();
|
||||
|
||||
private:
|
||||
QString name;
|
||||
QtAdsUtl::DockInDockWidget& dockManager;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
25
examples/dockindock/perspectiveactions.py
Normal 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)
|
||||
278
examples/dockindock/perspectives.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "perspectives.h"
|
||||
#include "dockindock.h"
|
||||
#include "dockindockmanager.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define GROUP_PREFIX QString("Group")
|
||||
|
||||
using namespace QtAdsUtl;
|
||||
|
||||
PerspectivesManager::PerspectivesManager( const QString& perspectivesFolder ) :
|
||||
m_perspectivesFolder( perspectivesFolder )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PerspectivesManager::~PerspectivesManager()
|
||||
{
|
||||
// remove temp files:
|
||||
for ( auto perspective : m_perspectives )
|
||||
{
|
||||
QString fileName = perspective.settings->fileName();
|
||||
perspective.settings.reset();
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QStringList PerspectivesManager::perspectiveNames() const
|
||||
{
|
||||
return m_perspectives.keys();
|
||||
}
|
||||
|
||||
void PerspectivesManager::addPerspective( const QString& name, DockInDockWidget& widget )
|
||||
{
|
||||
if ( !m_perspectivesFolder.isEmpty() )
|
||||
{
|
||||
m_perspectives[name].settings = getSettingsObject( getSettingsFileName( name, true ) );
|
||||
m_perspectives[name].groups = widget.getManager()->getGroupContents();
|
||||
|
||||
// save perspective internally
|
||||
widget.getManager()->addPerspectiveRec( name );
|
||||
// store it in QSettings object
|
||||
widget.getManager()->savePerspectivesRec( *(m_perspectives[name].settings) );
|
||||
// remove internal perspectives
|
||||
widget.getManager()->removePerspectives( widget.getManager()->perspectiveNames() );
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
emit perspectivesListChanged();
|
||||
}
|
||||
|
||||
|
||||
ads::CDockWidget* findWidget( QString name, const std::vector<DockInDockManager*>& managers )
|
||||
{
|
||||
for ( auto mgr : managers )
|
||||
{
|
||||
auto widget = mgr->findDockWidget(name);
|
||||
if ( widget )
|
||||
return widget;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PerspectivesManager::openPerspective( const QString& name, DockInDockWidget& widget )
|
||||
{
|
||||
assert( widget.getTopLevelDockWidget() == &widget );
|
||||
|
||||
if ( !m_perspectivesFolder.isEmpty() )
|
||||
{
|
||||
if ( m_perspectives.contains( name ) )
|
||||
{
|
||||
emit openingPerspective();
|
||||
|
||||
if ( widget.canCreateNewGroups() )
|
||||
{
|
||||
auto curGroups = widget.getManager()->allManagers(true,true);
|
||||
for ( auto group : m_perspectives[name].groups.keys() )
|
||||
{
|
||||
bool found = false;
|
||||
for ( auto curgroup : curGroups )
|
||||
{
|
||||
if ( curgroup->getPersistGroupName() == group )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
group = DockInDockManager::getGroupNameFromPersistGroupName( group );
|
||||
|
||||
// restore group in file but not in GUI yet
|
||||
ads::CDockAreaWidget* insertPos = NULL;
|
||||
widget.createGroup( group, insertPos );
|
||||
}
|
||||
}
|
||||
|
||||
curGroups = widget.getManager()->allManagers(false,true);
|
||||
for ( auto curgroup : curGroups )
|
||||
{
|
||||
if ( !m_perspectives[name].groups.keys().contains( curgroup->getPersistGroupName() ) )
|
||||
{
|
||||
widget.destroyGroup( &curgroup->parent() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto managers = widget.getManager()->allManagers(true,true);
|
||||
for ( auto group : m_perspectives[name].groups.keys() )
|
||||
{
|
||||
for ( auto mgr : managers )
|
||||
{
|
||||
if ( mgr->getPersistGroupName() == group )
|
||||
{
|
||||
for ( QString widgetName : m_perspectives[name].groups[group] )
|
||||
{
|
||||
ads::CDockWidget* widget = findWidget( widgetName, { mgr } );
|
||||
if ( widget )
|
||||
{
|
||||
// OK, widget is already in the good manager!
|
||||
}
|
||||
else
|
||||
{
|
||||
widget = findWidget( widgetName, 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( *(m_perspectives[name].settings) );
|
||||
// load perspective (update GUI)
|
||||
widget.getManager()->openPerspectiveRec( name );
|
||||
// remove internal perspectives
|
||||
widget.getManager()->removePerspectives( widget.getManager()->perspectiveNames() );
|
||||
|
||||
emit openedPerspective();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
void PerspectivesManager::removePerspectives()
|
||||
{
|
||||
m_perspectives.clear();
|
||||
emit perspectivesListChanged();
|
||||
}
|
||||
|
||||
void PerspectivesManager::removePerspective( const QString& name )
|
||||
{
|
||||
m_perspectives.remove( name );
|
||||
emit perspectivesListChanged();
|
||||
}
|
||||
|
||||
QString PerspectivesManager::getSettingsFileName( const QString& perspective, bool temp ) const
|
||||
{
|
||||
auto name = ( perspective.isEmpty() ) ? "perspectives.ini" : "perspective_" + perspective + (temp?".tmp":".ini");
|
||||
|
||||
return m_perspectivesFolder + "/" + name;
|
||||
}
|
||||
|
||||
std::shared_ptr<QSettings> PerspectivesManager::getSettingsObject( const QString& filePath ) const
|
||||
{
|
||||
return std::make_shared<QSettings>(filePath, QSettings::IniFormat);
|
||||
}
|
||||
|
||||
void PerspectivesManager::loadPerspectives()
|
||||
{
|
||||
if ( !m_perspectivesFolder.isEmpty() )
|
||||
{
|
||||
QDir().mkpath( m_perspectivesFolder );
|
||||
|
||||
m_perspectives.clear();
|
||||
|
||||
auto mainSettings = getSettingsObject( getSettingsFileName( "", false ) );
|
||||
std::string debug = mainSettings->fileName().toStdString();
|
||||
|
||||
int Size = mainSettings->beginReadArray("Perspectives");
|
||||
|
||||
for (int i = 0; i < Size; ++i)
|
||||
{
|
||||
mainSettings->setArrayIndex(i);
|
||||
QString perspective = mainSettings->value("Name").toString();
|
||||
|
||||
if ( !perspective.isEmpty() )
|
||||
{
|
||||
// load perspective file:
|
||||
auto toLoad = getSettingsFileName( perspective, false );
|
||||
auto loaded = getSettingsFileName( perspective, true );
|
||||
|
||||
#ifdef _DEBUG
|
||||
std::string debug1 = loaded.toStdString();
|
||||
std::string debug2 = toLoad.toStdString();
|
||||
#endif
|
||||
|
||||
QFile::remove( loaded );
|
||||
if ( !QFile::copy( toLoad, loaded ) )
|
||||
assert( false );
|
||||
|
||||
m_perspectives[perspective].settings = getSettingsObject( loaded );
|
||||
|
||||
// load group info:
|
||||
mainSettings->beginGroup(GROUP_PREFIX);
|
||||
for ( auto key : mainSettings->allKeys() )
|
||||
m_perspectives[perspective].groups[key] = mainSettings->value( key ).toStringList();
|
||||
mainSettings->endGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
mainSettings->endArray();
|
||||
}
|
||||
|
||||
emit perspectivesListChanged();
|
||||
}
|
||||
|
||||
void PerspectivesManager::savePerspectives() const
|
||||
{
|
||||
if ( !m_perspectivesFolder.isEmpty() )
|
||||
{
|
||||
auto mainSettings = getSettingsObject( getSettingsFileName( "", false ) );
|
||||
|
||||
// Save list of perspective and group organization
|
||||
mainSettings->beginWriteArray("Perspectives", m_perspectives.size());
|
||||
int i = 0;
|
||||
for ( auto perspective : m_perspectives.keys() )
|
||||
{
|
||||
mainSettings->setArrayIndex(i);
|
||||
mainSettings->setValue("Name", perspective);
|
||||
mainSettings->beginGroup(GROUP_PREFIX);
|
||||
for ( auto group : m_perspectives[perspective].groups.keys() )
|
||||
{
|
||||
mainSettings->setValue( group, m_perspectives[perspective].groups[group] );
|
||||
}
|
||||
mainSettings->endGroup();
|
||||
++i;
|
||||
}
|
||||
mainSettings->endArray();
|
||||
|
||||
// Save perspectives themselves
|
||||
for ( auto perspectiveName : m_perspectives.keys() )
|
||||
{
|
||||
auto toSave = getSettingsFileName( perspectiveName, false );
|
||||
QSettings& settings = *(m_perspectives[perspectiveName].settings);
|
||||
settings.sync();
|
||||
|
||||
#ifdef _DEBUG
|
||||
std::string debug1 = settings.fileName().toStdString();
|
||||
std::string debug2 = toSave.toStdString();
|
||||
#endif
|
||||
|
||||
QFile::remove( toSave );
|
||||
if ( !QFile::copy( settings.fileName(), toSave ) )
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
59
examples/dockindock/perspectives.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QMenu;
|
||||
class QSettings;
|
||||
|
||||
namespace QtAdsUtl
|
||||
{
|
||||
|
||||
class DockInDockWidget;
|
||||
class PerspectivesManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PerspectivesManager( const QString& perspectivesFolder );
|
||||
virtual ~PerspectivesManager();
|
||||
|
||||
QStringList perspectiveNames() const;
|
||||
|
||||
void addPerspective( const QString& name, DockInDockWidget& widget );
|
||||
void openPerspective( const QString& name, DockInDockWidget& widget );
|
||||
void removePerspectives();
|
||||
void removePerspective( const QString& name );
|
||||
|
||||
void loadPerspectives();
|
||||
void savePerspectives() const;
|
||||
|
||||
signals:
|
||||
void perspectivesListChanged();
|
||||
void openingPerspective();
|
||||
void openedPerspective();
|
||||
|
||||
private:
|
||||
|
||||
// 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!
|
||||
class PerspectiveInfo
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<QSettings> settings;
|
||||
QMap<QString,QStringList> groups;
|
||||
};
|
||||
QMap<QString,PerspectiveInfo> m_perspectives;
|
||||
|
||||
QString m_perspectivesFolder;
|
||||
QString getSettingsFileName( const QString& perspective, bool temp ) const;
|
||||
std::shared_ptr<QSettings> getSettingsObject( const QString& filePath ) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
203
examples/dockindock/perspectives.py
Normal 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
|
||||
28
examples/emptydockarea/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||
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)
|
||||
add_executable(EmptyDockAreaExample WIN32
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.ui
|
||||
)
|
||||
target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking)
|
||||
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
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
AUTOUIC ON
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
VERSION ${VERSION_SHORT}
|
||||
EXPORT_NAME "Qt Advanced Docking System Empty Dock Area Example"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||
)
|
||||
34
examples/emptydockarea/emptydockarea.pro
Normal file
@@ -0,0 +1,34 @@
|
||||
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
TARGET = EmptyDockareaExample
|
||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||
TEMPLATE = app
|
||||
CONFIG += c++14
|
||||
CONFIG += debug_and_release
|
||||
adsBuildStatic {
|
||||
DEFINES += ADS_STATIC
|
||||
}
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
10
examples/emptydockarea/main.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <mainwindow.h>
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
CMainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
108
examples/emptydockarea/main.py
Normal 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_()
|
||||
135
examples/emptydockarea/mainwindow.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QWidgetAction>
|
||||
#include <QLabel>
|
||||
#include <QCalendarWidget>
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
#include <QTableWidget>
|
||||
#include <QHBoxLayout>
|
||||
#include <QRadioButton>
|
||||
#include <QPushButton>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QSettings>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QToolBar>
|
||||
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockAreaTitleBar.h"
|
||||
#include "DockAreaTabBar.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
|
||||
using namespace ads;
|
||||
|
||||
|
||||
CMainWindow::CMainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::CMainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
||||
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
||||
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||
DockManager = new CDockManager(this);
|
||||
|
||||
// Set central widget
|
||||
QLabel* label = new QLabel();
|
||||
label->setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.");
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||
CentralDockWidget->setWidget(label);
|
||||
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||
|
||||
// create other dock widgets
|
||||
QTableWidget* table = new QTableWidget();
|
||||
table->setColumnCount(3);
|
||||
table->setRowCount(10);
|
||||
CDockWidget* TableDockWidget = new CDockWidget("Table 1");
|
||||
TableDockWidget->setWidget(table);
|
||||
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
TableDockWidget->resize(250, 150);
|
||||
TableDockWidget->setMinimumSize(200,150);
|
||||
DockManager->addDockWidgetTabToArea(TableDockWidget, CentralDockArea);
|
||||
auto TableArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, TableDockWidget);
|
||||
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||
|
||||
table = new QTableWidget();
|
||||
table->setColumnCount(5);
|
||||
table->setRowCount(1020);
|
||||
TableDockWidget = new CDockWidget("Table 2");
|
||||
TableDockWidget->setWidget(table);
|
||||
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
TableDockWidget->resize(250, 150);
|
||||
TableDockWidget->setMinimumSize(200,150);
|
||||
DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, TableArea);
|
||||
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||
|
||||
QTableWidget* propertiesTable = new QTableWidget();
|
||||
propertiesTable->setColumnCount(3);
|
||||
propertiesTable->setRowCount(10);
|
||||
CDockWidget* PropertiesDockWidget = new CDockWidget("Properties");
|
||||
PropertiesDockWidget->setWidget(propertiesTable);
|
||||
PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||
PropertiesDockWidget->resize(250, 150);
|
||||
PropertiesDockWidget->setMinimumSize(200,150);
|
||||
DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea);
|
||||
ui->menuView->addAction(PropertiesDockWidget->toggleViewAction());
|
||||
|
||||
createPerspectiveUi();
|
||||
}
|
||||
|
||||
CMainWindow::~CMainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
void CMainWindow::createPerspectiveUi()
|
||||
{
|
||||
SavePerspectiveAction = new QAction("Create Perspective", this);
|
||||
connect(SavePerspectiveAction, SIGNAL(triggered()), SLOT(savePerspective()));
|
||||
PerspectiveListAction = new QWidgetAction(this);
|
||||
PerspectiveComboBox = new QComboBox(this);
|
||||
PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
connect(PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||
DockManager, SLOT(openPerspective(const QString&)));
|
||||
PerspectiveListAction->setDefaultWidget(PerspectiveComboBox);
|
||||
ui->toolBar->addSeparator();
|
||||
ui->toolBar->addAction(PerspectiveListAction);
|
||||
ui->toolBar->addAction(SavePerspectiveAction);
|
||||
}
|
||||
|
||||
|
||||
void CMainWindow::savePerspective()
|
||||
{
|
||||
QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:");
|
||||
if (PerspectiveName.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DockManager->addPerspective(PerspectiveName);
|
||||
QSignalBlocker Blocker(PerspectiveComboBox);
|
||||
PerspectiveComboBox->clear();
|
||||
PerspectiveComboBox->addItems(DockManager->perspectiveNames());
|
||||
PerspectiveComboBox->setCurrentText(PerspectiveName);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
// Delete dock manager here to delete all floating widgets. This ensures
|
||||
// that all top level windows of the dock manager are properly closed
|
||||
DockManager->deleteLater();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
|
||||
43
examples/emptydockarea/mainwindow.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QComboBox>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#include "DockManager.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockWidget.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class CMainWindow; }
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class CMainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CMainWindow(QWidget *parent = nullptr);
|
||||
~CMainWindow();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
QAction* SavePerspectiveAction = nullptr;
|
||||
QWidgetAction* PerspectiveListAction = nullptr;
|
||||
QComboBox* PerspectiveComboBox = nullptr;
|
||||
|
||||
Ui::CMainWindow *ui;
|
||||
|
||||
ads::CDockManager* DockManager;
|
||||
ads::CDockAreaWidget* StatusDockArea;
|
||||
ads::CDockWidget* TimelineDockWidget;
|
||||
|
||||
void createPerspectiveUi();
|
||||
|
||||
private slots:
|
||||
void savePerspective();
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
47
examples/emptydockarea/mainwindow.ui
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CMainWindow</class>
|
||||
<widget class="QMainWindow" name="CMainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1284</width>
|
||||
<height>757</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1284</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,6 +1,9 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
centralwidget \
|
||||
simple \
|
||||
sidebar \
|
||||
deleteonclose
|
||||
deleteonclose \
|
||||
emptydockarea \
|
||||
dockindock
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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)
|
||||
add_executable(SidebarExample WIN32
|
||||
main.cpp
|
||||
@@ -9,7 +10,9 @@ add_executable(SidebarExample WIN32
|
||||
)
|
||||
target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
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
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
|
||||
@@ -51,3 +51,13 @@ MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
QMainWindow::closeEvent(event);
|
||||
if (m_DockManager)
|
||||
{
|
||||
m_DockManager->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
ads::CDockManager* m_DockManager;
|
||||
|
||||
@@ -25,23 +25,7 @@ FORMS += \
|
||||
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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)
|
||||
add_executable(SimpleExample WIN32
|
||||
main.cpp
|
||||
@@ -9,7 +10,9 @@ add_executable(SimpleExample WIN32
|
||||
)
|
||||
target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
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
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ui_MainWindow.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
@@ -38,3 +39,4 @@ MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import os
|
||||
import sys
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QCloseEvent
|
||||
from PyQt5.QtWidgets import QApplication, QLabel
|
||||
|
||||
from PyQtAds import QtAds
|
||||
@@ -33,6 +34,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
# as the dock widget content
|
||||
dock_widget = QtAds.CDockWidget("Label 1")
|
||||
dock_widget.setWidget(l)
|
||||
|
||||
|
||||
# Add the toggleViewAction of the dock widget to the menu to give
|
||||
# the user the possibility to show the dock widget if it has been closed
|
||||
@@ -25,23 +25,7 @@ FORMS += \
|
||||
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
include(../../ads.pri)
|
||||
INCLUDEPATH += ../../src
|
||||
DEPENDPATH += ../../src
|
||||
|
||||
|
||||
61
setup.py
@@ -159,8 +159,20 @@ class build_ext(sipdistutils.build_ext):
|
||||
return sip_bin
|
||||
|
||||
raise SystemExit('Could not find PyQt SIP binary.')
|
||||
|
||||
def _sip_sipfiles_dir(self):
|
||||
sip_dir = super()._sip_sipfiles_dir()
|
||||
if os.path.exists(sip_dir):
|
||||
return sip_dir
|
||||
|
||||
return os.path.join(sys.prefix, 'sip', 'PyQt5')
|
||||
|
||||
def _sip_compile(self, sip_bin, source, sbf):
|
||||
target_dir = os.path.dirname(__file__) if self.inplace else self.build_lib
|
||||
pyi = os.path.join(target_dir, "PyQtAds", "QtAds", "ads.pyi")
|
||||
if not os.path.exists(os.path.dirname(pyi)):
|
||||
os.makedirs(os.path.dirname(pyi))
|
||||
|
||||
cmd = [sip_bin]
|
||||
if hasattr(self, 'sip_opts'):
|
||||
cmd += self.sip_opts
|
||||
@@ -173,11 +185,20 @@ class build_ext(sipdistutils.build_ext):
|
||||
"-I", self.inc_dir,
|
||||
"-c", self._sip_output_dir(),
|
||||
"-b", sbf,
|
||||
"-y", pyi,
|
||||
"-w", "-o"]
|
||||
|
||||
cmd += shlex.split(self.pyqt_sip_flags) # use same SIP flags as for PyQt5
|
||||
cmd.append(source)
|
||||
self.spawn(cmd)
|
||||
|
||||
if os.path.exists(pyi):
|
||||
with open(pyi) as f:
|
||||
content = f.readlines()
|
||||
with open(pyi, "w") as f:
|
||||
for line in content:
|
||||
if not line.startswith("class ads"):
|
||||
f.write(line)
|
||||
|
||||
def swig_sources (self, sources, extension=None):
|
||||
if not self.extensions:
|
||||
@@ -210,10 +231,9 @@ class build_ext(sipdistutils.build_ext):
|
||||
|
||||
return super().swig_sources(sources, extension)
|
||||
|
||||
def build_extension(self, ext):
|
||||
# /usr/bin/rcc -name ads ../../Qt-Advanced-Docking-System/src/ads.qrc -o release/qrc_ads.cpp
|
||||
|
||||
def build_extension(self, ext):
|
||||
cppsources = [source for source in ext.sources if source.endswith(".cpp")]
|
||||
headersources = ['src/DockAreaTitleBar_p.h']
|
||||
|
||||
dir_util.mkpath(self.build_temp, dry_run=self.dry_run)
|
||||
|
||||
@@ -247,6 +267,30 @@ class build_ext(sipdistutils.build_ext):
|
||||
if os.path.getsize(out_file) > 0:
|
||||
ext.sources.append(out_file)
|
||||
|
||||
# Run moc on all orphan header files.
|
||||
for source in headersources:
|
||||
# *.cpp -> *.moc
|
||||
moc_file = os.path.basename(source).replace(".h", ".moc")
|
||||
out_file = os.path.join(self.build_temp, moc_file)
|
||||
|
||||
if newer(source, out_file) or self.force:
|
||||
spawn.spawn(get_moc_args(out_file, source),
|
||||
dry_run=self.dry_run)
|
||||
|
||||
header = source
|
||||
if os.path.exists(header):
|
||||
# *.h -> moc_*.cpp
|
||||
moc_file = "moc_" + os.path.basename(header).replace(
|
||||
".h", ".cpp")
|
||||
out_file = os.path.join(self.build_temp, moc_file)
|
||||
|
||||
if newer(header, out_file) or self.force:
|
||||
spawn.spawn(get_moc_args(out_file, header),
|
||||
dry_run=self.dry_run)
|
||||
|
||||
if os.path.getsize(out_file) > 0:
|
||||
ext.sources.append(out_file)
|
||||
|
||||
# Add the temp build directory to include path, for compiler to find
|
||||
# the created .moc files
|
||||
ext.include_dirs += [self._sip_output_dir()]
|
||||
@@ -263,6 +307,17 @@ class build_ext(sipdistutils.build_ext):
|
||||
ext.sources.append(out_file)
|
||||
|
||||
sipdistutils.build_ext.build_extension(self, ext)
|
||||
|
||||
import inspect
|
||||
sys.path.append(os.path.join(self.build_lib, 'PyQtAds', 'QtAds'))
|
||||
import ads
|
||||
|
||||
with open(os.path.join(self.build_lib, 'PyQtAds', 'QtAds', '__init__.py'), 'w') as f:
|
||||
f.write('from .._version import *\n')
|
||||
f.write('from .ads import ads\n')
|
||||
for name, member in sorted(inspect.getmembers(ads.ads)):
|
||||
if not name.startswith('_'):
|
||||
f.write('{0} = ads.{0}\n'.format(name))
|
||||
|
||||
|
||||
class ProcessResourceCommand(cmd.Command):
|
||||
|
||||
37
sip/DockAreaTitleBar_p.sip
Normal file
@@ -0,0 +1,37 @@
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%If (Qt_5_0_0 -)
|
||||
|
||||
namespace ads
|
||||
{
|
||||
|
||||
class CTitleBarButton : QToolButton
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <DockAreaTitleBar_p.h>
|
||||
%End
|
||||
|
||||
protected:
|
||||
bool event(QEvent *ev);
|
||||
|
||||
public:
|
||||
CTitleBarButton(bool visible = true, QWidget* parent /TransferThis/ = Q_NULLPTR );
|
||||
virtual void setVisible(bool visible);
|
||||
};
|
||||
|
||||
|
||||
class CSpacerWidget : QWidget
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <DockAreaTitleBar_p.h>
|
||||
%End
|
||||
|
||||
public:
|
||||
CSpacerWidget(QWidget* Parent /TransferThis/ = 0 );
|
||||
virtual QSize sizeHint() const;
|
||||
virtual QSize minimumSizeHint() const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
%End
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
namespace ads
|
||||
{
|
||||
|
||||
|
||||
class CDockAreaWidget : QFrame
|
||||
{
|
||||
|
||||
@@ -28,10 +28,18 @@ protected slots:
|
||||
void toggleView(bool Open);
|
||||
|
||||
public:
|
||||
enum eDockAreaFlag
|
||||
{
|
||||
HideSingleWidgetTitleBar,
|
||||
DefaultFlags
|
||||
};
|
||||
typedef QFlags<ads::CDockAreaWidget::eDockAreaFlag> DockAreaFlags;
|
||||
|
||||
CDockAreaWidget(ads::CDockManager* DockManager /TransferThis/, ads::CDockContainerWidget* parent /TransferThis/);
|
||||
virtual ~CDockAreaWidget();
|
||||
ads::CDockManager* dockManager() const;
|
||||
ads::CDockContainerWidget* dockContainer() const;
|
||||
virtual QSize minimumSizeHint() const;
|
||||
QRect titleBarGeometry() const;
|
||||
QRect contentAreaGeometry() const;
|
||||
int dockWidgetsCount() const;
|
||||
@@ -51,6 +59,12 @@ public:
|
||||
void setAllowedAreas(DockWidgetAreas areas);
|
||||
DockWidgetAreas allowedAreas() const;
|
||||
CDockAreaTitleBar* titleBar() const;
|
||||
|
||||
DockAreaFlags dockAreaFlags() const;
|
||||
void setDockAreaFlags(DockAreaFlags Flags);
|
||||
void setDockAreaFlag(eDockAreaFlag Flag, bool On);
|
||||
|
||||
bool isCentralWidgetArea() const;
|
||||
|
||||
public slots:
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
@@ -32,6 +32,7 @@ protected:
|
||||
ads::CDockWidget* topLevelDockWidget() const;
|
||||
ads::CDockAreaWidget* topLevelDockArea() const;
|
||||
QList<ads::CDockWidget*> dockWidgets() const;
|
||||
void updateSplitterHandles(QSplitter* splitter);
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
33
sip/DockFocusController.sip
Normal file
@@ -0,0 +1,33 @@
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%If (Qt_5_0_0 -)
|
||||
|
||||
namespace ads
|
||||
{
|
||||
|
||||
/**
|
||||
* Manages focus styling of dock widgets and handling of focus changes
|
||||
*/
|
||||
class CDockFocusController : QObject
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <DockFocusController.h>
|
||||
%End
|
||||
|
||||
public:
|
||||
CDockFocusController(ads::CDockManager* DockManager);
|
||||
virtual ~CDockFocusController();
|
||||
|
||||
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
|
||||
void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget);
|
||||
ads::CDockWidget* focusedDockWidget() const;
|
||||
void setDockWidgetTabFocused(ads::CDockWidgetTab* Tab);
|
||||
|
||||
public slots:
|
||||
void setDockWidgetFocused(ads::CDockWidget* focusedNow);
|
||||
|
||||
}; // class DockFocusController
|
||||
};
|
||||
// namespace ads
|
||||
|
||||
%End
|
||||
@@ -1,7 +1,7 @@
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%If (Qt_5_0_0 -)
|
||||
|
||||
|
||||
%MappedType QMap<QString, ads::CDockWidget*>
|
||||
/TypeHint="Dict[QString, CDockWidget*]", TypeHintValue="{}"/
|
||||
{
|
||||
@@ -133,6 +133,8 @@ protected:
|
||||
void removeDockContainer(ads::CDockContainerWidget* DockContainer /TransferBack/);
|
||||
ads::CDockOverlay* containerOverlay() const;
|
||||
ads::CDockOverlay* dockAreaOverlay() const;
|
||||
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
|
||||
void notifyFloatingWidgetDrop(ads::CFloatingDockContainer* FloatingWidget);
|
||||
|
||||
virtual void showEvent(QShowEvent *event);
|
||||
|
||||
@@ -166,6 +168,10 @@ public:
|
||||
FloatingContainerHasWidgetTitle,
|
||||
FloatingContainerHasWidgetIcon,
|
||||
HideSingleCentralWidgetTitleBar,
|
||||
FocusHighlighting,
|
||||
EqualSplitOnInsertion,
|
||||
FloatingContainerForceNativeTitleBar,
|
||||
FloatingContainerForceQWidgetTitleBar,
|
||||
DefaultDockAreaButtons,
|
||||
DefaultBaseConfig,
|
||||
DefaultOpaqueConfig,
|
||||
@@ -194,23 +200,29 @@ public:
|
||||
const QList<ads::CDockContainerWidget*> dockContainers() const;
|
||||
const QList<ads::CFloatingDockContainer*> floatingWidgets() const;
|
||||
unsigned int zOrderIndex() const;
|
||||
QByteArray saveState(int version = 1) const;
|
||||
bool restoreState(const QByteArray &state, int version = 1);
|
||||
QByteArray saveState(int version = 0) const;
|
||||
bool restoreState(const QByteArray &state, int version = 0);
|
||||
void addPerspective(const QString& UniquePrespectiveName);
|
||||
void removePerspective(const QString& Name);
|
||||
void removePerspectives(const QStringList& Names);
|
||||
QStringList perspectiveNames() const;
|
||||
void savePerspectives(QSettings& Settings) const;
|
||||
void loadPerspectives(QSettings& Settings);
|
||||
CDockWidget* centralWidget() const;
|
||||
CDockAreaWidget* setCentralWidget(CDockWidget* widget /Transfer/);
|
||||
QAction* addToggleViewActionToMenu(QAction* ToggleViewAction /Transfer/,
|
||||
const QString& Group = QString(), const QIcon& GroupIcon = QIcon());
|
||||
QMenu* viewMenu() const;
|
||||
void setViewMenuInsertionOrder(ads::CDockManager::eViewMenuInsertionOrder Order);
|
||||
bool isRestoringState() const;
|
||||
static int startDragDistance();
|
||||
ads::CDockWidget* focusedDockWidget() const;
|
||||
QList<int> splitterSizes(ads::CDockAreaWidget *ContainedArea) const;
|
||||
void setSplitterSizes(ads::CDockAreaWidget *ContainedArea, const QList<int>& sizes);
|
||||
|
||||
public slots:
|
||||
void openPerspective(const QString& PerspectiveName);
|
||||
void setDockWidgetFocused(ads::CDockWidget* DockWidget);
|
||||
|
||||
signals:
|
||||
void perspectiveListChanged();
|
||||
@@ -219,10 +231,12 @@ signals:
|
||||
void stateRestored();
|
||||
void openingPerspective(const QString& PerspectiveName);
|
||||
void perspectiveOpened(const QString& PerspectiveName);
|
||||
void floatingWidgetCreated(CFloatingDockContainer* FloatingWidget);
|
||||
void dockAreaCreated(ads::CDockAreaWidget* DockArea);
|
||||
void dockWidgetAboutToBeRemoved(ads::CDockWidget* DockWidget);
|
||||
void dockWidgetRemoved(ads::CDockWidget* DockWidget);
|
||||
void floatingWidgetCreated(ads::CFloatingDockContainer*);
|
||||
void dockAreaCreated(ads::CDockAreaWidget*);
|
||||
void dockWidgetAdded(ads::CDockWidget* DockWidget);
|
||||
void dockWidgetAboutToBeRemoved(ads::CDockWidget*);
|
||||
void dockWidgetRemoved(ads::CDockWidget*);
|
||||
void focusedDockWidgetChanged(ads::CDockWidget*, ads::CDockWidget*);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
bool hasVisibleContent() const;
|
||||
QWidget* firstWidget() const;
|
||||
QWidget* lastWidget() const;
|
||||
bool isResizingWithContainer() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -31,8 +31,12 @@ public:
|
||||
DockWidgetFloatable,
|
||||
DockWidgetDeleteOnClose,
|
||||
CustomCloseHandling,
|
||||
DockWidgetFocusable,
|
||||
DockWidgetForceCloseWithArea,
|
||||
NoTab,
|
||||
DefaultDockWidgetFeatures,
|
||||
AllDockWidgetFeatures,
|
||||
DockWidgetAlwaysCloseAndDelete,
|
||||
NoDockWidgetFeatures
|
||||
};
|
||||
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;
|
||||
@@ -83,6 +87,7 @@ public:
|
||||
QAction* toggleViewAction() const;
|
||||
void setToggleViewActionMode(ads::CDockWidget::eToggleViewActionMode Mode);
|
||||
void setMinimumSizeHintMode(ads::CDockWidget::eMinimumSizeHintMode Mode);
|
||||
bool isCentralWidget() const;
|
||||
void setIcon(const QIcon& Icon);
|
||||
QIcon icon() const;
|
||||
QToolBar* toolBar() const;
|
||||
|
||||
@@ -34,6 +34,9 @@ public:
|
||||
bool isClosable() const;
|
||||
virtual bool event(QEvent *e);
|
||||
void setElideMode(Qt::TextElideMode mode);
|
||||
void updateStyle();
|
||||
QSize iconSize() const;
|
||||
void setIconSize(const QSize& Size);
|
||||
|
||||
public slots:
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
virtual void moveFloating() = 0;
|
||||
virtual void finishDragging() = 0;
|
||||
virtual ~IFloatingWidget();
|
||||
};
|
||||
|
||||
|
||||
@@ -51,11 +52,24 @@ protected:
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent *event);
|
||||
virtual void moveEvent(QMoveEvent *event);
|
||||
virtual bool event(QEvent *e);
|
||||
virtual void closeEvent(QCloseEvent *event);
|
||||
virtual void hideEvent(QHideEvent *event);
|
||||
virtual void showEvent(QShowEvent *event);
|
||||
|
||||
%If (WS_MACX)
|
||||
virtual bool event(QEvent *e);
|
||||
virtual void moveEvent(QMoveEvent *event);
|
||||
%End
|
||||
|
||||
%If (WS_X11)
|
||||
virtual void moveEvent(QMoveEvent *event);
|
||||
virtual void resizeEvent(QResizeEvent *event);
|
||||
%End
|
||||
|
||||
%If (WS_WIN)
|
||||
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
|
||||
%End
|
||||
|
||||
|
||||
public:
|
||||
CFloatingDockContainer(ads::CDockManager* DockManager /TransferThis/);
|
||||
@@ -67,6 +81,15 @@ public:
|
||||
bool hasTopLevelDockWidget() const;
|
||||
ads::CDockWidget* topLevelDockWidget() const;
|
||||
QList<ads::CDockWidget*> dockWidgets() const;
|
||||
|
||||
%If (WS_X11)
|
||||
void onMaximizeRequest();
|
||||
void showNormal(bool fixGeometry);
|
||||
void showMaximized();
|
||||
bool isMaximized() const;
|
||||
void show();
|
||||
bool hasNativeTitleBar();
|
||||
%End
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ class CFloatingDragPreview : QWidget, ads::IFloatingWidget
|
||||
%End
|
||||
|
||||
protected:
|
||||
virtual void moveEvent(QMoveEvent *event);
|
||||
virtual void paintEvent(QPaintEvent *e);
|
||||
CFloatingDragPreview(QWidget* Content /TransferThis/, QWidget* parent /TransferThis/);
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
%Include ads_globals.sip
|
||||
%Include DockWidget.sip
|
||||
%Include DockAreaTabBar.sip
|
||||
%Include DockAreaTitleBar_p.sip
|
||||
%Include DockAreaTitleBar.sip
|
||||
%Include DockAreaWidget.sip
|
||||
%Include DockComponentsFactory.sip
|
||||
%Include DockContainerWidget.sip
|
||||
%Include DockingStateReader.sip
|
||||
%Include DockFocusController.sip
|
||||
%Include DockManager.sip
|
||||
%Include DockOverlay.sip
|
||||
%Include DockSplitter.sip
|
||||
|
||||
@@ -1,67 +1,138 @@
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%If (Qt_5_0_0 -)
|
||||
|
||||
namespace ads
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <ads_globals.h>
|
||||
%End
|
||||
|
||||
enum eStateFileVersion
|
||||
{
|
||||
InitialVersion,
|
||||
Version1,
|
||||
CurrentVersion
|
||||
};
|
||||
|
||||
enum DockWidgetArea
|
||||
{
|
||||
NoDockWidgetArea,
|
||||
LeftDockWidgetArea,
|
||||
RightDockWidgetArea,
|
||||
TopDockWidgetArea,
|
||||
BottomDockWidgetArea,
|
||||
CenterDockWidgetArea,
|
||||
|
||||
InvalidDockWidgetArea,
|
||||
OuterDockAreas,
|
||||
AllDockAreas
|
||||
};
|
||||
typedef QFlags<ads::DockWidgetArea> DockWidgetAreas;
|
||||
|
||||
|
||||
enum TitleBarButton
|
||||
{
|
||||
TitleBarButtonTabsMenu,
|
||||
TitleBarButtonUndock,
|
||||
TitleBarButtonClose
|
||||
};
|
||||
|
||||
enum eDragState
|
||||
{
|
||||
DraggingInactive,
|
||||
DraggingMousePressed,
|
||||
DraggingTab,
|
||||
DraggingFloatingWidget
|
||||
};
|
||||
|
||||
enum eIcon
|
||||
{
|
||||
TabCloseIcon,
|
||||
DockAreaMenuIcon,
|
||||
DockAreaUndockIcon,
|
||||
DockAreaCloseIcon,
|
||||
|
||||
IconCount,
|
||||
};
|
||||
|
||||
enum eBitwiseOperator
|
||||
{
|
||||
BitwiseAnd,
|
||||
BitwiseOr
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
%End
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%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
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <ads_globals.h>
|
||||
%End
|
||||
|
||||
enum DockWidgetArea
|
||||
{
|
||||
NoDockWidgetArea,
|
||||
LeftDockWidgetArea,
|
||||
RightDockWidgetArea,
|
||||
TopDockWidgetArea,
|
||||
BottomDockWidgetArea,
|
||||
CenterDockWidgetArea,
|
||||
|
||||
InvalidDockWidgetArea,
|
||||
OuterDockAreas,
|
||||
AllDockAreas
|
||||
};
|
||||
typedef QFlags<ads::DockWidgetArea> DockWidgetAreas;
|
||||
|
||||
|
||||
enum TitleBarButton
|
||||
{
|
||||
TitleBarButtonTabsMenu,
|
||||
TitleBarButtonUndock,
|
||||
TitleBarButtonClose
|
||||
};
|
||||
|
||||
enum eDragState
|
||||
{
|
||||
DraggingInactive,
|
||||
DraggingMousePressed,
|
||||
DraggingTab,
|
||||
DraggingFloatingWidget
|
||||
};
|
||||
|
||||
enum eIcon
|
||||
{
|
||||
TabCloseIcon,
|
||||
DockAreaMenuIcon,
|
||||
DockAreaUndockIcon,
|
||||
DockAreaCloseIcon,
|
||||
|
||||
IconCount,
|
||||
};
|
||||
|
||||
enum eBitwiseOperator
|
||||
{
|
||||
BitwiseAnd,
|
||||
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
|
||||
|
||||
@@ -15,15 +15,24 @@ protected:
|
||||
virtual void mousePressEvent(QMouseEvent *ev);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *ev);
|
||||
virtual void mouseMoveEvent(QMouseEvent *ev);
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
|
||||
void setMaximizeIcon(const QIcon& Icon);
|
||||
QIcon maximizeIcon() const;
|
||||
void setNormalIcon(const QIcon& Icon);
|
||||
QIcon normalIcon() const;
|
||||
|
||||
public:
|
||||
explicit CFloatingWidgetTitleBar(CFloatingDockContainer *parent /TransferThis/ = 0);
|
||||
virtual ~CFloatingWidgetTitleBar();
|
||||
void enableCloseButton(bool Enable);
|
||||
void setTitle(const QString &Text);
|
||||
void updateStyle();
|
||||
void setMaximizedIcon(bool maximized);
|
||||
|
||||
signals:
|
||||
void closeRequested();
|
||||
void maximizeRequested();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
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)
|
||||
include_directories(${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
if(BUILD_STATIC)
|
||||
set(CMAKE_STATIC_LIBRARY_SUFFIX "_static${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
@@ -17,6 +21,7 @@ set(ads_SRCS
|
||||
DockWidget.cpp
|
||||
DockWidgetTab.cpp
|
||||
DockingStateReader.cpp
|
||||
DockFocusController.cpp
|
||||
ElidingLabel.cpp
|
||||
FloatingDockContainer.cpp
|
||||
FloatingDragPreview.cpp
|
||||
@@ -24,7 +29,7 @@ set(ads_SRCS
|
||||
DockComponentsFactory.cpp
|
||||
ads.qrc
|
||||
)
|
||||
set(ads_INSTALL_INCLUDE
|
||||
set(ads_HEADERS
|
||||
ads_globals.h
|
||||
DockAreaTabBar.h
|
||||
DockAreaTitleBar.h
|
||||
@@ -37,25 +42,29 @@ set(ads_INSTALL_INCLUDE
|
||||
DockWidget.h
|
||||
DockWidgetTab.h
|
||||
DockingStateReader.h
|
||||
DockFocusController.h
|
||||
ElidingLabel.h
|
||||
FloatingDockContainer.h
|
||||
FloatingDragPreview.h
|
||||
IconProvider.h
|
||||
DockComponentsFactory.h
|
||||
)
|
||||
if (UNIX)
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
if (UNIX AND NOT APPLE)
|
||||
set(ads_SRCS linux/FloatingWidgetTitleBar.cpp ${ads_SRCS})
|
||||
set(ads_INSTALL_INCLUDE linux/FloatingWidgetTitleBar.h ${ads_INSTALL_INCLUDE})
|
||||
set(ads_HEADERS linux/FloatingWidgetTitleBar.h ${ads_HEADERS})
|
||||
endif()
|
||||
if(BUILD_STATIC)
|
||||
add_library(qtadvanceddocking STATIC ${ads_SRCS})
|
||||
add_library(qtadvanceddocking STATIC ${ads_SRCS} ${ads_HEADERS})
|
||||
target_compile_definitions(qtadvanceddocking PUBLIC ADS_STATIC)
|
||||
else()
|
||||
add_library(qtadvanceddocking SHARED ${ads_SRCS})
|
||||
add_library(qtadvanceddocking SHARED ${ads_SRCS} ${ads_HEADERS})
|
||||
target_compile_definitions(qtadvanceddocking PRIVATE ADS_SHARED_EXPORT)
|
||||
endif()
|
||||
target_link_libraries(qtadvanceddocking PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
set_target_properties(qtadvanceddocking PROPERTIES
|
||||
target_link_libraries(qtadvanceddocking PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set_target_properties(qtadvanceddocking PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
CXX_STANDARD 14
|
||||
@@ -73,14 +82,14 @@ write_basic_package_version_file(
|
||||
VERSION ${VERSION_SHORT}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
install(FILES ${ads_INSTALL_INCLUDE}
|
||||
DESTINATION include
|
||||
install(FILES ${ads_HEADERS}
|
||||
DESTINATION include
|
||||
COMPONENT headers
|
||||
)
|
||||
install(FILES
|
||||
install(FILES
|
||||
"${CMAKE_SOURCE_DIR}/LICENSE"
|
||||
"${CMAKE_SOURCE_DIR}/gnu-lgpl-v2.1.md"
|
||||
DESTINATION license
|
||||
DESTINATION license
|
||||
COMPONENT license
|
||||
)
|
||||
install(TARGETS qtadvanceddocking
|
||||
@@ -102,5 +111,5 @@ install(FILES qtadvanceddockingConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/qtadvan
|
||||
|
||||
target_include_directories(qtadvanceddocking PUBLIC
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
)
|
||||
|
||||
|
||||
@@ -138,6 +138,8 @@ CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
|
||||
d->TabsLayout->addStretch(1);
|
||||
d->TabsContainerWidget->setLayout(d->TabsLayout);
|
||||
setWidget(d->TabsContainerWidget);
|
||||
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,11 +180,11 @@ void CDockAreaTabBar::setCurrentIndex(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
Q_EMIT currentChanging(index);
|
||||
d->CurrentIndex = index;
|
||||
d->updateTabs();
|
||||
updateGeometry();
|
||||
emit currentChanged(index);
|
||||
Q_EMIT currentChanged(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +206,7 @@ void CDockAreaTabBar::insertTab(int Index, CDockWidgetTab* Tab)
|
||||
connect(Tab, SIGNAL(moved(const QPoint&)), this, SLOT(onTabWidgetMoved(const QPoint&)));
|
||||
connect(Tab, SIGNAL(elidedChanged(bool)), this, SIGNAL(elidedChanged(bool)));
|
||||
Tab->installEventFilter(this);
|
||||
emit tabInserted(Index);
|
||||
Q_EMIT tabInserted(Index);
|
||||
if (Index <= d->CurrentIndex)
|
||||
{
|
||||
setCurrentIndex(d->CurrentIndex + 1);
|
||||
@@ -264,7 +266,7 @@ void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
|
||||
}
|
||||
}
|
||||
|
||||
emit removingTab(RemoveIndex);
|
||||
Q_EMIT removingTab(RemoveIndex);
|
||||
d->TabsLayout->removeWidget(Tab);
|
||||
Tab->disconnect(this);
|
||||
Tab->removeEventFilter(this);
|
||||
@@ -318,7 +320,7 @@ void CDockAreaTabBar::onTabClicked()
|
||||
return;
|
||||
}
|
||||
setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
Q_EMIT tabBarClicked(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -407,7 +409,7 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
|
||||
d->TabsLayout->removeWidget(MovingTab);
|
||||
d->TabsLayout->insertWidget(toIndex, MovingTab);
|
||||
ADS_PRINT("tabMoved from " << fromIndex << " to " << toIndex);
|
||||
emit tabMoved(fromIndex, toIndex);
|
||||
Q_EMIT tabMoved(fromIndex, toIndex);
|
||||
setCurrentIndex(toIndex);
|
||||
}
|
||||
else
|
||||
@@ -430,7 +432,7 @@ void CDockAreaTabBar::closeTab(int Index)
|
||||
{
|
||||
return;
|
||||
}
|
||||
emit tabCloseRequested(Index);
|
||||
Q_EMIT tabCloseRequested(Index);
|
||||
}
|
||||
|
||||
|
||||
@@ -447,15 +449,20 @@ bool CDockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::Hide:
|
||||
emit tabClosed(d->TabsLayout->indexOf(Tab));
|
||||
Q_EMIT tabClosed(d->TabsLayout->indexOf(Tab));
|
||||
updateGeometry();
|
||||
break;
|
||||
|
||||
case QEvent::Show:
|
||||
emit tabOpened(d->TabsLayout->indexOf(Tab));
|
||||
Q_EMIT tabOpened(d->TabsLayout->indexOf(Tab));
|
||||
updateGeometry();
|
||||
break;
|
||||
|
||||
// Setting the text of a tab will cause a LayoutRequest event
|
||||
case QEvent::LayoutRequest:
|
||||
updateGeometry();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
friend struct DockAreaTabBarPrivate;
|
||||
friend class CDockAreaTitleBar;
|
||||
|
||||
private slots:
|
||||
private Q_SLOTS:
|
||||
void onTabClicked();
|
||||
void onTabCloseRequested();
|
||||
void onCloseOtherTabsRequested();
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
*/
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
public slots:
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* This property sets the index of the tab bar's visible tab
|
||||
*/
|
||||
@@ -152,7 +152,7 @@ public slots:
|
||||
*/
|
||||
void closeTab(int Index);
|
||||
|
||||
signals:
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||
* current has the given index, or -1 if there isn't a new one.
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "DockAreaTabBar.h"
|
||||
#include "IconProvider.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
#include "DockFocusController.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -103,10 +104,11 @@ struct DockAreaTitleBarPrivate
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set
|
||||
* Convenience function to ease config flag testing
|
||||
*/
|
||||
static bool testConfigFlag(CDockManager::eConfigFlag Flag)
|
||||
{
|
||||
return CDockManager::configFlags().testFlag(Flag);
|
||||
return CDockManager::testConfigFlag(Flag);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +164,7 @@ void DockAreaTitleBarPrivate::createButtons()
|
||||
|
||||
// Undock button
|
||||
UndockButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasUndockButton));
|
||||
UndockButton->setObjectName("undockButton");
|
||||
UndockButton->setObjectName("detachGroupButton");
|
||||
UndockButton->setAutoRaise(true);
|
||||
internal::setToolTip(UndockButton, QObject::tr("Detach Group"));
|
||||
internal::setButtonIcon(UndockButton, QStyle::SP_TitleBarNormalButton, ads::DockAreaUndockIcon);
|
||||
@@ -172,7 +174,7 @@ void DockAreaTitleBarPrivate::createButtons()
|
||||
|
||||
// Close button
|
||||
CloseButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasCloseButton));
|
||||
CloseButton->setObjectName("closeButton");
|
||||
CloseButton->setObjectName("dockAreaCloseButton");
|
||||
CloseButton->setAutoRaise(true);
|
||||
internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, ads::DockAreaCloseIcon);
|
||||
if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
|
||||
@@ -212,7 +214,7 @@ IFloatingWidget* DockAreaTitleBarPrivate::makeAreaFloating(const QPoint& Offset,
|
||||
{
|
||||
QSize Size = DockArea->size();
|
||||
this->DragState = DragState;
|
||||
bool OpaqueUndocking = CDockManager::configFlags().testFlag(CDockManager::OpaqueUndocking) ||
|
||||
bool OpaqueUndocking = CDockManager::testConfigFlag(CDockManager::OpaqueUndocking) ||
|
||||
(DraggingFloatingWidget != DragState);
|
||||
CFloatingDockContainer* FloatingDockContainer = nullptr;
|
||||
IFloatingWidget* FloatingWidget;
|
||||
@@ -268,6 +270,8 @@ CDockAreaTitleBar::CDockAreaTitleBar(CDockAreaWidget* parent) :
|
||||
d->createTabBar();
|
||||
d->Layout->addWidget(new CSpacerWidget(this));
|
||||
d->createButtons();
|
||||
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
|
||||
@@ -379,7 +383,7 @@ void CDockAreaTitleBar::onTabsMenuActionTriggered(QAction* Action)
|
||||
{
|
||||
int Index = Action->data().toInt();
|
||||
d->TabBar->setCurrentIndex(Index);
|
||||
emit tabBarClicked(Index);
|
||||
Q_EMIT tabBarClicked(Index);
|
||||
}
|
||||
|
||||
|
||||
@@ -465,6 +469,12 @@ void CDockAreaTitleBar::mousePressEvent(QMouseEvent* ev)
|
||||
ev->accept();
|
||||
d->DragStartMousePos = ev->pos();
|
||||
d->DragState = DraggingMousePressed;
|
||||
|
||||
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
{
|
||||
//d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason);
|
||||
d->dockManager()->dockFocusController()->setDockWidgetTabFocused(d->TabBar->currentTab());
|
||||
}
|
||||
return;
|
||||
}
|
||||
Super::mousePressEvent(ev);
|
||||
@@ -485,6 +495,7 @@ void CDockAreaTitleBar::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
d->FloatingWidget->finishDragging();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Super::mouseReleaseEvent(ev);
|
||||
@@ -570,12 +581,12 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev)
|
||||
}
|
||||
|
||||
QMenu Menu(this);
|
||||
auto Action = Menu.addAction(tr("Detach Area"), this, SLOT(onUndockButtonClicked()));
|
||||
auto Action = Menu.addAction(tr("Detach Group"), this, SLOT(onUndockButtonClicked()));
|
||||
Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable));
|
||||
Menu.addSeparator();
|
||||
Action = Menu.addAction(tr("Close Area"), this, SLOT(onCloseButtonClicked()));
|
||||
Action = Menu.addAction(tr("Close Group"), this, SLOT(onCloseButtonClicked()));
|
||||
Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable));
|
||||
Menu.addAction(tr("Close Other Areas"), d->DockArea, SLOT(closeOtherAreas()));
|
||||
Menu.addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas()));
|
||||
Menu.exec(ev->globalPos());
|
||||
}
|
||||
|
||||
@@ -594,11 +605,12 @@ int CDockAreaTitleBar::indexOf(QWidget *widget) const
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
CTitleBarButton::CTitleBarButton(bool visible /*= true*/, QWidget* parent /*= nullptr*/) : tTitleBarButton(parent),
|
||||
Visible(visible),
|
||||
HideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(CDockManager::DockAreaHideDisabledButtons))
|
||||
CTitleBarButton::CTitleBarButton(bool visible, QWidget* parent)
|
||||
: tTitleBarButton(parent),
|
||||
Visible(visible),
|
||||
HideWhenDisabled(CDockManager::testConfigFlag(CDockManager::DockAreaHideDisabledButtons))
|
||||
{
|
||||
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
@@ -54,7 +54,7 @@ private:
|
||||
DockAreaTitleBarPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockAreaTitleBarPrivate;
|
||||
|
||||
private slots:
|
||||
private Q_SLOTS:
|
||||
void onTabsMenuAboutToShow();
|
||||
void onCloseButtonClicked();
|
||||
void onUndockButtonClicked();
|
||||
@@ -88,7 +88,7 @@ protected:
|
||||
*/
|
||||
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
public slots:
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Call this slot to tell the title bar that it should update the tabs menu
|
||||
* the next time it is shown.
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
*/
|
||||
int indexOf(QWidget *widget) const;
|
||||
|
||||
signals:
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||
* or if the user clicks on a tab item in the title bar tab menu.
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
//============================================================================
|
||||
#include "DockAreaWidget.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QStackedLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QScrollArea>
|
||||
@@ -136,6 +134,10 @@ public:
|
||||
m_CurrentWidget = nullptr;
|
||||
m_CurrentIndex = -1;
|
||||
}
|
||||
else if (indexOf(Widget) < m_CurrentIndex)
|
||||
{
|
||||
--m_CurrentIndex;
|
||||
}
|
||||
m_Widgets.removeOne(Widget);
|
||||
}
|
||||
|
||||
@@ -173,6 +175,7 @@ public:
|
||||
{
|
||||
LayoutItem->widget()->setParent(nullptr);
|
||||
}
|
||||
delete LayoutItem;
|
||||
|
||||
m_ParentLayout->addWidget(next);
|
||||
if (prev)
|
||||
@@ -233,6 +236,7 @@ public:
|
||||
|
||||
|
||||
using DockAreaLayout = CDockAreaLayout;
|
||||
static const DockWidgetAreas DefaultAllowedAreas = AllDockAreas;
|
||||
|
||||
|
||||
/**
|
||||
@@ -246,8 +250,9 @@ struct DockAreaWidgetPrivate
|
||||
CDockAreaTitleBar* TitleBar = nullptr;
|
||||
CDockManager* DockManager = nullptr;
|
||||
bool UpdateTitleBarButtons = false;
|
||||
DockWidgetAreas AllowedAreas = AllDockAreas;
|
||||
DockWidgetAreas AllowedAreas = DefaultAllowedAreas;
|
||||
QSize MinSizeHint;
|
||||
CDockAreaWidget::DockAreaFlags Flags{CDockAreaWidget::DefaultFlags};
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
@@ -319,7 +324,6 @@ struct DockAreaWidgetPrivate
|
||||
}
|
||||
}
|
||||
};
|
||||
// struct DockAreaWidgetPrivate
|
||||
|
||||
|
||||
//============================================================================
|
||||
@@ -374,7 +378,7 @@ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget
|
||||
d->ContentsLayout = new DockAreaLayout(d->Layout);
|
||||
if (d->DockManager)
|
||||
{
|
||||
emit d->DockManager->dockAreaCreated(this);
|
||||
Q_EMIT d->DockManager->dockAreaCreated(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +417,7 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
|
||||
bool Activate)
|
||||
{
|
||||
d->ContentsLayout->insertWidget(index, DockWidget);
|
||||
DockWidget->setDockArea(this);
|
||||
DockWidget->tabWidget()->setDockAreaWidget(this);
|
||||
auto TabWidget = DockWidget->tabWidget();
|
||||
// Inserting the tab will change the current index which in turn will
|
||||
@@ -428,7 +433,6 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
|
||||
{
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
DockWidget->setDockArea(this);
|
||||
// If this dock area is hidden, then we need to make it visible again
|
||||
// by calling DockWidget->toggleViewInternal(true);
|
||||
if (!this->isVisible() && d->ContentsLayout->count() > 1 && !dockManager()->isRestoringState())
|
||||
@@ -436,6 +440,7 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
|
||||
DockWidget->toggleViewInternal(true);
|
||||
}
|
||||
d->updateTitleBarButtonStates();
|
||||
updateTitleBarVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -443,12 +448,15 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
|
||||
void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
|
||||
{
|
||||
ADS_PRINT("CDockAreaWidget::removeDockWidget");
|
||||
auto NextOpenDockWidget = nextOpenDockWidget(DockWidget);
|
||||
auto CurrentDockWidget = currentDockWidget();
|
||||
auto NextOpenDockWidget = (DockWidget == CurrentDockWidget) ? nextOpenDockWidget(DockWidget) : nullptr;
|
||||
|
||||
d->ContentsLayout->removeWidget(DockWidget);
|
||||
auto TabWidget = DockWidget->tabWidget();
|
||||
TabWidget->hide();
|
||||
d->tabBar()->removeTab(TabWidget);
|
||||
TabWidget->setParent(DockWidget);
|
||||
DockWidget->setDockArea(nullptr);
|
||||
CDockContainerWidget* DockContainer = dockContainer();
|
||||
if (NextOpenDockWidget)
|
||||
{
|
||||
@@ -459,8 +467,16 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
|
||||
ADS_PRINT("Dock Area empty");
|
||||
DockContainer->removeDockArea(this);
|
||||
this->deleteLater();
|
||||
if(DockContainer->dockAreaCount() == 0)
|
||||
{
|
||||
if(CFloatingDockContainer* FloatingDockContainer = DockContainer->floatingWidget())
|
||||
{
|
||||
FloatingDockContainer->hide();
|
||||
FloatingDockContainer->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (DockWidget == CurrentDockWidget)
|
||||
{
|
||||
// if contents layout is not empty but there are no more open dock
|
||||
// widgets, then we need to hide the dock area because it does not
|
||||
@@ -588,11 +604,11 @@ void CDockAreaWidget::setCurrentIndex(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
Q_EMIT currentChanging(index);
|
||||
TabBar->setCurrentIndex(index);
|
||||
d->ContentsLayout->setCurrentIndex(index);
|
||||
d->ContentsLayout->currentWidget()->show();
|
||||
emit currentChanged(index);
|
||||
Q_EMIT currentChanged(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -731,7 +747,7 @@ void CDockAreaWidget::updateTitleBarVisibility()
|
||||
return;
|
||||
}
|
||||
|
||||
if (CDockManager::configFlags().testFlag(CDockManager::AlwaysShowTabs))
|
||||
if (CDockManager::testConfigFlag(CDockManager::AlwaysShowTabs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -739,7 +755,8 @@ void CDockAreaWidget::updateTitleBarVisibility()
|
||||
if (d->TitleBar)
|
||||
{
|
||||
bool Hidden = Container->hasTopLevelDockWidget() && (Container->isFloating()
|
||||
|| CDockManager::configFlags().testFlag(CDockManager::HideSingleCentralWidgetTitleBar));
|
||||
|| CDockManager::testConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar));
|
||||
Hidden |= (d->Flags.testFlag(HideSingleWidgetTitleBar) && openDockWidgetsCount() == 1);
|
||||
d->TitleBar->setVisible(!Hidden);
|
||||
}
|
||||
}
|
||||
@@ -764,6 +781,17 @@ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const
|
||||
auto CurrentDockWidget = currentDockWidget();
|
||||
QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : "";
|
||||
s.writeAttribute("Current", Name);
|
||||
// To keep the saved XML data small, we only save the allowed areas and the
|
||||
// dock area flags if the values are different from the default values
|
||||
if (d->AllowedAreas != DefaultAllowedAreas)
|
||||
{
|
||||
s.writeAttribute("AllowedAreas", QString::number(d->AllowedAreas, 16));
|
||||
}
|
||||
|
||||
if (d->Flags != DefaultFlags)
|
||||
{
|
||||
s.writeAttribute("Flags", QString::number(d->Flags, 16));
|
||||
}
|
||||
ADS_PRINT("CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
|
||||
<< " Current: " << Name);
|
||||
for (int i = 0; i < d->ContentsLayout->count(); ++i)
|
||||
@@ -780,18 +808,49 @@ CDockWidget* CDockAreaWidget::nextOpenDockWidget(CDockWidget* DockWidget) const
|
||||
auto OpenDockWidgets = openedDockWidgets();
|
||||
if (OpenDockWidgets.count() > 1 || (OpenDockWidgets.count() == 1 && OpenDockWidgets[0] != DockWidget))
|
||||
{
|
||||
CDockWidget* NextDockWidget;
|
||||
if (OpenDockWidgets.last() == DockWidget)
|
||||
{
|
||||
NextDockWidget = OpenDockWidgets[OpenDockWidgets.count() - 2];
|
||||
CDockWidget* NextDockWidget = OpenDockWidgets[OpenDockWidgets.count() - 2];
|
||||
// search backwards for widget with tab
|
||||
for (int i = OpenDockWidgets.count() - 2; i >= 0; --i)
|
||||
{
|
||||
auto dw = OpenDockWidgets[i];
|
||||
if (!dw->features().testFlag(CDockWidget::NoTab))
|
||||
{
|
||||
return dw;
|
||||
}
|
||||
}
|
||||
|
||||
// return widget without tab
|
||||
return NextDockWidget;
|
||||
}
|
||||
else
|
||||
{
|
||||
int NextIndex = OpenDockWidgets.indexOf(DockWidget) + 1;
|
||||
NextDockWidget = OpenDockWidgets[NextIndex];
|
||||
}
|
||||
int IndexOfDockWidget = OpenDockWidgets.indexOf(DockWidget);
|
||||
CDockWidget* NextDockWidget = OpenDockWidgets[IndexOfDockWidget + 1];
|
||||
// search forwards for widget with tab
|
||||
for (int i = IndexOfDockWidget + 1; i < OpenDockWidgets.count(); ++i)
|
||||
{
|
||||
auto dw = OpenDockWidgets[i];
|
||||
if (!dw->features().testFlag(CDockWidget::NoTab))
|
||||
{
|
||||
return dw;
|
||||
}
|
||||
}
|
||||
|
||||
return NextDockWidget;
|
||||
// search backwards for widget with tab
|
||||
for (int i = IndexOfDockWidget - 1; i >= 0; --i)
|
||||
{
|
||||
auto dw = OpenDockWidgets[i];
|
||||
if (!dw->features().testFlag(CDockWidget::NoTab))
|
||||
{
|
||||
return dw;
|
||||
}
|
||||
}
|
||||
|
||||
// return widget without tab
|
||||
return NextDockWidget;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -829,7 +888,7 @@ void CDockAreaWidget::toggleView(bool Open)
|
||||
{
|
||||
setVisible(Open);
|
||||
|
||||
emit viewToggled(Open);
|
||||
Q_EMIT viewToggled(Open);
|
||||
}
|
||||
|
||||
|
||||
@@ -843,16 +902,49 @@ void CDockAreaWidget::setVisible(bool Visible)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setAllowedAreas(DockWidgetAreas areas)
|
||||
{
|
||||
d->AllowedAreas = areas;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
DockWidgetAreas CDockAreaWidget::allowedAreas() const
|
||||
{
|
||||
return d->AllowedAreas;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget::DockAreaFlags CDockAreaWidget::dockAreaFlags() const
|
||||
{
|
||||
return d->Flags;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setDockAreaFlags(DockAreaFlags Flags)
|
||||
{
|
||||
auto ChangedFlags = d->Flags ^ Flags;
|
||||
d->Flags = Flags;
|
||||
if (ChangedFlags.testFlag(HideSingleWidgetTitleBar))
|
||||
{
|
||||
updateTitleBarVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setDockAreaFlag(eDockAreaFlag Flag, bool On)
|
||||
{
|
||||
auto flags = dockAreaFlags();
|
||||
internal::setFlag(flags, Flag, On);
|
||||
setDockAreaFlags(flags);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QAbstractButton* CDockAreaWidget::titleBarButton(TitleBarButton which) const
|
||||
{
|
||||
@@ -866,17 +958,20 @@ void CDockAreaWidget::closeArea()
|
||||
// If there is only one single dock widget and this widget has the
|
||||
// DeleteOnClose feature, then we delete the dock widget now
|
||||
auto OpenDockWidgets = openedDockWidgets();
|
||||
if (OpenDockWidgets.count() == 1 && OpenDockWidgets[0]->features().testFlag(CDockWidget::DockWidgetDeleteOnClose))
|
||||
if (OpenDockWidgets.count() == 1 && OpenDockWidgets[0]->features().testFlag(CDockWidget::DockWidgetDeleteOnClose))
|
||||
{
|
||||
OpenDockWidgets[0]->closeDockWidgetInternal();
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
for (auto DockWidget : openedDockWidgets())
|
||||
{
|
||||
DockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
for (auto DockWidget : openedDockWidgets())
|
||||
{
|
||||
if (DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) && DockWidget->features().testFlag(CDockWidget::DockWidgetForceCloseWithArea))
|
||||
DockWidget->closeDockWidgetInternal();
|
||||
else
|
||||
DockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -894,11 +989,62 @@ CDockAreaTitleBar* CDockAreaWidget::titleBar() const
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockAreaWidget::isCentralWidgetArea() const
|
||||
{
|
||||
if (dockWidgetsCount()!= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return dockManager()->centralWidget() == dockWidgets()[0];
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QSize CDockAreaWidget::minimumSizeHint() const
|
||||
{
|
||||
return d->MinSizeHint.isValid() ? d->MinSizeHint : Super::minimumSizeHint();
|
||||
if (!d->MinSizeHint.isValid())
|
||||
{
|
||||
return Super::minimumSizeHint();
|
||||
}
|
||||
|
||||
if (d->TitleBar->isVisible())
|
||||
{
|
||||
return d->MinSizeHint + QSize(0, d->TitleBar->minimumSizeHint().height());
|
||||
}
|
||||
else
|
||||
{
|
||||
return d->MinSizeHint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::onDockWidgetFeaturesChanged()
|
||||
{
|
||||
if (d->TitleBar)
|
||||
{
|
||||
d->updateTitleBarButtonStates();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -65,8 +65,9 @@ private:
|
||||
friend class CDockWidget;
|
||||
friend struct DockManagerPrivate;
|
||||
friend class CDockManager;
|
||||
void onDockWidgetFeaturesChanged();
|
||||
|
||||
private slots:
|
||||
private Q_SLOTS:
|
||||
void onTabCloseRequested(int Index);
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,17 @@ private slots:
|
||||
void reorderDockWidget(int fromIndex, int toIndex);
|
||||
|
||||
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.
|
||||
* All dockwidgets in the dock area tabified in a stacked layout with tabs.
|
||||
@@ -137,13 +149,23 @@ protected:
|
||||
*/
|
||||
void markTitleBarMenuOutdated();
|
||||
|
||||
protected slots:
|
||||
protected Q_SLOTS:
|
||||
void toggleView(bool Open);
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
/**
|
||||
* Dock area related flags
|
||||
*/
|
||||
enum eDockAreaFlag
|
||||
{
|
||||
HideSingleWidgetTitleBar = 0x0001,
|
||||
DefaultFlags = 0x0000
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockAreaFlags, eDockAreaFlag)
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent);
|
||||
@@ -277,7 +299,31 @@ public:
|
||||
*/
|
||||
CDockAreaTitleBar* titleBar() const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Returns the dock area flags - a combination of flags that configure the
|
||||
* appearance and features of the dock area.
|
||||
* \see setDockAreaFlasg()
|
||||
*/
|
||||
DockAreaFlags dockAreaFlags() const;
|
||||
|
||||
/**
|
||||
* Sets the dock area flags - a combination of flags that configure the
|
||||
* appearance and features of the dock area
|
||||
*/
|
||||
void setDockAreaFlags(DockAreaFlags Flags);
|
||||
|
||||
/**
|
||||
* Sets the dock area flag Flag on this widget if on is true; otherwise
|
||||
* clears the flag.
|
||||
*/
|
||||
void setDockAreaFlag(eDockAreaFlag Flag, bool On);
|
||||
|
||||
/**
|
||||
* Returns true if the area contains the central widget of it's manager.
|
||||
*/
|
||||
bool isCentralWidgetArea() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* This activates the tab for the given tab index.
|
||||
* If the dock widget for the given tab is not visible, the this function
|
||||
@@ -295,7 +341,7 @@ public slots:
|
||||
*/
|
||||
void closeOtherAreas();
|
||||
|
||||
signals:
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when user clicks on a tab at an index.
|
||||
*/
|
||||
|
||||
@@ -147,12 +147,12 @@ public:
|
||||
* Adds dock widget to container and returns the dock area that contains
|
||||
* the inserted dock widget
|
||||
*/
|
||||
CDockAreaWidget* dockWidgetIntoContainer(DockWidgetArea area, CDockWidget* Dockwidget);
|
||||
CDockAreaWidget* addDockWidgetToContainer(DockWidgetArea area, CDockWidget* Dockwidget);
|
||||
|
||||
/**
|
||||
* Adds dock widget to a existing DockWidgetArea
|
||||
*/
|
||||
CDockAreaWidget* dockWidgetIntoDockArea(DockWidgetArea area, CDockWidget* Dockwidget,
|
||||
CDockAreaWidget* addDockWidgetToDockArea(DockWidgetArea area, CDockWidget* Dockwidget,
|
||||
CDockAreaWidget* TargetDockArea);
|
||||
|
||||
/**
|
||||
@@ -284,13 +284,13 @@ public:
|
||||
void emitDockAreasRemoved()
|
||||
{
|
||||
onVisibleDockAreaCountChanged();
|
||||
emit _this->dockAreasRemoved();
|
||||
Q_EMIT _this->dockAreasRemoved();
|
||||
}
|
||||
|
||||
void emitDockAreasAdded()
|
||||
{
|
||||
onVisibleDockAreaCountChanged();
|
||||
emit _this->dockAreasAdded();
|
||||
Q_EMIT _this->dockAreasAdded();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,11 +299,41 @@ public:
|
||||
CDockSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = nullptr)
|
||||
{
|
||||
CDockSplitter* s = new CDockSplitter(orientation, parent);
|
||||
s->setOpaqueResize(CDockManager::configFlags().testFlag(CDockManager::OpaqueSplitterResize));
|
||||
s->setOpaqueResize(CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize));
|
||||
s->setChildrenCollapsible(false);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures equal distribution of the sizes of a splitter if an dock widget
|
||||
* is inserted from code
|
||||
*/
|
||||
void adjustSplitterSizesOnInsertion(QSplitter* Splitter, qreal LastRatio = 1.0)
|
||||
{
|
||||
int AreaSize = (Splitter->orientation() == Qt::Horizontal) ? Splitter->width() : Splitter->height();
|
||||
auto SplitterSizes = Splitter->sizes();
|
||||
|
||||
qreal TotRatio = SplitterSizes.size() - 1.0 + LastRatio;
|
||||
for(int i = 0; i < SplitterSizes.size() -1; i++)
|
||||
{
|
||||
SplitterSizes[i] = AreaSize / TotRatio;
|
||||
}
|
||||
SplitterSizes.back() = AreaSize * LastRatio / TotRatio;
|
||||
Splitter->setSizes(SplitterSizes);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function forces the dock container widget to update handles of splitters
|
||||
* based if a central widget exists.
|
||||
*/
|
||||
void updateSplitterHandles(QSplitter* splitter);
|
||||
|
||||
/**
|
||||
* If no central widget exists, the widgets resize with the container.
|
||||
* If a central widget exists, the widgets surrounding the central widget
|
||||
* do not resize its height or width.
|
||||
*/
|
||||
bool widgetResizesWithContainer(QWidget* widget);
|
||||
|
||||
// private slots: ------------------------------------------------------------
|
||||
void onDockAreaViewToggled(bool Visible)
|
||||
@@ -311,7 +341,7 @@ public:
|
||||
CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(_this->sender());
|
||||
VisibleDockAreaCount += Visible ? 1 : -1;
|
||||
onVisibleDockAreaCountChanged();
|
||||
emit _this->dockAreaViewToggled(DockArea, Visible);
|
||||
Q_EMIT _this->dockAreaViewToggled(DockArea, Visible);
|
||||
}
|
||||
}; // struct DockContainerWidgetPrivate
|
||||
|
||||
@@ -403,7 +433,8 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float
|
||||
QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
|
||||
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
|
||||
NewSplitter->addWidget(Splitter);
|
||||
Splitter = NewSplitter;
|
||||
updateSplitterHandles(NewSplitter);
|
||||
Splitter = NewSplitter;
|
||||
delete li;
|
||||
}
|
||||
|
||||
@@ -412,19 +443,21 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float
|
||||
if (FloatingSplitter->count() == 1)
|
||||
{
|
||||
insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append());
|
||||
}
|
||||
updateSplitterHandles(Splitter);
|
||||
}
|
||||
else if (FloatingSplitter->orientation() == InsertParam.orientation())
|
||||
{
|
||||
int InsertIndex = InsertParam.append() ? Splitter->count() : 0;
|
||||
while (FloatingSplitter->count())
|
||||
{
|
||||
Splitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0));
|
||||
}
|
||||
}
|
||||
updateSplitterHandles(Splitter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
insertWidgetIntoSplitter(Splitter, FloatingSplitter, InsertParam.append());
|
||||
}
|
||||
}
|
||||
|
||||
RootSplitter = Splitter;
|
||||
addDockAreasToList(NewDockAreas);
|
||||
@@ -435,7 +468,7 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float
|
||||
if (!Splitter->isVisible())
|
||||
{
|
||||
Splitter->show();
|
||||
}
|
||||
}
|
||||
_this->dumpLayout();
|
||||
}
|
||||
|
||||
@@ -497,7 +530,8 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin
|
||||
QSplitter* Splitter = newSplitter(InsertParam.orientation());
|
||||
Layout->replaceWidget(TargetArea, Splitter);
|
||||
Splitter->addWidget(TargetArea);
|
||||
TargetAreaSplitter = Splitter;
|
||||
updateSplitterHandles(Splitter);
|
||||
TargetAreaSplitter = Splitter;
|
||||
}
|
||||
int AreaIndex = TargetAreaSplitter->indexOf(TargetArea);
|
||||
auto Widget = FloatingWidget->dockContainer()->findChild<QWidget*>(QString(), Qt::FindDirectChildrenOnly);
|
||||
@@ -511,7 +545,8 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin
|
||||
if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
|
||||
{
|
||||
TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), Widget);
|
||||
}
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
}
|
||||
else
|
||||
{
|
||||
AdjustSplitterSizes = (FloatingSplitter->count() == 1);
|
||||
@@ -519,8 +554,9 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin
|
||||
while (FloatingSplitter->count())
|
||||
{
|
||||
TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0));
|
||||
}
|
||||
}
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
}
|
||||
}
|
||||
|
||||
if (AdjustSplitterSizes)
|
||||
{
|
||||
@@ -539,28 +575,32 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin
|
||||
if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
|
||||
{
|
||||
NewSplitter->addWidget(Widget);
|
||||
}
|
||||
updateSplitterHandles(NewSplitter);
|
||||
}
|
||||
else
|
||||
{
|
||||
AdjustSplitterSizes = (FloatingSplitter->count() == 1);
|
||||
while (FloatingSplitter->count())
|
||||
{
|
||||
NewSplitter->addWidget(FloatingSplitter->widget(0));
|
||||
}
|
||||
}
|
||||
updateSplitterHandles(NewSplitter);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the sizes before insertion and restore it later to prevent
|
||||
// shrinking of existing area
|
||||
auto Sizes = TargetAreaSplitter->sizes();
|
||||
insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append());
|
||||
if (AdjustSplitterSizes)
|
||||
updateSplitterHandles(NewSplitter);
|
||||
if (AdjustSplitterSizes)
|
||||
{
|
||||
int Size = TargetAreaSize / 2;
|
||||
NewSplitter->setSizes({Size, Size});
|
||||
}
|
||||
TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter);
|
||||
TargetAreaSplitter->setSizes(Sizes);
|
||||
}
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
}
|
||||
|
||||
addDockAreasToList(NewDockAreas);
|
||||
_this->dumpLayout();
|
||||
@@ -645,7 +685,8 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg
|
||||
{
|
||||
int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height();
|
||||
TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), NewDockArea);
|
||||
int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2;
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2;
|
||||
Sizes[AreaIndex] = Size;
|
||||
Sizes.insert(AreaIndex, Size);
|
||||
}
|
||||
@@ -656,16 +697,58 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg
|
||||
QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
|
||||
NewSplitter->addWidget(TargetArea);
|
||||
insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
|
||||
int Size = TargetAreaSize / 2;
|
||||
updateSplitterHandles(NewSplitter);
|
||||
int Size = TargetAreaSize / 2;
|
||||
NewSplitter->setSizes({Size, Size});
|
||||
TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter);
|
||||
}
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
}
|
||||
TargetAreaSplitter->setSizes(Sizes);
|
||||
|
||||
addDockAreasToList({NewDockArea});
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter )
|
||||
{
|
||||
if (!DockManager->centralWidget() || !splitter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < splitter->count(); ++i)
|
||||
{
|
||||
splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget* widget)
|
||||
{
|
||||
if (!DockManager->centralWidget())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Area = qobject_cast<CDockAreaWidget*>(widget);
|
||||
if(Area)
|
||||
{
|
||||
return Area->isCentralWidgetArea();
|
||||
}
|
||||
|
||||
auto innerSplitter = qobject_cast<CDockSplitter*>(widget);
|
||||
if (innerSplitter)
|
||||
{
|
||||
return innerSplitter->isResizingWithContainer();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea area)
|
||||
{
|
||||
@@ -834,15 +917,15 @@ bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s,
|
||||
{
|
||||
QWidget* ChildNode = nullptr;
|
||||
bool Result = true;
|
||||
if (s.name() == "Splitter")
|
||||
if (s.name() == QLatin1String("Splitter"))
|
||||
{
|
||||
Result = restoreSplitter(s, ChildNode, Testing);
|
||||
}
|
||||
else if (s.name() == "Area")
|
||||
else if (s.name() == QLatin1String("Area"))
|
||||
{
|
||||
Result = restoreDockArea(s, ChildNode, Testing);
|
||||
}
|
||||
else if (s.name() == "Sizes")
|
||||
else if (s.name() == QLatin1String("Sizes"))
|
||||
{
|
||||
QString sSizes = s.readElementText().trimmed();
|
||||
ADS_PRINT("Sizes: " << sSizes);
|
||||
@@ -874,6 +957,10 @@ bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s,
|
||||
Splitter->addWidget(ChildNode);
|
||||
Visible |= ChildNode->isVisibleTo(Splitter);
|
||||
}
|
||||
if(!Testing)
|
||||
{
|
||||
updateSplitterHandles(Splitter);
|
||||
}
|
||||
|
||||
if (Sizes.count() != WidgetCount)
|
||||
{
|
||||
@@ -924,11 +1011,22 @@ bool DockContainerWidgetPrivate::restoreDockArea(CDockingStateReader& s,
|
||||
if (!Testing)
|
||||
{
|
||||
DockArea = new CDockAreaWidget(DockManager, _this);
|
||||
const auto AllowedAreasAttribute = s.attributes().value("AllowedAreas");
|
||||
if (!AllowedAreasAttribute.isEmpty())
|
||||
{
|
||||
DockArea->setAllowedAreas((DockWidgetArea)AllowedAreasAttribute.toInt(nullptr, 16));
|
||||
}
|
||||
|
||||
const auto FlagsAttribute = s.attributes().value("Flags");
|
||||
if (!FlagsAttribute.isEmpty())
|
||||
{
|
||||
DockArea->setDockAreaFlags((CDockAreaWidget::DockAreaFlags)FlagsAttribute.toInt(nullptr, 16));
|
||||
}
|
||||
}
|
||||
|
||||
while (s.readNextStartElement())
|
||||
{
|
||||
if (s.name() != "Widget")
|
||||
if (s.name() != QLatin1String("Widget"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -991,12 +1089,12 @@ bool DockContainerWidgetPrivate::restoreChildNodes(CDockingStateReader& s,
|
||||
bool Result = true;
|
||||
while (s.readNextStartElement())
|
||||
{
|
||||
if (s.name() == "Splitter")
|
||||
if (s.name() == QLatin1String("Splitter"))
|
||||
{
|
||||
Result = restoreSplitter(s, CreatedWidget, Testing);
|
||||
ADS_PRINT("Splitter");
|
||||
}
|
||||
else if (s.name() == "Area")
|
||||
else if (s.name() == QLatin1String("Area"))
|
||||
{
|
||||
Result = restoreDockArea(s, CreatedWidget, Testing);
|
||||
ADS_PRINT("DockAreaWidget");
|
||||
@@ -1013,7 +1111,7 @@ bool DockContainerWidgetPrivate::restoreChildNodes(CDockingStateReader& s,
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoContainer(DockWidgetArea area,
|
||||
CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToContainer(DockWidgetArea area,
|
||||
CDockWidget* Dockwidget)
|
||||
{
|
||||
CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this);
|
||||
@@ -1040,7 +1138,8 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW
|
||||
if (Splitter->orientation() == InsertParam.orientation())
|
||||
{
|
||||
insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append());
|
||||
if (Splitter->isHidden())
|
||||
updateSplitterHandles(Splitter);
|
||||
if (Splitter->isHidden())
|
||||
{
|
||||
Splitter->show();
|
||||
}
|
||||
@@ -1053,14 +1152,16 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW
|
||||
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
|
||||
NewSplitter->addWidget(Splitter);
|
||||
NewSplitter->addWidget(NewDockArea);
|
||||
delete li;
|
||||
updateSplitterHandles(NewSplitter);
|
||||
delete li;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewSplitter->addWidget(NewDockArea);
|
||||
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
|
||||
NewSplitter->addWidget(Splitter);
|
||||
delete li;
|
||||
updateSplitterHandles(NewSplitter);
|
||||
delete li;
|
||||
}
|
||||
RootSplitter = NewSplitter;
|
||||
}
|
||||
@@ -1106,7 +1207,7 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
|
||||
std::cout << (const char*)buf
|
||||
<< (DockArea->isHidden() ? " " : "v")
|
||||
<< (DockArea->openDockWidgetsCount() > 0 ? " " : "c")
|
||||
<< " DockArea" << std::endl;
|
||||
<< " DockArea " << "[hs: " << DockArea->sizePolicy().horizontalStretch() << ", vs: " << DockArea->sizePolicy().verticalStretch() << "]" << std::endl;
|
||||
buf.fill(' ', (level + 1) * 4);
|
||||
for (int i = 0; i < DockArea->dockWidgetsCount(); ++i)
|
||||
{
|
||||
@@ -1126,7 +1227,7 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetArea area,
|
||||
CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetArea area,
|
||||
CDockWidget* Dockwidget, CDockAreaWidget* TargetDockArea)
|
||||
{
|
||||
if (CenterDockWidgetArea == area)
|
||||
@@ -1144,20 +1245,34 @@ CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetAr
|
||||
int index = TargetAreaSplitter ->indexOf(TargetDockArea);
|
||||
if (TargetAreaSplitter->orientation() == InsertParam.orientation())
|
||||
{
|
||||
ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()");
|
||||
ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()");
|
||||
TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea);
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
// do nothing, if flag is not enabled
|
||||
if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion))
|
||||
{
|
||||
adjustSplitterSizesOnInsertion(TargetAreaSplitter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()");
|
||||
ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()");
|
||||
auto TargetAreaSizes = TargetAreaSplitter->sizes();
|
||||
QSplitter* NewSplitter = newSplitter(InsertParam.orientation());
|
||||
NewSplitter->addWidget(TargetDockArea);
|
||||
|
||||
insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
|
||||
TargetAreaSplitter->insertWidget(index, NewSplitter);
|
||||
updateSplitterHandles(NewSplitter);
|
||||
TargetAreaSplitter->insertWidget(index, NewSplitter);
|
||||
updateSplitterHandles(TargetAreaSplitter);
|
||||
if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion))
|
||||
{
|
||||
TargetAreaSplitter->setSizes(TargetAreaSizes);
|
||||
adjustSplitterSizesOnInsertion(NewSplitter);
|
||||
}
|
||||
}
|
||||
|
||||
appendDockAreas({NewDockArea});
|
||||
emitDockAreasAdded();
|
||||
addDockAreasToList({NewDockArea});
|
||||
return NewDockArea;
|
||||
}
|
||||
|
||||
@@ -1211,11 +1326,11 @@ CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockW
|
||||
Dockwidget->setDockManager(d->DockManager);
|
||||
if (DockAreaWidget)
|
||||
{
|
||||
return d->dockWidgetIntoDockArea(area, Dockwidget, DockAreaWidget);
|
||||
return d->addDockWidgetToDockArea(area, Dockwidget, DockAreaWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
return d->dockWidgetIntoContainer(area, Dockwidget);
|
||||
return d->addDockWidgetToContainer(area, Dockwidget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1288,9 +1403,9 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
|
||||
internal::hideEmptyParentSplitters(Splitter);
|
||||
|
||||
// Remove this area from cached areas
|
||||
const auto& cache = d->LastAddedAreaCache;
|
||||
if (auto p = std::find(cache, cache+sizeof(cache)/sizeof(cache[0]), area)) {
|
||||
d->LastAddedAreaCache[std::distance(cache, p)] = nullptr;
|
||||
auto p = std::find(std::begin(d->LastAddedAreaCache), std::end(d->LastAddedAreaCache), area);
|
||||
if (p != std::end(d->LastAddedAreaCache)) {
|
||||
*p = nullptr;
|
||||
}
|
||||
|
||||
// If splitter has more than 1 widgets, we are finished and can leave
|
||||
@@ -1339,9 +1454,11 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
|
||||
}
|
||||
|
||||
delete Splitter;
|
||||
Splitter = nullptr;
|
||||
|
||||
emitAndExit:
|
||||
CDockWidget* TopLevelWidget = topLevelDockWidget();
|
||||
updateSplitterHandles(Splitter);
|
||||
CDockWidget* TopLevelWidget = topLevelDockWidget();
|
||||
|
||||
// Updated the title bar visibility of the dock widget if there is only
|
||||
// one single visible dock widget
|
||||
@@ -1448,8 +1565,9 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
|
||||
}
|
||||
|
||||
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
|
||||
// drop a top level widget that changes from floating to docked now
|
||||
@@ -1459,6 +1577,13 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
|
||||
// level widget anymore
|
||||
CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false);
|
||||
}
|
||||
|
||||
window()->activateWindow();
|
||||
if (SingleDroppedDockWidget)
|
||||
{
|
||||
d->DockManager->notifyWidgetOrAreaRelocation(SingleDroppedDockWidget);
|
||||
}
|
||||
d->DockManager->notifyFloatingWidgetDrop(FloatingWidget);
|
||||
}
|
||||
|
||||
|
||||
@@ -1478,6 +1603,9 @@ void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea,
|
||||
// If there was a top level widget before the drop, then it is not top
|
||||
// level widget anymore
|
||||
CDockWidget::emitTopLevelEventForWidget(SingleDockWidget, false);
|
||||
|
||||
window()->activateWindow();
|
||||
d->DockManager->notifyWidgetOrAreaRelocation(Widget);
|
||||
}
|
||||
|
||||
|
||||
@@ -1537,7 +1665,7 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing)
|
||||
if (IsFloating)
|
||||
{
|
||||
ADS_PRINT("Restore floating widget");
|
||||
if (!s.readNextStartElement() || s.name() != "Geometry")
|
||||
if (!s.readNextStartElement() || s.name() != QLatin1String("Geometry"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1680,6 +1808,13 @@ QList<CDockWidget*> CDockContainerWidget::dockWidgets() const
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockContainerWidget::updateSplitterHandles(QSplitter* splitter)
|
||||
{
|
||||
d->updateSplitterHandles(splitter);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const
|
||||
{
|
||||
|
||||
@@ -155,6 +155,12 @@ protected:
|
||||
*/
|
||||
QList<CDockWidget*> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* This function forces the dock container widget to update handles of splitters
|
||||
* based on resize modes of dock widgets contained in the container.
|
||||
*/
|
||||
void updateSplitterHandles(QSplitter* splitter);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default Constructor
|
||||
@@ -193,7 +199,7 @@ public:
|
||||
bool isInFrontOf(CDockContainerWidget* Other) const;
|
||||
|
||||
/**
|
||||
* Returns the dock area at teh given global position or 0 if there is no
|
||||
* Returns the dock area at the given global position or 0 if there is no
|
||||
* dock area at this position
|
||||
*/
|
||||
CDockAreaWidget* dockAreaAt(const QPoint& GlobalPos) const;
|
||||
@@ -259,7 +265,7 @@ public:
|
||||
*/
|
||||
void closeOtherAreas(CDockAreaWidget* KeepOpenArea);
|
||||
|
||||
signals:
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted if one or multiple dock areas has been added to
|
||||
* the internal list of dock areas.
|
||||
@@ -276,7 +282,7 @@ signals:
|
||||
* This signal is emitted if a dock area is opened or closed via
|
||||
* toggleView() function
|
||||
*/
|
||||
void dockAreaViewToggled(CDockAreaWidget* DockArea, bool Open);
|
||||
void dockAreaViewToggled(ads::CDockAreaWidget* DockArea, bool Open);
|
||||
}; // class DockContainerWidget
|
||||
} // namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
413
src/DockFocusController.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
//============================================================================
|
||||
/// \file DockFocusController.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 05.06.2020
|
||||
/// \brief Implementation of CDockFocusController class
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include "DockFocusController.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QApplication>
|
||||
#include <QAbstractButton>
|
||||
#include <QWindow>
|
||||
|
||||
#include "DockWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockWidgetTab.h"
|
||||
#include "DockContainerWidget.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockManager.h"
|
||||
#include "DockAreaTitleBar.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "linux/FloatingWidgetTitleBar.h"
|
||||
#endif
|
||||
|
||||
namespace ads
|
||||
{
|
||||
/**
|
||||
* Private data class of CDockFocusController class (pimpl)
|
||||
*/
|
||||
struct DockFocusControllerPrivate
|
||||
{
|
||||
CDockFocusController *_this;
|
||||
QPointer<CDockWidget> FocusedDockWidget = nullptr;
|
||||
QPointer<CDockAreaWidget> FocusedArea = nullptr;
|
||||
CDockWidget* OldFocusedDockWidget = nullptr;
|
||||
#ifdef Q_OS_LINUX
|
||||
QPointer<CFloatingDockContainer> FloatingWidget = nullptr;
|
||||
#endif
|
||||
CDockManager* DockManager;
|
||||
bool ForceFocusChangedSignal = false;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockFocusControllerPrivate(CDockFocusController *_public);
|
||||
|
||||
/**
|
||||
* This function updates the focus style of the given dock widget and
|
||||
* the dock area that it belongs to
|
||||
*/
|
||||
void updateDockWidgetFocus(CDockWidget* DockWidget);
|
||||
};
|
||||
// struct DockFocusControllerPrivate
|
||||
|
||||
|
||||
//===========================================================================
|
||||
static void updateDockWidgetFocusStyle(CDockWidget* DockWidget, bool Focused)
|
||||
{
|
||||
DockWidget->setProperty("focused", Focused);
|
||||
DockWidget->tabWidget()->setProperty("focused", Focused);
|
||||
DockWidget->tabWidget()->updateStyle();
|
||||
internal::repolishStyle(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
static void updateDockAreaFocusStyle(CDockAreaWidget* DockArea, bool Focused)
|
||||
{
|
||||
DockArea->setProperty("focused", Focused);
|
||||
internal::repolishStyle(DockArea);
|
||||
internal::repolishStyle(DockArea->titleBar());
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
#ifdef Q_OS_LINUX
|
||||
static void updateFloatingWidgetFocusStyle(CFloatingDockContainer* FloatingWidget, bool Focused)
|
||||
{
|
||||
if (FloatingWidget->hasNativeTitleBar())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto TitleBar = qobject_cast<CFloatingWidgetTitleBar*>(FloatingWidget->titleBarWidget());
|
||||
if (!TitleBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TitleBar->setProperty("focused", Focused);
|
||||
TitleBar->updateStyle();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//============================================================================
|
||||
DockFocusControllerPrivate::DockFocusControllerPrivate(
|
||||
CDockFocusController *_public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
|
||||
{
|
||||
if (!DockWidget->features().testFlag(CDockWidget::DockWidgetFocusable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QWindow* Window = nullptr;
|
||||
auto DockContainer = DockWidget->dockContainer();
|
||||
if (DockContainer)
|
||||
{
|
||||
Window = DockContainer->window()->windowHandle();
|
||||
}
|
||||
|
||||
if (Window)
|
||||
{
|
||||
Window->setProperty("FocusedDockWidget", QVariant::fromValue<CDockWidget*>(DockWidget));
|
||||
}
|
||||
CDockAreaWidget* NewFocusedDockArea = nullptr;
|
||||
if (FocusedDockWidget)
|
||||
{
|
||||
updateDockWidgetFocusStyle(FocusedDockWidget, false);
|
||||
}
|
||||
|
||||
CDockWidget* old = FocusedDockWidget;
|
||||
FocusedDockWidget = DockWidget;
|
||||
updateDockWidgetFocusStyle(FocusedDockWidget, true);
|
||||
NewFocusedDockArea = FocusedDockWidget->dockAreaWidget();
|
||||
if (NewFocusedDockArea && (FocusedArea != NewFocusedDockArea))
|
||||
{
|
||||
if (FocusedArea)
|
||||
{
|
||||
QObject::disconnect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool)));
|
||||
updateDockAreaFocusStyle(FocusedArea, false);
|
||||
}
|
||||
|
||||
FocusedArea = NewFocusedDockArea;
|
||||
updateDockAreaFocusStyle(FocusedArea, true);
|
||||
QObject::connect(FocusedArea, SIGNAL(viewToggled(bool)), _this, SLOT(onFocusedDockAreaViewToggled(bool)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
CFloatingDockContainer* NewFloatingWidget = nullptr;
|
||||
DockContainer = FocusedDockWidget->dockContainer();
|
||||
if (DockContainer)
|
||||
{
|
||||
NewFloatingWidget = DockContainer->floatingWidget();
|
||||
}
|
||||
|
||||
if (NewFloatingWidget)
|
||||
{
|
||||
NewFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(DockWidget));
|
||||
}
|
||||
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// This code is required for styling the floating widget titlebar for linux
|
||||
// depending on the current focus state
|
||||
if (FloatingWidget != NewFloatingWidget)
|
||||
{
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, false);
|
||||
}
|
||||
FloatingWidget = NewFloatingWidget;
|
||||
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (old == DockWidget && !ForceFocusChangedSignal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ForceFocusChangedSignal = false;
|
||||
if (DockWidget->isVisible())
|
||||
{
|
||||
Q_EMIT DockManager->focusedDockWidgetChanged(old, DockWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
OldFocusedDockWidget = old;
|
||||
QObject::connect(DockWidget, SIGNAL(visibilityChanged(bool)), _this, SLOT(onDockWidgetVisibilityChanged(bool)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockFocusController::onDockWidgetVisibilityChanged(bool Visible)
|
||||
{
|
||||
auto Sender = sender();
|
||||
auto DockWidget = qobject_cast<ads::CDockWidget*>(Sender);
|
||||
disconnect(Sender, SIGNAL(visibilityChanged(bool)), this, SLOT(onDockWidgetVisibilityChanged(bool)));
|
||||
if (DockWidget && Visible)
|
||||
{
|
||||
Q_EMIT d->DockManager->focusedDockWidgetChanged(d->OldFocusedDockWidget, DockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockFocusController::CDockFocusController(CDockManager* DockManager) :
|
||||
Super(DockManager),
|
||||
d(new DockFocusControllerPrivate(this))
|
||||
{
|
||||
d->DockManager = DockManager;
|
||||
connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)),
|
||||
this, SLOT(onApplicationFocusChanged(QWidget*, QWidget*)));
|
||||
connect(QApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)),
|
||||
this, SLOT(onFocusWindowChanged(QWindow*)));
|
||||
connect(d->DockManager, SIGNAL(stateRestored()), SLOT(onStateRestored()));
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
CDockFocusController::~CDockFocusController()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockFocusController::onFocusWindowChanged(QWindow *focusWindow)
|
||||
{
|
||||
if (!focusWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto vDockWidget = focusWindow->property("FocusedDockWidget");
|
||||
if (!vDockWidget.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto DockWidget = vDockWidget.value<CDockWidget*>();
|
||||
if (!DockWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->updateDockWidgetFocus(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow)
|
||||
{
|
||||
Q_UNUSED(focusedOld);
|
||||
|
||||
if (d->DockManager->isRestoringState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ADS_PRINT("CDockFocusController::onApplicationFocusChanged "
|
||||
<< " old: " << focusedOld << " new: " << focusedNow);
|
||||
if (!focusedNow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CDockWidget* DockWidget = qobject_cast<CDockWidget*>(focusedNow);
|
||||
if (!DockWidget)
|
||||
{
|
||||
DockWidget = internal::findParent<CDockWidget*>(focusedNow);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!DockWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!DockWidget || DockWidget->tabWidget()->isHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
d->updateDockWidgetFocus(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::setDockWidgetTabFocused(CDockWidgetTab* Tab)
|
||||
{
|
||||
auto DockWidget = Tab->dockWidget();
|
||||
if (DockWidget)
|
||||
{
|
||||
d->updateDockWidgetFocus(DockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow)
|
||||
{
|
||||
d->updateDockWidgetFocus(focusedNow);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::onFocusedDockAreaViewToggled(bool Open)
|
||||
{
|
||||
if (d->DockManager->isRestoringState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(sender());
|
||||
if (!DockArea || Open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto Container = DockArea->dockContainer();
|
||||
auto OpenedDockAreas = Container->openedDockAreas();
|
||||
if (OpenedDockAreas.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->updateDockWidgetFocus(OpenedDockAreas[0]->currentDockWidget());
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget)
|
||||
{
|
||||
if (d->DockManager->isRestoringState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CDockWidget* DockWidget = qobject_cast<CDockWidget*>(DroppedWidget);
|
||||
if (!DockWidget)
|
||||
{
|
||||
CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(DroppedWidget);
|
||||
if (DockArea)
|
||||
{
|
||||
DockWidget = DockArea->currentDockWidget();
|
||||
}
|
||||
}
|
||||
|
||||
if (!DockWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->ForceFocusChangedSignal = true;
|
||||
CDockManager::setWidgetFocus(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget)
|
||||
{
|
||||
if (!FloatingWidget || d->DockManager->isRestoringState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto vDockWidget = FloatingWidget->property("FocusedDockWidget");
|
||||
if (!vDockWidget.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto DockWidget = vDockWidget.value<CDockWidget*>();
|
||||
if (DockWidget)
|
||||
{
|
||||
DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget);
|
||||
CDockManager::setWidgetFocus(DockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
void CDockFocusController::onStateRestored()
|
||||
{
|
||||
if (d->FocusedDockWidget)
|
||||
{
|
||||
updateDockWidgetFocusStyle(d->FocusedDockWidget, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
CDockWidget* CDockFocusController::focusedDockWidget() const
|
||||
{
|
||||
return d->FocusedDockWidget.data();
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockFocusController.cpp
|
||||
88
src/DockFocusController.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef DockFocusControllerH
|
||||
#define DockFocusControllerH
|
||||
//============================================================================
|
||||
/// \file DockFocusController.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 05.06.2020
|
||||
/// \brief Declaration of CDockFocusController class
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QObject>
|
||||
#include "ads_globals.h"
|
||||
#include "DockManager.h"
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct DockFocusControllerPrivate;
|
||||
class CDockManager;
|
||||
class CFloatingDockContainer;
|
||||
|
||||
/**
|
||||
* Manages focus styling of dock widgets and handling of focus changes
|
||||
*/
|
||||
class ADS_EXPORT CDockFocusController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockFocusControllerPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockFocusControllerPrivate;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onApplicationFocusChanged(QWidget *old, QWidget *now);
|
||||
void onFocusWindowChanged(QWindow *focusWindow);
|
||||
void onFocusedDockAreaViewToggled(bool Open);
|
||||
void onStateRestored();
|
||||
void onDockWidgetVisibilityChanged(bool Visible);
|
||||
|
||||
public:
|
||||
using Super = QObject;
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
CDockFocusController(CDockManager* DockManager);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockFocusController();
|
||||
|
||||
/**
|
||||
* A container needs to call this function if a widget has been dropped
|
||||
* into it
|
||||
*/
|
||||
void notifyWidgetOrAreaRelocation(QWidget* RelocatedWidget);
|
||||
|
||||
/**
|
||||
* This function is called, if a floating widget has been dropped into
|
||||
* an new position.
|
||||
* When this function is called, all dock widgets of the FloatingWidget
|
||||
* are already inserted into its new position
|
||||
*/
|
||||
void notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget);
|
||||
|
||||
/**
|
||||
* Returns the dock widget that has focus style in the ui or a nullptr if
|
||||
* not dock widget is painted focused.
|
||||
*/
|
||||
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:
|
||||
/**
|
||||
* Request a focus change to the given dock widget
|
||||
*/
|
||||
void setDockWidgetFocused(CDockWidget* focusedNow);
|
||||
}; // class DockFocusController
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockFocusControllerH
|
||||
|
||||