mirror of
https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
synced 2024-12-24 07:21:32 +08:00
Merge branch 'githubuser0xFFFF:master' into master
This commit is contained in:
commit
4b27af959b
4
.github/workflows/linux-builds.yml
vendored
4
.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,7 +16,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get install qt5-default
|
||||
sudo apt-get install libqt5x11extras5-dev
|
||||
sudo apt-get install qtbase5-private-dev
|
||||
- name: qmake
|
||||
run: qmake
|
||||
- name: make
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -383,3 +383,4 @@ FodyWeavers.xsd
|
||||
/ build
|
||||
/Settings.ini
|
||||
.vscode/settings.json
|
||||
/.settings
|
||||
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project>
|
||||
<configuration id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795" name="Default">
|
||||
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="(g?cc)|([gc]\+\+)|(clang)" prefer-non-shared="true"/>
|
||||
<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorMinGW" console="false" env-hash="-703253235712566569" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings MinGW" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||
</provider>
|
||||
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||
</extension>
|
||||
</configuration>
|
||||
</project>
|
20
.travis.yml
20
.travis.yml
@ -9,8 +9,6 @@ matrix:
|
||||
os: linux
|
||||
dist: trusty
|
||||
group: stable
|
||||
before_install:
|
||||
- sudo apt-get -y install libqt5x11extras5-dev
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@ -20,8 +18,6 @@ matrix:
|
||||
packages:
|
||||
- qt55base
|
||||
- qt55tools
|
||||
- qt55x11extras
|
||||
- libqt5x11extras5-dev
|
||||
- gcc-9
|
||||
- g++-9
|
||||
script:
|
||||
@ -39,8 +35,6 @@ matrix:
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get -y install libqt5x11extras5-dev
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@ -50,8 +44,6 @@ matrix:
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- qt514x11extras
|
||||
- libqt5x11extras5-dev
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
@ -73,8 +65,6 @@ matrix:
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get -y install libqt5x11extras5-dev
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@ -84,8 +74,6 @@ matrix:
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- qt514x11extras
|
||||
- libqt5x11extras5-dev
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
@ -107,8 +95,6 @@ matrix:
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get -y install libqt5x11extras5-dev
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@ -118,8 +104,6 @@ matrix:
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- qt514x11extras
|
||||
- libqt5x11extras5-dev
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
@ -147,8 +131,6 @@ matrix:
|
||||
services:
|
||||
- xvfb
|
||||
compiler: gcc
|
||||
before_install:
|
||||
- sudo apt-get -y install libqt5x11extras5-dev
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
@ -158,8 +140,6 @@ matrix:
|
||||
packages:
|
||||
- qt514base
|
||||
- qt514tools
|
||||
- qt514x11extras
|
||||
- libqt5x11extras5-dev
|
||||
- gcc-9
|
||||
- g++-9
|
||||
- libc6-i386
|
||||
|
52
README.md
52
README.md
@ -15,6 +15,13 @@ integrated development environments (IDEs) such as Visual Studio.
|
||||
|
||||
## New and Noteworthy
|
||||
|
||||
The [release 3.8](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.8.0)
|
||||
adds the following features:
|
||||
|
||||
- option to close tabs with the middle mouse button
|
||||
- `DeleteContentOnClose` flag for dynamic deletion and creation of dock widget
|
||||
content
|
||||
|
||||
The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.0)
|
||||
adds the following features:
|
||||
|
||||
@ -81,6 +88,8 @@ know it from Visual Studio.
|
||||
- [ezEditor](#ezeditor)
|
||||
- [D-Tect X](#d-tect-x)
|
||||
- [HiveWE](#hivewe)
|
||||
- [Ramses Composer](#ramses-composer)
|
||||
- [Plot Juggler](#plot-juggler)
|
||||
|
||||
### Docking everywhere - no central widget
|
||||
|
||||
@ -219,7 +228,13 @@ 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
|
||||
@ -391,3 +406,38 @@ of the open editor windows.
|
||||
[learn more...](https://github.com/stijnherfst/HiveWE)
|
||||
|
||||
![HiveWE](doc/showcase_hivewe.png)
|
||||
|
||||
### [Ramses Composer](https://github.com/GENIVI/ramses-composer)
|
||||
|
||||
Ramses Composer is the authoring tool for the open source [RAMSES](https://github.com/GENIVI/ramses)
|
||||
rendering ecosystem.
|
||||
|
||||
Ramses is a low-level rendering engine which is optimized for embedded hardware
|
||||
mobile devices, automotive ECUs, IoT electronics. Ramses was initially developed
|
||||
at the BMW Group and open-sourced in 2018 as part of a collaboration initiative
|
||||
with the Genivi Alliance. It is an important part of the BMW infotainment cluster
|
||||
and digital portfolio.
|
||||
|
||||
[learn more...](https://github.com/GENIVI/ramses-composer)
|
||||
|
||||
![RamsesComposer](doc/showcase_ramses_composer.png)
|
||||
|
||||
### [Plot Juggler](https://github.com/facontidavide/PlotJuggler)
|
||||
|
||||
PlotJuggler is a fast, powerful and intuitive tool to visualize time series.
|
||||
It makes it easy to visualize data but also to analyze it. You can manipulate
|
||||
your time series using a simple and extendable Transform Editor. Some of the
|
||||
highlights are:
|
||||
|
||||
- Simple Drag & Drop user interface.
|
||||
- Load data from file.
|
||||
- Connect to live streaming of data.
|
||||
- Save the visualization layout and configurations to re-use them later.
|
||||
- Fast OpenGL visualization.
|
||||
- Can handle thousands of timeseries and millions of data points.
|
||||
- Transform your data using a simple editor: derivative, moving average, integral, etc…
|
||||
- PlotJuggler can be easily extended using plugins.
|
||||
|
||||
[read more...](https://github.com/facontidavide/PlotJuggler)
|
||||
|
||||
[![Plot Juggler](doc/showcase_plot_juggler.png)](https://vimeo.com/480588113#t=46s)
|
29
ads.pri
29
ads.pri
@ -1,28 +1,25 @@
|
||||
|
||||
lessThan(QT_MAJOR_VERSION, 6) {
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
CONFIG(debug, debug|release){
|
||||
win32 {
|
||||
versionAtLeast(QT_VERSION, 5.15.0) {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddockingd
|
||||
}
|
||||
}
|
||||
else{
|
||||
else:mac {
|
||||
LIBS += -lqtadvanceddocking_debug
|
||||
}
|
||||
else {
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
}
|
||||
else {
|
||||
# qt$$qtLibraryTarget(qtadvanceddocking) does not produce an advanceddockingd.dll file on Windows
|
||||
# for Qt6 - I don't know if this is a bug and I have to investigate
|
||||
else{
|
||||
LIBS += -lqtadvanceddocking
|
||||
}
|
||||
|
||||
|
||||
unix:!macx {
|
||||
LIBS += -lxcb
|
||||
QT += x11extras
|
||||
}
|
||||
|
@ -2,7 +2,4 @@ include(CMakeFindDependencyMacro)
|
||||
find_dependency(Qt5Core ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_dependency(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
find_dependency(Qt5Widgets ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_dependency(Qt5X11Extras ${REQUIRED_QT_VERSION} REQUIRED)
|
||||
endif()
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/adsTargets.cmake")
|
@ -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
|
||||
|
@ -79,7 +79,7 @@
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
#include "StatusDialog.h"
|
||||
|
||||
#include "DockSplitter.h"
|
||||
|
||||
|
||||
/**
|
||||
@ -417,6 +417,22 @@ void MainWindowPrivate::createContent()
|
||||
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
|
||||
@ -443,13 +459,14 @@ void MainWindowPrivate::createContent()
|
||||
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 (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking))
|
||||
@ -613,6 +630,9 @@ CMainWindow::CMainWindow(QWidget *parent) :
|
||||
// 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);
|
||||
|
@ -215,6 +215,17 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
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
|
BIN
doc/cfg_flag_MiddleMouseButtonClosesTab.gif
Normal file
BIN
doc/cfg_flag_MiddleMouseButtonClosesTab.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 257 KiB |
BIN
doc/showcase_plot_juggler.png
Normal file
BIN
doc/showcase_plot_juggler.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 264 KiB |
BIN
doc/showcase_ramses_composer.png
Normal file
BIN
doc/showcase_ramses_composer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 450 KiB |
@ -27,6 +27,17 @@
|
||||
- [`EqualSplitOnInsertion`](#equalsplitoninsertion)
|
||||
- [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only)
|
||||
- [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only)
|
||||
- [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab)
|
||||
- [DockWidget Feature Flags](#dockwidget-feature-flags)
|
||||
- [`DockWidgetClosable`](#dockwidgetclosable)
|
||||
- [`DockWidgetMovable`](#dockwidgetmovable)
|
||||
- [`DockWidgetFloatable`](#dockwidgetfloatable)
|
||||
- [`DockWidgetDeleteOnClose`](#dockwidgetdeleteonclose)
|
||||
- [`CustomCloseHandling`](#customclosehandling)
|
||||
- [`DockWidgetFocusable`](#dockwidgetfocusable)
|
||||
- [`DockWidgetForceCloseWithArea`](#dockwidgetforceclosewitharea)
|
||||
- [`NoTab`](#notab)
|
||||
- [`DeleteContentOnClose`](#deletecontentonclose)
|
||||
- [Central Widget](#central-widget)
|
||||
- [Empty Dock Area](#empty-dock-area)
|
||||
- [Custom Close Handling](#custom-close-handling)
|
||||
@ -464,6 +475,69 @@ If you would like to overwrite autodetection, then you can activate this flag
|
||||
to force QWidget based title bars. You can overwrite autodetection and this
|
||||
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
|
||||
|
||||
### `MiddleMouseButtonClosesTab`
|
||||
|
||||
If the flag is set, the user can use the mouse middle button to close the tab
|
||||
under the mouse. So you do not need to exactly hit the tab close button to
|
||||
close tab. Just click with the middle mouse button on a tab like this is
|
||||
possible in various web browsers.
|
||||
|
||||
![MiddleMouseButtonClosesTab true](cfg_flag_MiddleMouseButtonClosesTab.gif)
|
||||
|
||||
## DockWidget Feature Flags
|
||||
|
||||
### `DockWidgetClosable`
|
||||
|
||||
If set, the dock widget will have a close button.
|
||||
|
||||
### `DockWidgetMovable`
|
||||
|
||||
If a dock widget is movable, then it and can be moved to a new position in the
|
||||
current dock container. Disable this flag to prevent moving of a dock widget
|
||||
via mouse. If the `OpaqueUndocking` configuration flag is set, then dock widgets
|
||||
are immediately undocked into floating widgets. That means, moving is only
|
||||
possible in this case, if the dock widget is also floatable (feature flag
|
||||
`DockWidgetFloatable` is set).
|
||||
|
||||
### `DockWidgetFloatable`
|
||||
|
||||
If set, a dock widget can be dragged into a floating window.
|
||||
|
||||
### `DockWidgetDeleteOnClose`
|
||||
|
||||
Deletes the dock widget and its content when it is closed.
|
||||
|
||||
### `CustomCloseHandling`
|
||||
|
||||
Clicking the close button will not close the dock widget but emits the
|
||||
`closeRequested()` signal instead. This allows the application to implement
|
||||
a custom close handling.
|
||||
|
||||
### `DockWidgetFocusable`
|
||||
|
||||
If this is enabled, a dock widget can get focus highlighting.
|
||||
|
||||
### `DockWidgetForceCloseWithArea`
|
||||
|
||||
A dock widget will be closed when the dock area hosting it is closed. If the
|
||||
`DockWidgetDeleteOnClose` feature is enabled for a dock widget, then it will
|
||||
be deleted, if the user clicks the close button of this dock widget. If the
|
||||
user clicks the close button of the dock area that contains this widget,
|
||||
then only the visibility of the dock widget is toggled. If this feature flag
|
||||
is set, the closing the dock area also closes the dock widget. That means, if
|
||||
the dock widget feature `DockWidgetDeleteOnClose` is set for the dock widgets
|
||||
in a dock area, then all dock widgets will be deleted if the dock area is closed.
|
||||
|
||||
### `NoTab`
|
||||
|
||||
A dock widget tab will never be shown if this flag is set.
|
||||
|
||||
### `DeleteContentOnClose`
|
||||
|
||||
Deletes only the contained widget on close, keeping the dock widget intact and
|
||||
in place. Attempts to rebuild the contents widget on show if there is a widget
|
||||
factory set. See [issue #365](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/pull/365) for more details.
|
||||
|
||||
## Central Widget
|
||||
|
||||
The Advanced Docking System has been developed to overcome the limitations of
|
||||
|
@ -4,4 +4,5 @@ add_subdirectory(simple)
|
||||
add_subdirectory(sidebar)
|
||||
add_subdirectory(deleteonclose)
|
||||
add_subdirectory(centralwidget)
|
||||
add_subdirectory(emptydockarea)
|
||||
add_subdirectory(emptydockarea)
|
||||
add_subdirectory(dockindock)
|
@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
add_executable(CentralWidgetExample WIN32
|
||||
main.cpp
|
||||
@ -9,7 +10,9 @@ add_executable(CentralWidgetExample WIN32
|
||||
)
|
||||
target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking)
|
||||
target_link_libraries(CentralWidgetExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set_target_properties(CentralWidgetExample PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
|
@ -12,19 +12,7 @@ from PyQtAds import QtAds
|
||||
|
||||
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||
|
||||
import demo_rc # pyrcc5 demo\demo.qrc -o examples\centralWidget\demo_rc.py
|
||||
|
||||
|
||||
def svg_icon(filename: str):
|
||||
'''Helper function to create an SVG icon'''
|
||||
# This is a workaround, because because in item views SVG icons are not
|
||||
# properly scaled and look blurry or pixelate
|
||||
icon = QIcon(filename)
|
||||
icon.addPixmap(icon.pixmap(92))
|
||||
return icon
|
||||
|
||||
|
||||
class MainWindow(MainWindowUI, MainWindowBase):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@ -46,27 +34,26 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
||||
|
||||
# create other dock widgets
|
||||
file_tree = QTreeView()
|
||||
file_tree.setFrameShape(QFrame.NoFrame)
|
||||
file_model = QFileSystemModel(file_tree)
|
||||
file_model.setRootPath(QDir.currentPath())
|
||||
file_tree.setModel(file_model)
|
||||
data_dock_widget = QtAds.CDockWidget("File system")
|
||||
data_dock_widget.setWidget(file_tree)
|
||||
data_dock_widget.resize(150, 250)
|
||||
data_dock_widget.setMinimumSize(100, 250)
|
||||
file_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, data_dock_widget, central_dock_area)
|
||||
self.menuView.addAction(data_dock_widget.toggleViewAction())
|
||||
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(3)
|
||||
table.setRowCount(10)
|
||||
table_dock_widget = QtAds.CDockWidget("Table")
|
||||
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.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, file_area)
|
||||
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
|
||||
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||
|
||||
table = QTableWidget()
|
||||
table.setColumnCount(5)
|
||||
table.setRowCount(1020)
|
||||
table_dock_widget = QtAds.CDockWidget("Table 2")
|
||||
table_dock_widget.setWidget(table)
|
||||
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||
table_dock_widget.resize(250, 150)
|
||||
table_dock_widget.setMinimumSize(200, 150)
|
||||
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
|
||||
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||
|
||||
properties_table = QTableWidget()
|
||||
@ -76,7 +63,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
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)
|
||||
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())
|
||||
|
||||
@ -84,7 +71,6 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
||||
|
||||
def create_perspective_ui(self):
|
||||
save_perspective_action = QAction("Create Perspective", self)
|
||||
save_perspective_action.setIcon(svg_icon(":/adsdemo/images/picture_in_picture.svg"))
|
||||
save_perspective_action.triggered.connect(self.save_perspective)
|
||||
perspective_list_action = QWidgetAction(self)
|
||||
self.perspective_combobox = QComboBox(self)
|
@ -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
|
||||
|
@ -41,19 +41,39 @@ int main(int argc, char *argv[])
|
||||
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, [&]() {
|
||||
|
@ -1,8 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_simple VERSION ${VERSION_SHORT})
|
||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
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(DockInDock WIN32
|
||||
add_executable(DockInDockExample WIN32
|
||||
dockindock.cpp
|
||||
dockindockmanager.cpp
|
||||
perspectiveactions.cpp
|
||||
@ -10,10 +11,12 @@ add_executable(DockInDock WIN32
|
||||
main.cpp
|
||||
mainframe.cpp
|
||||
)
|
||||
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)
|
||||
set_target_properties(SimpleExample PROPERTIES
|
||||
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
|
||||
|
203
examples/dockindock/dockindock.py
Normal file
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)")
|
214
examples/dockindock/dockindockmanager.py
Normal file
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
|
72
examples/dockindock/main.py
Normal file
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_()
|
25
examples/dockindock/perspectiveactions.py
Normal file
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)
|
203
examples/dockindock/perspectives.py
Normal file
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
|
@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
add_executable(EmptyDockAreaExample WIN32
|
||||
main.cpp
|
||||
@ -9,7 +10,9 @@ add_executable(EmptyDockAreaExample WIN32
|
||||
)
|
||||
target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||
target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking)
|
||||
target_link_libraries(EmptyDockAreaExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
target_link_libraries(EmptyDockAreaExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
set_target_properties(EmptyDockAreaExample PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTORCC ON
|
||||
|
108
examples/emptydockarea/main.py
Normal file
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_()
|
@ -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
|
||||
|
@ -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
|
||||
|
4
setup.py
4
setup.py
@ -227,9 +227,7 @@ class build_ext(sipdistutils.build_ext):
|
||||
extension.extra_link_args += ['-F' + self.qtconfig.QT_INSTALL_LIBS,
|
||||
'-mmacosx-version-min=10.9']
|
||||
elif sys.platform == 'linux':
|
||||
extension.extra_compile_args += ['-D', 'QT_X11EXTRAS_LIB', '-std=c++11']
|
||||
extension.include_dirs += [os.path.join(self.qt_include_dir, 'QtX11Extras')]
|
||||
extension.libraries += ['Qt5X11Extras' + self.qt_libinfix]
|
||||
extension.extra_compile_args += ['-std=c++11']
|
||||
|
||||
return super().swig_sources(sources, extension)
|
||||
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
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);
|
||||
|
@ -33,8 +33,10 @@ public:
|
||||
CustomCloseHandling,
|
||||
DockWidgetFocusable,
|
||||
DockWidgetForceCloseWithArea,
|
||||
NoTab,
|
||||
DefaultDockWidgetFeatures,
|
||||
AllDockWidgetFeatures,
|
||||
DockWidgetAlwaysCloseAndDelete,
|
||||
NoDockWidgetFeatures
|
||||
};
|
||||
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;
|
||||
|
@ -1,60 +1,138 @@
|
||||
%Import QtWidgets/QtWidgetsmod.sip
|
||||
|
||||
%If (Qt_5_0_0 -)
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
%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
|
||||
|
@ -1,8 +1,9 @@
|
||||
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)
|
||||
find_package(Qt5 5.5 COMPONENTS X11Extras REQUIRED)
|
||||
include_directories(${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
if(BUILD_STATIC)
|
||||
@ -60,12 +61,10 @@ else()
|
||||
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)
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(qtadvanceddocking PUBLIC Qt5::X11Extras)
|
||||
target_link_libraries(qtadvanceddocking PRIVATE xcb)
|
||||
endif()
|
||||
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
|
||||
@ -84,13 +83,13 @@ write_basic_package_version_file(
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
install(FILES ${ads_HEADERS}
|
||||
DESTINATION include
|
||||
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
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "DockAreaTabBar.h"
|
||||
#include "IconProvider.h"
|
||||
#include "DockComponentsFactory.h"
|
||||
#include "DockFocusController.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -471,7 +472,8 @@ void CDockAreaTitleBar::mousePressEvent(QMouseEvent* ev)
|
||||
|
||||
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
{
|
||||
d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason);
|
||||
//d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason);
|
||||
d->dockManager()->dockFocusController()->setDockWidgetTabFocused(d->TabBar->currentTab());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1030,6 +1030,21 @@ void CDockAreaWidget::onDockWidgetFeaturesChanged()
|
||||
}
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
//============================================================================
|
||||
bool CDockAreaWidget::event(QEvent *e)
|
||||
{
|
||||
switch (e->type())
|
||||
{
|
||||
case QEvent::PlatformSurface: return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Super::event(e);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -77,6 +77,17 @@ private Q_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.
|
||||
@ -356,7 +367,8 @@ Q_SIGNALS:
|
||||
*/
|
||||
void viewToggled(bool Open);
|
||||
}; // class DockAreaWidget
|
||||
}
|
||||
// namespace ads
|
||||
} // namespace ads
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockAreaWidget::DockAreaFlags)
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockAreaWidgetH
|
||||
|
@ -1565,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
|
||||
@ -1679,7 +1680,10 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing)
|
||||
if (!Testing)
|
||||
{
|
||||
CFloatingDockContainer* FloatingWidget = floatingWidget();
|
||||
FloatingWidget->restoreGeometry(Geometry);
|
||||
if (FloatingWidget)
|
||||
{
|
||||
FloatingWidget->restoreGeometry(Geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <QPointer>
|
||||
#include <QApplication>
|
||||
#include <QAbstractButton>
|
||||
#include <QWindow>
|
||||
|
||||
#include "DockWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
@ -31,6 +32,8 @@
|
||||
|
||||
namespace ads
|
||||
{
|
||||
static const char* const FocusedDockWidgetProperty = "FocusedDockWidget";
|
||||
|
||||
/**
|
||||
* Private data class of CDockFocusController class (pimpl)
|
||||
*/
|
||||
@ -56,8 +59,8 @@ struct DockFocusControllerPrivate
|
||||
* the dock area that it belongs to
|
||||
*/
|
||||
void updateDockWidgetFocus(CDockWidget* DockWidget);
|
||||
};
|
||||
// struct DockFocusControllerPrivate
|
||||
}; // struct DockFocusControllerPrivate
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
@ -115,6 +118,17 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
|
||||
return;
|
||||
}
|
||||
|
||||
QWindow* Window = nullptr;
|
||||
auto DockContainer = DockWidget->dockContainer();
|
||||
if (DockContainer)
|
||||
{
|
||||
Window = DockContainer->window()->windowHandle();
|
||||
}
|
||||
|
||||
if (Window)
|
||||
{
|
||||
Window->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(DockWidget)));
|
||||
}
|
||||
CDockAreaWidget* NewFocusedDockArea = nullptr;
|
||||
if (FocusedDockWidget)
|
||||
{
|
||||
@ -139,31 +153,36 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget)
|
||||
}
|
||||
|
||||
|
||||
auto NewFloatingWidget = FocusedDockWidget->dockContainer()->floatingWidget();
|
||||
|
||||
CFloatingDockContainer* NewFloatingWidget = nullptr;
|
||||
DockContainer = FocusedDockWidget->dockContainer();
|
||||
if (DockContainer)
|
||||
{
|
||||
NewFloatingWidget = DockContainer->floatingWidget();
|
||||
}
|
||||
|
||||
if (NewFloatingWidget)
|
||||
{
|
||||
NewFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(DockWidget));
|
||||
NewFloatingWidget->setProperty(FocusedDockWidgetProperty, QVariant::fromValue(QPointer<CDockWidget>(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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (FloatingWidget != NewFloatingWidget)
|
||||
{
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, false);
|
||||
}
|
||||
FloatingWidget = NewFloatingWidget;
|
||||
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, false);
|
||||
}
|
||||
FloatingWidget = NewFloatingWidget;
|
||||
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, true);
|
||||
}
|
||||
if (FloatingWidget)
|
||||
{
|
||||
updateFloatingWidgetFocusStyle(FloatingWidget, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (old == DockWidget && !ForceFocusChangedSignal)
|
||||
@ -206,6 +225,8 @@ CDockFocusController::CDockFocusController(CDockManager* DockManager) :
|
||||
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()));
|
||||
}
|
||||
|
||||
@ -216,9 +237,35 @@ CDockFocusController::~CDockFocusController()
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockFocusController::onFocusWindowChanged(QWindow *focusWindow)
|
||||
{
|
||||
if (!focusWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto vDockWidget = focusWindow->property(FocusedDockWidgetProperty);
|
||||
if (!vDockWidget.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
|
||||
if (!DockWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->updateDockWidgetFocus(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidget* focusedNow)
|
||||
{
|
||||
Q_UNUSED(focusedOld);
|
||||
|
||||
if (d->DockManager->isRestoringState())
|
||||
{
|
||||
return;
|
||||
@ -231,47 +278,7 @@ void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidge
|
||||
return;
|
||||
}
|
||||
|
||||
// If the close button in another tab steals the focus from the current
|
||||
// active dock widget content, i.e. if the user clicks its close button,
|
||||
// then we immediately give the focus back to the previous focused widget
|
||||
// focusedOld
|
||||
if (CDockManager::testConfigFlag(CDockManager::AllTabsHaveCloseButton))
|
||||
{
|
||||
auto OtherDockWidgetTab = internal::findParent<CDockWidgetTab*>(focusedNow);
|
||||
if (OtherDockWidgetTab && focusedOld)
|
||||
{
|
||||
auto OldFocusedDockWidget = internal::findParent<CDockWidget*>(focusedOld);
|
||||
if (OldFocusedDockWidget)
|
||||
{
|
||||
focusedOld->setFocus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CDockWidget* DockWidget = nullptr;
|
||||
auto DockWidgetTab = qobject_cast<CDockWidgetTab*>(focusedNow);
|
||||
if (DockWidgetTab)
|
||||
{
|
||||
DockWidget = DockWidgetTab->dockWidget();
|
||||
// If the DockWidgetTab "steals" the focus from a widget in the same
|
||||
// DockWidget, then we immediately give the focus back to the previous
|
||||
// focused widget focusedOld
|
||||
if (focusedOld)
|
||||
{
|
||||
auto OldFocusedDockWidget = internal::findParent<CDockWidget*>(focusedOld);
|
||||
if (OldFocusedDockWidget && OldFocusedDockWidget == DockWidget)
|
||||
{
|
||||
focusedOld->setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!DockWidget)
|
||||
{
|
||||
DockWidget = qobject_cast<CDockWidget*>(focusedNow);
|
||||
}
|
||||
|
||||
CDockWidget* DockWidget = qobject_cast<CDockWidget*>(focusedNow);
|
||||
if (!DockWidget)
|
||||
{
|
||||
DockWidget = internal::findParent<CDockWidget*>(focusedNow);
|
||||
@ -293,6 +300,17 @@ void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidge
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::setDockWidgetTabFocused(CDockWidgetTab* Tab)
|
||||
{
|
||||
auto DockWidget = Tab->dockWidget();
|
||||
if (DockWidget)
|
||||
{
|
||||
d->updateDockWidgetFocus(DockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow)
|
||||
{
|
||||
@ -320,7 +338,7 @@ void CDockFocusController::onFocusedDockAreaViewToggled(bool Open)
|
||||
return;
|
||||
}
|
||||
|
||||
CDockManager::setWidgetFocus(OpenedDockAreas[0]->currentDockWidget()->tabWidget());
|
||||
d->updateDockWidgetFocus(OpenedDockAreas[0]->currentDockWidget());
|
||||
}
|
||||
|
||||
|
||||
@ -348,7 +366,7 @@ void CDockFocusController::notifyWidgetOrAreaRelocation(QWidget* DroppedWidget)
|
||||
}
|
||||
|
||||
d->ForceFocusChangedSignal = true;
|
||||
CDockManager::setWidgetFocus(DockWidget->tabWidget());
|
||||
CDockManager::setWidgetFocus(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
@ -360,18 +378,17 @@ void CDockFocusController::notifyFloatingWidgetDrop(CFloatingDockContainer* Floa
|
||||
return;
|
||||
}
|
||||
|
||||
auto vDockWidget = FloatingWidget->property("FocusedDockWidget");
|
||||
auto vDockWidget = FloatingWidget->property(FocusedDockWidgetProperty);
|
||||
if (!vDockWidget.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto DockWidget = vDockWidget.value<CDockWidget*>();
|
||||
auto DockWidget = vDockWidget.value<QPointer<CDockWidget>>();
|
||||
if (DockWidget)
|
||||
{
|
||||
d->FocusedDockWidget = nullptr;
|
||||
DockWidget->dockAreaWidget()->setCurrentDockWidget(DockWidget);
|
||||
CDockManager::setWidgetFocus(DockWidget->tabWidget());
|
||||
CDockManager::setWidgetFocus(DockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ private:
|
||||
|
||||
private Q_SLOTS:
|
||||
void onApplicationFocusChanged(QWidget *old, QWidget *now);
|
||||
void onFocusWindowChanged(QWindow *focusWindow);
|
||||
void onFocusedDockAreaViewToggled(bool Open);
|
||||
void onStateRestored();
|
||||
void onDockWidgetVisibilityChanged(bool Visible);
|
||||
@ -48,21 +49,6 @@ public:
|
||||
*/
|
||||
virtual ~CDockFocusController();
|
||||
|
||||
/**
|
||||
* Helper function to set focus depending on the configuration of the
|
||||
* FocusStyling flag
|
||||
*/
|
||||
template <class QWidgetPtr>
|
||||
static void setWidgetFocus(QWidgetPtr widget)
|
||||
{
|
||||
if (!CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
widget->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
|
||||
/**
|
||||
* A container needs to call this function if a widget has been dropped
|
||||
* into it
|
||||
@ -83,6 +69,12 @@ public:
|
||||
*/
|
||||
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
|
||||
|
@ -1140,6 +1140,13 @@ void CDockManager::setSplitterSizes(CDockAreaWidget *ContainedArea, const QList<
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
CDockFocusController* CDockManager::dockFocusController() const
|
||||
{
|
||||
return d->FocusController;
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -53,6 +53,7 @@ struct DockWidgetTabPrivate;
|
||||
struct DockAreaWidgetPrivate;
|
||||
class CIconProvider;
|
||||
class CDockComponentsFactory;
|
||||
class CDockFocusController;
|
||||
|
||||
/**
|
||||
* The central dock manager that maintains the complete docking system.
|
||||
@ -134,12 +135,18 @@ protected:
|
||||
*/
|
||||
void notifyFloatingWidgetDrop(CFloatingDockContainer* FloatingWidget);
|
||||
|
||||
|
||||
/**
|
||||
* Show the floating widgets that has been created floating
|
||||
*/
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
|
||||
/**
|
||||
* Acces for the internal dock focus controller.
|
||||
* This function only returns a valid object, if the FocusHighlighting
|
||||
* flag is set.
|
||||
*/
|
||||
CDockFocusController* dockFocusController() const;
|
||||
|
||||
public:
|
||||
using Super = CDockContainerWidget;
|
||||
|
||||
@ -188,6 +195,7 @@ public:
|
||||
FloatingContainerForceQWidgetTitleBar = 0x1000000,//!< Linux only ! Forces all FloatingContainer to use a QWidget based title bar.
|
||||
//!< If neither this nor FloatingContainerForceNativeTitleBar is set (the default) native titlebars are used except on known bad systems.
|
||||
//! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0".
|
||||
MiddleMouseButtonClosesTab = 0x2000000, //! If the flag is set, the user can use the mouse middle button to close the tab under the mouse
|
||||
|
||||
DefaultDockAreaButtons = DockAreaHasCloseButton
|
||||
| DockAreaHasUndockButton
|
||||
@ -609,5 +617,7 @@ Q_SIGNALS:
|
||||
void focusedDockWidgetChanged(ads::CDockWidget* old, ads::CDockWidget* now);
|
||||
}; // class DockManager
|
||||
} // namespace ads
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockManager::ConfigFlags)
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockManagerH
|
||||
|
@ -806,7 +806,7 @@ void CDockOverlayCross::setIconColors(const QString& Colors)
|
||||
{"Arrow", CDockOverlayCross::ArrowColor},
|
||||
{"Shadow", CDockOverlayCross::ShadowColor}};
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
||||
auto SkipEmptyParts = QString::SkipEmptyParts;
|
||||
#else
|
||||
auto SkipEmptyParts = Qt::SkipEmptyParts;
|
||||
|
@ -66,6 +66,12 @@ namespace ads
|
||||
*/
|
||||
struct DockWidgetPrivate
|
||||
{
|
||||
struct WidgetFactory
|
||||
{
|
||||
CDockWidget::FactoryFunc createWidget;
|
||||
CDockWidget::eInsertMode insertMode;
|
||||
};
|
||||
|
||||
CDockWidget* _this = nullptr;
|
||||
QBoxLayout* Layout = nullptr;
|
||||
QWidget* Widget = nullptr;
|
||||
@ -84,7 +90,8 @@ struct DockWidgetPrivate
|
||||
bool IsFloatingTopLevel = false;
|
||||
QList<QAction*> TitleBarActions;
|
||||
CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget;
|
||||
|
||||
WidgetFactory* Factory = nullptr;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
@ -116,6 +123,12 @@ struct DockWidgetPrivate
|
||||
* Setup the main scroll area
|
||||
*/
|
||||
void setupScrollArea();
|
||||
|
||||
/**
|
||||
* Creates the content widget with the registered widget factory and
|
||||
* returns true on success.
|
||||
*/
|
||||
bool createWidgetFromFactory();
|
||||
};
|
||||
// struct DockWidgetPrivate
|
||||
|
||||
@ -130,10 +143,23 @@ DockWidgetPrivate::DockWidgetPrivate(CDockWidget* _public) :
|
||||
//============================================================================
|
||||
void DockWidgetPrivate::showDockWidget()
|
||||
{
|
||||
if (!Widget)
|
||||
{
|
||||
if (!createWidgetFromFactory())
|
||||
{
|
||||
Q_ASSERT(!Features.testFlag(CDockWidget::DeleteContentOnClose)
|
||||
&& "DeleteContentOnClose flag was set, but the widget "
|
||||
"factory is missing or it doesn't return a valid QWidget.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DockArea)
|
||||
{
|
||||
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this);
|
||||
FloatingWidget->resize(_this->size());
|
||||
// We use the size hint of the content widget to provide a good
|
||||
// initial size
|
||||
FloatingWidget->resize(Widget ? Widget->sizeHint() : _this->sizeHint());
|
||||
TabWidget->show();
|
||||
FloatingWidget->show();
|
||||
}
|
||||
@ -165,6 +191,12 @@ void DockWidgetPrivate::hideDockWidget()
|
||||
{
|
||||
TabWidget->hide();
|
||||
updateParentDockArea();
|
||||
|
||||
if (Features.testFlag(CDockWidget::DeleteContentOnClose))
|
||||
{
|
||||
Widget->deleteLater();
|
||||
Widget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -218,6 +250,30 @@ void DockWidgetPrivate::setupScrollArea()
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool DockWidgetPrivate::createWidgetFromFactory()
|
||||
{
|
||||
if (!Features.testFlag(CDockWidget::DeleteContentOnClose))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Factory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget* w = Factory->createWidget(_this);
|
||||
if (!w)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_this->setWidget(w, Factory->insertMode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
|
||||
QFrame(parent),
|
||||
@ -288,6 +344,17 @@ void CDockWidget::setWidget(QWidget* widget, eInsertMode InsertMode)
|
||||
d->Widget->setProperty("dockWidgetContent", true);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setWidgetFactory(FactoryFunc createWidget, eInsertMode insertMode)
|
||||
{
|
||||
if (d->Factory)
|
||||
{
|
||||
delete d->Factory;
|
||||
}
|
||||
|
||||
d->Factory = new DockWidgetPrivate::WidgetFactory { createWidget, insertMode };
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QWidget* CDockWidget::takeWidget()
|
||||
@ -530,7 +597,8 @@ void CDockWidget::toggleViewInternal(bool Open)
|
||||
CDockWidget* TopLevelDockWidgetAfter = DockContainer
|
||||
? DockContainer->topLevelDockWidget() : nullptr;
|
||||
CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetAfter, true);
|
||||
CFloatingDockContainer* FloatingContainer = DockContainer->floatingWidget();
|
||||
CFloatingDockContainer* FloatingContainer = DockContainer
|
||||
? DockContainer->floatingWidget() : nullptr;
|
||||
if (FloatingContainer)
|
||||
{
|
||||
FloatingContainer->updateWindowTitle();
|
||||
|
@ -147,18 +147,19 @@ public:
|
||||
|
||||
enum DockWidgetFeature
|
||||
{
|
||||
DockWidgetClosable = 0x01,///< dock widget has a close button
|
||||
DockWidgetMovable = 0x02,///< dock widget is movable and can be moved to a new position in the current dock container
|
||||
DockWidgetFloatable = 0x04,///< dock widget can be dragged into a floating window
|
||||
DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed
|
||||
CustomCloseHandling = 0x10, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead
|
||||
DockWidgetFocusable = 0x20, ///< if this is enabled, a dock widget can get focus highlighting
|
||||
DockWidgetForceCloseWithArea = 0x40, ///< dock widget will be closed when the dock area hosting it is closed
|
||||
NoTab = 0x80, ///< dock widget tab will never be shown if this flag is set
|
||||
DockWidgetClosable = 0x001,///< dock widget has a close button
|
||||
DockWidgetMovable = 0x002,///< dock widget is movable and can be moved to a new position in the current dock container
|
||||
DockWidgetFloatable = 0x004,///< dock widget can be dragged into a floating window
|
||||
DockWidgetDeleteOnClose = 0x008, ///< deletes the dock widget when it is closed
|
||||
CustomCloseHandling = 0x010, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead
|
||||
DockWidgetFocusable = 0x020, ///< if this is enabled, a dock widget can get focus highlighting
|
||||
DockWidgetForceCloseWithArea = 0x040, ///< dock widget will be closed when the dock area hosting it is closed
|
||||
NoTab = 0x080, ///< dock widget tab will never be shown if this flag is set
|
||||
DeleteContentOnClose = 0x100, ///< deletes only the contained widget on close, keeping the dock widget intact and in place. Attempts to rebuild the contents widget on show if there is a widget factory set.
|
||||
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable,
|
||||
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling,
|
||||
DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose,
|
||||
NoDockWidgetFeatures = 0x00
|
||||
NoDockWidgetFeatures = 0x000
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
|
||||
|
||||
@ -254,7 +255,7 @@ public:
|
||||
/**
|
||||
* Sets the widget for the dock widget to widget.
|
||||
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||
* The content of a dock widget should be resizable do a very small size to
|
||||
* The content of a dock widget should be resizable to a very small size to
|
||||
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||
* dock widget can be resized very well, it is better to insert the content+
|
||||
* widget into a scroll area or to provide a widget that is already a scroll
|
||||
@ -269,7 +270,19 @@ public:
|
||||
* provide the InsertMode ForceNoScrollArea
|
||||
*/
|
||||
void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea);
|
||||
|
||||
|
||||
/**
|
||||
* Only used when the feature flag DeleteContentOnClose is set.
|
||||
* Using the flag and setting a widget factory allows to free the resources
|
||||
* of the widget of your application while retaining the position the next
|
||||
* time you want to show your widget, unlike the flag DockWidgetDeleteOnClose
|
||||
* which deletes the dock widget itself. Since we keep the dock widget, all
|
||||
* regular features of ADS should work as normal, including saving and
|
||||
* restoring the state of the docking system and using perspectives.
|
||||
*/
|
||||
using FactoryFunc = std::function<QWidget*(QWidget*)>;
|
||||
void setWidgetFactory(FactoryFunc createWidget, eInsertMode InsertMode = AutoScrollArea);
|
||||
|
||||
/**
|
||||
* Remove the widget from the dock and give ownership back to the caller
|
||||
*/
|
||||
@ -587,7 +600,8 @@ Q_SIGNALS:
|
||||
*/
|
||||
void featuresChanged(ads::CDockWidget::DockWidgetFeatures features);
|
||||
}; // class DockWidget
|
||||
}
|
||||
// namespace ads
|
||||
} // namespace ads
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::CDockWidget::DockWidgetFeatures)
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockWidgetH
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "DockOverlay.h"
|
||||
#include "DockManager.h"
|
||||
#include "IconProvider.h"
|
||||
#include "DockFocusController.h"
|
||||
|
||||
|
||||
namespace ads
|
||||
@ -207,6 +208,14 @@ struct DockWidgetTabPrivate
|
||||
IconLabel->setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for access to the dock manager dock focus controller
|
||||
*/
|
||||
CDockFocusController* focusController() const
|
||||
{
|
||||
return DockWidget->dockManager()->dockFocusController();
|
||||
}
|
||||
|
||||
};
|
||||
// struct DockWidgetTabPrivate
|
||||
|
||||
@ -234,6 +243,7 @@ void DockWidgetTabPrivate::createLayout()
|
||||
CloseButton->setObjectName("tabCloseButton");
|
||||
internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon);
|
||||
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
CloseButton->setFocusPolicy(Qt::NoFocus);
|
||||
updateCloseButtonSizePolicy();
|
||||
internal::setToolTip(CloseButton, QObject::tr("Close Tab"));
|
||||
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
|
||||
@ -331,10 +341,11 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) :
|
||||
setAttribute(Qt::WA_NoMousePropagation, true);
|
||||
d->DockWidget = DockWidget;
|
||||
d->createLayout();
|
||||
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
/*if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
{
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
@ -353,6 +364,10 @@ void CDockWidgetTab::mousePressEvent(QMouseEvent* ev)
|
||||
ev->accept();
|
||||
d->saveDragStartMousePosition(internal::globalPositionOf(ev));
|
||||
d->DragState = DraggingMousePressed;
|
||||
if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting))
|
||||
{
|
||||
d->focusController()->setDockWidgetTabFocused(this);
|
||||
}
|
||||
Q_EMIT clicked();
|
||||
return;
|
||||
}
|
||||
@ -377,16 +392,30 @@ void CDockWidgetTab::mouseReleaseEvent(QMouseEvent* ev)
|
||||
// End of tab moving, emit signal
|
||||
if (d->DockArea)
|
||||
{
|
||||
ev->accept();
|
||||
Q_EMIT moved(internal::globalPositionOf(ev));
|
||||
}
|
||||
break;
|
||||
|
||||
case DraggingFloatingWidget:
|
||||
ev->accept();
|
||||
d->FloatingWidget->finishDragging();
|
||||
break;
|
||||
|
||||
default:; // do nothing
|
||||
}
|
||||
}
|
||||
else if (ev->button() == Qt::MiddleButton)
|
||||
{
|
||||
if (CDockManager::testConfigFlag(CDockManager::MiddleMouseButtonClosesTab) && d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable))
|
||||
{
|
||||
// Only attempt to close if the mouse is still
|
||||
// on top of the widget, to allow the user to cancel.
|
||||
if (rect().contains(mapFromGlobal(QCursor::pos()))) {
|
||||
ev->accept();
|
||||
Q_EMIT closeRequested();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::mouseReleaseEvent(ev);
|
||||
@ -515,7 +544,8 @@ void CDockWidgetTab::setActiveTab(bool active)
|
||||
bool UpdateFocusStyle = false;
|
||||
if (active && !hasFocus())
|
||||
{
|
||||
setFocus(Qt::OtherFocusReason);
|
||||
//setFocus(Qt::OtherFocusReason);
|
||||
d->focusController()->setDockWidgetTabFocused(this);
|
||||
UpdateFocusStyle = true;
|
||||
}
|
||||
|
||||
@ -612,14 +642,18 @@ QString CDockWidgetTab::text() const
|
||||
//============================================================================
|
||||
void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
// If this is the last dock area in a dock container it does not make
|
||||
// sense to move it to a new floating widget and leave this one
|
||||
// empty
|
||||
if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
|
||||
&& d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
d->saveDragStartMousePosition(internal::globalPositionOf(event));
|
||||
d->startFloating(DraggingInactive);
|
||||
// If this is the last dock area in a dock container it does not make
|
||||
// sense to move it to a new floating widget and leave this one
|
||||
// empty
|
||||
if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
|
||||
&& d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
|
||||
{
|
||||
event->accept();
|
||||
d->saveDragStartMousePosition(internal::globalPositionOf(event));
|
||||
d->startFloating(DraggingInactive);
|
||||
}
|
||||
}
|
||||
|
||||
Super::mouseDoubleClickEvent(event);
|
||||
@ -677,6 +711,9 @@ bool CDockWidgetTab::event(QEvent *e)
|
||||
{
|
||||
const auto text = toolTip();
|
||||
d->TitleLabel->setToolTip(text);
|
||||
if (d->IconLabel) {
|
||||
d->IconLabel->setToolTip(text);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Super::event(e);
|
||||
|
@ -163,8 +163,8 @@ void CElidingLabel::resizeEvent(QResizeEvent *event)
|
||||
//============================================================================
|
||||
QSize CElidingLabel::minimumSizeHint() const
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool HasPixmap = !pixmap().isNull();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
bool HasPixmap = !pixmap(Qt::ReturnByValue).isNull();
|
||||
#else
|
||||
bool HasPixmap = (pixmap() != nullptr);
|
||||
#endif
|
||||
@ -185,8 +185,8 @@ QSize CElidingLabel::minimumSizeHint() const
|
||||
//============================================================================
|
||||
QSize CElidingLabel::sizeHint() const
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool HasPixmap = !pixmap().isNull();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
bool HasPixmap = !pixmap(Qt::ReturnByValue).isNull();
|
||||
#else
|
||||
bool HasPixmap = (pixmap() != nullptr);
|
||||
#endif
|
||||
|
@ -373,6 +373,7 @@ struct FloatingDockContainerPrivate
|
||||
CDockAreaWidget *SingleDockArea = nullptr;
|
||||
QPoint DragStartPos;
|
||||
bool Hiding = false;
|
||||
bool AutoHideChildren = true;
|
||||
#ifdef Q_OS_LINUX
|
||||
QWidget* MouseEventHandler = nullptr;
|
||||
CFloatingWidgetTitleBar* TitleBar = nullptr;
|
||||
@ -841,15 +842,18 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
d->Hiding = true;
|
||||
for (auto DockArea : d->DockContainer->openedDockAreas())
|
||||
if ( d->AutoHideChildren )
|
||||
{
|
||||
for (auto DockWidget : DockArea->openedDockWidgets())
|
||||
d->Hiding = true;
|
||||
for ( auto DockArea : d->DockContainer->openedDockAreas() )
|
||||
{
|
||||
DockWidget->toggleView(false);
|
||||
for ( auto DockWidget : DockArea->openedDockWidgets() )
|
||||
{
|
||||
DockWidget->toggleView( false );
|
||||
}
|
||||
}
|
||||
d->Hiding = false;
|
||||
}
|
||||
d->Hiding = false;
|
||||
}
|
||||
|
||||
|
||||
@ -1035,6 +1039,18 @@ QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const
|
||||
return d->DockContainer->dockWidgets();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::hideAndDeleteLater()
|
||||
{
|
||||
// Widget has been redocked, so it must be hidden right way (see
|
||||
// https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351)
|
||||
// but AutoHideChildren must be set to false because "this" still contains
|
||||
// dock widgets that shall not be toggled hidden.
|
||||
d->AutoHideChildren = false;
|
||||
hide();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::finishDragging()
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ class CDockingStateReader;
|
||||
* This interface is used for opaque and non-opaque undocking. If opaque
|
||||
* undocking is used, the a real CFloatingDockContainer widget will be created
|
||||
*/
|
||||
class IFloatingWidget
|
||||
class ADS_EXPORT IFloatingWidget
|
||||
{
|
||||
public:
|
||||
virtual ~IFloatingWidget() = default;
|
||||
@ -258,6 +258,11 @@ public:
|
||||
*/
|
||||
QList<CDockWidget*> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* This function hides the floating bar instantely and delete it later.
|
||||
*/
|
||||
void hideAndDeleteLater();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
/**
|
||||
* This is a function that responds to FloatingWidgetTitleBar::maximizeRequest()
|
||||
|
@ -39,13 +39,11 @@
|
||||
#include "ads_globals.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <QX11Info>
|
||||
#include <QSettings>
|
||||
#include <QFile>
|
||||
#endif
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
namespace ads
|
||||
{
|
||||
@ -57,10 +55,31 @@ static QString _window_manager;
|
||||
static QHash<QString, xcb_atom_t> _xcb_atom_cache;
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool is_platform_x11()
|
||||
{
|
||||
return QGuiApplication::platformName() == QLatin1String("xcb");
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
xcb_connection_t* x11_connection()
|
||||
{
|
||||
if (!qApp)
|
||||
return nullptr;
|
||||
QPlatformNativeInterface *native = qApp->platformNativeInterface();
|
||||
if (!native)
|
||||
return nullptr;
|
||||
|
||||
void *connection = native->nativeResourceForIntegration(QByteArray("connection"));
|
||||
return reinterpret_cast<xcb_connection_t *>(connection);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
xcb_atom_t xcb_get_atom(const char *name)
|
||||
{
|
||||
if (!QX11Info::isPlatformX11())
|
||||
if (!is_platform_x11())
|
||||
{
|
||||
return XCB_ATOM_NONE;
|
||||
}
|
||||
@ -69,7 +88,7 @@ xcb_atom_t xcb_get_atom(const char *name)
|
||||
{
|
||||
return _xcb_atom_cache[key];
|
||||
}
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_connection_t *connection = x11_connection();
|
||||
xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
|
||||
if (!reply)
|
||||
@ -93,7 +112,7 @@ xcb_atom_t xcb_get_atom(const char *name)
|
||||
//============================================================================
|
||||
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
|
||||
{
|
||||
auto connection = QX11Info::connection();
|
||||
auto connection = x11_connection();
|
||||
xcb_atom_t type_atom = xcb_get_atom(type);
|
||||
xcb_atom_t prop_atom = xcb_get_atom(prop);
|
||||
xcb_client_message_event_t event;
|
||||
@ -118,11 +137,11 @@ void xcb_update_prop(bool set, WId window, const char *type, const char *prop, c
|
||||
//============================================================================
|
||||
xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type)
|
||||
{
|
||||
if (!QX11Info::isPlatformX11())
|
||||
if (!is_platform_x11())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_connection_t *connection = x11_connection();
|
||||
xcb_atom_t type_atom = xcb_get_atom(type);
|
||||
if (type_atom == XCB_ATOM_NONE)
|
||||
{
|
||||
@ -191,7 +210,7 @@ bool xcb_dump_props(WId window, const char *type)
|
||||
QVector<xcb_atom_t> atoms;
|
||||
xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
|
||||
qDebug() << "\n\n!!!" << type << " - " << atoms.length();
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_connection_t *connection = x11_connection();
|
||||
for (auto atom : atoms)
|
||||
{
|
||||
auto foo = xcb_get_atom_name(connection, atom);
|
||||
@ -206,7 +225,7 @@ bool xcb_dump_props(WId window, const char *type)
|
||||
//============================================================================
|
||||
void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
|
||||
{
|
||||
if (!QX11Info::isPlatformX11())
|
||||
if (!is_platform_x11())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -227,7 +246,7 @@ void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
|
||||
{
|
||||
atoms.remove(index);
|
||||
}
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_connection_t *connection = x11_connection();
|
||||
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData());
|
||||
xcb_flush(connection);
|
||||
}
|
||||
@ -238,11 +257,11 @@ QString detectWindowManagerX11()
|
||||
{
|
||||
// Tries to detect the windowmanager via X11.
|
||||
// See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
|
||||
if (!QX11Info::isPlatformX11())
|
||||
if (!is_platform_x11())
|
||||
{
|
||||
return "UNKNOWN";
|
||||
}
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_connection_t *connection = x11_connection();
|
||||
xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
|
||||
if(!first_screen)
|
||||
{
|
||||
|
@ -308,5 +308,6 @@ void repolishStyle(QWidget* w, eRepolishChildOptions Options = RepolishIgnoreChi
|
||||
} // namespace internal
|
||||
} // namespace ads
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ads::DockWidgetAreas)
|
||||
//---------------------------------------------------------------------------
|
||||
#endif // ads_globalsH
|
||||
|
@ -73,8 +73,8 @@ SOURCES += \
|
||||
unix:!macx {
|
||||
HEADERS += linux/FloatingWidgetTitleBar.h
|
||||
SOURCES += linux/FloatingWidgetTitleBar.cpp
|
||||
QT += x11extras
|
||||
LIBS += -lxcb
|
||||
QT += gui-private
|
||||
}
|
||||
|
||||
isEmpty(PREFIX){
|
||||
|
Loading…
Reference in New Issue
Block a user