Compare commits

...

91 Commits

Author SHA1 Message Date
Renato Araujo Oliveira Filho
db9babbdca Fixed module import for python 3.8 2021-09-29 13:36:16 -07:00
Allen Winter
463dd2261e various - spelling fixes 2021-09-28 16:45:44 -04:00
Allen Winter
4dd66969af examples/dockwidgets/MyTitleBar_CSS.h - add multiple include guard 2021-09-28 16:36:55 -04:00
Allen Winter
2c98003219 CMakeLists.txt,python/tests/CMakeLists.txt - minor coding style 2021-09-28 16:28:51 -04:00
Allen Winter
dd853a8f72 python/tests/ - add copyright+license header 2021-09-28 16:24:35 -04:00
Allen Winter
88d5e6b29d Merge pull request #242 from KDAB/python-unittest
Fixed import module for windows and python 3.7
2021-09-28 16:03:40 -04:00
Renato Araujo
71e5d8cc31 Fixed import module for windows and python 3.7
create unit test
2021-09-28 14:45:52 -03:00
Allen Winter
15b3843172 ../KDAB/modules/PySide6ModuleBuild.cmake - set INSTALL_PATH for Mac
on Mac we need to set the rpath to "@loader_path"
2021-09-27 16:42:07 -04:00
Allen Winter
3cffe3b3be ../KDAB/modules/PySide2ModuleBuild.cmake - set INSTALL_PATH for Mac
on Mac we need to set the rpath to "@loader_path"
2021-09-27 16:27:54 -04:00
Allen Winter
9dd87b7777 python/PyKDDockWidgets/CMakeLists.txt - fix TARGET_SONAME_FILE
TARGET_SONAME_FILE isn't supported on Windows
2021-09-27 15:46:23 -04:00
Allen Winter
b057cf905b python/examples-qt6/main.py - uncomment rc_assets
commented by mistake
2021-09-27 12:59:59 -04:00
Allen Winter
d7943d30d8 cmake/KDAB/modules/PySide6ModuleBuild.cmake - add rpath property
follow PySide2ModuleBuild.cmake
2021-09-27 12:59:19 -04:00
Renato Araujo Oliveira Filho
1826d68841 Install library so into python module dir
We do the same a PySide and install the target library into the python
module this. This way the bindings can work without need to export
system paths.
2021-09-25 11:02:18 +01:00
Allen Winter
dc1e5103e2 docs/api/CMakeLists.txt - use INSTALL_DOC_DIR
install api docs files to ${INSTALL_DOC_DIR},
not to ${CMAKE_INSTALL_DOCDIR}
2021-09-20 17:28:48 -04:00
Sergio Martins
33c5647e57 Don't create duplicate central frame with restoring
When using MainWindowOption_HasCentralFrame we have a persistent
central frame which is never destroyed. When restoring a layout
we shouldn't create it, as it exists already.
2021-09-20 12:26:45 +01:00
Waqar Ahmed
f6f772edc5 Fix leaks in kddockwidgets_example 2021-09-17 11:27:09 +01:00
Allen Winter
7e460d8f34 examples/main.py, examples-qt6/main.py - uncomment import
uncomment the import rc_assets (commented by mistake)
2021-09-12 11:24:28 -04:00
Waqar Ahmed
7af2095f2f Allow switching tabs via context menu in the tabs area
This change allows you to switch tabs via the context menu. The context
menu only shows up if you click on the empty area beside the tabs.

Can be tested with `--allow-switch-tabs-via-menu` option in the example.

Fixes #232
Signed-off-by: Waqar Ahmed <waqar.ahmed@kdab.com>
2021-09-11 12:46:23 +01:00
Waqar Ahmed
8307598d5d tests: Fix tests, only repro in some distros 2021-09-11 12:17:50 +01:00
Waqar Ahmed
13fe276dbe Fix possible division by zero 2021-09-11 12:05:00 +01:00
Sergio Martins
13a5a472cd Fix build 2021-09-08 23:17:39 +01:00
Sergio Martins
b5c11ce199 example: Add an example about using stylesheets 2021-09-08 23:14:47 +01:00
Sergio Martins
83c8c22736 Add a TitleBar::isFocused property so we can use with qss
This has nothing to do with QWidget::hasFocus, but regarding our
focus scope support
2021-09-08 22:31:51 +01:00
Sergio Martins
c8ca7bf9e1 Allow TitleBarWidget to honour the stylesheet background color 2021-09-08 20:22:20 +01:00
Milian Wolff
0184deb54d Don't set CMAKE_MODULE/SHARED_LINKER_FLAGS in PARENT_SCOPE 2021-09-08 18:01:47 +02:00
Milian Wolff
389042f3cc Unbreak clang+asan build
Don't set -Wl,--no-undefined in such a configuration as it's not
supported, contrary to gcc+asan.
2021-09-08 16:13:03 +01:00
Sergio Martins
736358a71f cmake: Introduce KDDockWidgets_X11EXTRAS option, default ON
Use -DKDDockWidgets_X11EXTRAS=OFF to disable it, if you don't want
to link to Qt5X11Extras.

I've added a new option instead of relying on auto-detection, because
I want to make it difficult to disable it when on X11, it's opt-out.

X11Extras is needed when window managers don't have compositors, in
that case we use icons without transparency.
2021-09-07 12:19:20 +01:00
Sergio Martins
bffe671679 Fix QtQuick build 2021-09-04 20:11:35 +01:00
Sergio Martins
fafc0b7946 Fix "normal geometry" not being restored
When restoring a maximized window, calling QWidget::showNormal()
on it wouldn't set it's previous non-maximized size.

QWidget::normalGeometry() doesn't have a setter, so use private API.
2021-09-04 19:59:03 +01:00
Sergio Martins
3574feea98 Added isNormalWindowState()
As QWidget doesn't have it.
2021-09-04 13:38:06 +01:00
Sergio Martins
25b48920bb Make FloatingWindow::normalGeometry() virtual
So it can be overridden by QtQuick, since there's no "normal geometry"
for QWindow.

Also useful to workaround window manager bugs in a class derived from FloatingWindow.
2021-09-03 15:33:46 +01:00
Sergio Martins
e22ca48674 Fix QtQuick build 2021-09-03 00:18:42 +01:00
Sergio Martins
3b9773f872 Add support for restoring minimized/maximized floating window state 2021-09-02 18:32:35 +01:00
Sergio Martins
3e3cf10fff Allow to override FloatingWindow maximization/minimization
So users can workaround bugs in window managers / Qt, such as QTBUG-95478

The custom behavior needs to be done in a FloatingWindow sub-class,
as a generic workaround which doesn't break other window managers isn't
possible
2021-09-02 18:08:13 +01:00
Sergio Martins
421b401b4f qtquick: Added QWidgetAdapter::isMinimized() 2021-09-02 17:31:05 +01:00
Sergio Martins
a0c97e3388 Fix layout save restore when using MainWindowOption_HasCentralWidget
Those checks don't make sense anymore
2021-09-02 11:05:57 +01:00
Sergio Martins
d2a3968897 tests: Add tests for MainWindowOption_HasCentralWidget 2021-09-02 10:53:08 +01:00
Sergio Martins
4ea7953a76 Don't allow to overlay central widget 2021-09-02 10:52:56 +01:00
Sergio Martins
0cc0972eea Don't allow to close/float/tab persistent central widget
Fixes corner cases with MainWindowOption_HasCentralWidget
2021-09-02 10:30:34 +01:00
Sergio Martins
77b5d1630d Added DockWidget::isPersistentCentralDockWidget() 2021-09-02 10:24:43 +01:00
Sergio Martins
f962989484 Fix MainWindow::addDockWidgetAsTab with MainWindowOption_HasCentralWidget
When using central widget no tabs are supported
2021-09-02 10:17:37 +01:00
Sergio Martins
1ccdf445eb Introduce MainWindowOption_HasCentralWidget
You can now set an arbitrary widget as "central widget".
It's similar to MainWindowOption_HasCentralFrame, however the widget
won't be detachable and won't show tabs.

Similar to what you'd get with QMainWindow central widget concept.

Example:
    QWidget *myWidget = new MyWidget();
    mainWindow->setPersistentCentralWidget(myWidget);

Fixes #225
2021-08-30 10:12:46 +01:00
Sergio Martins
3fb8861eee Remove "EXPERIMENTAL" comment from MDI option
MDI doesn't have known bugs
2021-08-29 17:43:21 +01:00
Allen Winter
78381badd1 docs/api/Doxyfile.cmake - not generating latex
hard-code LATEX_CMD_NAME to latex since we won't
be generating the latex documentation; else we
have an ununitialized variable for @LATEX_COMPILER@
2021-08-25 15:26:32 -04:00
Allen Winter
8b1a9f2a41 README.md - Add info about Using in Qt6 projects 2021-08-24 09:24:30 -04:00
Sergio Martins
9ee52a0011 Merge branch '1.4' 2021-08-22 16:34:25 +01:00
Allen Winter
97a431f367 docs/api/Doxyfile.cmake - set PAPER_TYPE to a4
a4wide is no longer supported
2021-08-19 12:29:44 -04:00
Allen Winter
3769ad9615 python/PyKDDockWidgets/CMakeLists.txt - link to Python3 dev libs 2021-08-19 11:28:18 -04:00
Allen Winter
8332ab3ddc CMakeLists.txt - add LINKER_FLAGS fo various platforms
set CMAKE_SHARED_LINKER_FLAGS and CMAKE_SHARED_LINKER_FLAGS
- Linker warnings should be treated as errors
- Do not allow undefined symbols, even in non-symbolic shared libraries
2021-08-19 11:23:54 -04:00
Allen Winter
3b4a2f134f docs/api/Doxyfile.cmake - don't use fullpaths to makeindex
@MAKEINDEX@ expands to the fullpath /usr/bin/makeindex
breaks 'make pdf' in build/docs/api/latex
2021-08-19 11:23:34 -04:00
Allen Winter
5899e3716c CMakeLists.txt - Enable hidden visibility by default
"It's just good practice, catching some problems with missing
export macros that would otherwise only affect Windows,
and reducing the number of exported symbols on Unix."
2021-08-16 17:34:08 -04:00
Allen Winter
25bc05ae40 docs/api/CMakeLists.txt - introduce user variable QCH_INSTALL_DIR
provide a user settable variable (as in -DQCH_INSTALL_DIR=/path)
to override the qch file default installation location.
2021-08-16 17:29:48 -04:00
Allen Winter
f42629a6e5 docs/api/CMakeLists.txt - install the doxygen tagsfile 2021-08-16 17:28:58 -04:00
Allen Winter
192cb2daaa buildsystem - a bit of re-organizing the docs/api CMake 2021-08-16 17:28:09 -04:00
Allen Winter
63cc87af2d docs/api/Doxyfile.cmake - update and consistency 2021-08-16 17:25:31 -04:00
Allen Winter
3e1ea3ec67 Changelog - mention Qt6 co-installability 2021-08-16 17:21:26 -04:00
Allen Winter
c27a09c13c Merge branch '1.4' 2021-08-16 13:28:02 -04:00
Albert Astals Cid
6a7230e546 Update qt6-kddockwidgets.spec for new paths 2021-08-12 15:39:43 +02:00
Allen Winter
ca173de71d CMakeLists.txt - fix typo "set(d fault_build_type)" 2021-08-12 07:35:40 -04:00
Allen Winter
5732e8ed14 CMakeLists.txt - minor reorganizing 2021-08-11 19:49:19 -04:00
Albert Astals Cid
5cc7a0864b Fix KDDockWidgetsConfig to require the proper Qt version
And for Qt5X11Extras we only require it if building the Qt5 one
2021-08-11 16:26:10 +02:00
Albert Astals Cid
e4671f175c Install the qt6 cmake targets in prefix lib/cmake/KDDockWidgets-qt6
So it does not collide with the qt5 files
2021-08-11 16:19:05 +02:00
Albert Astals Cid
17fd6b2454 Make Make qt5/qt6 documentation coinstallable 2021-08-11 15:44:19 +02:00
Albert Astals Cid
7ed3945d58 Make qt5/qt6 includes coinstallable
Even if they are "the same file" it's always good to not have packages
trying to install over the same files.

Since we don't want users to have to change their existing
  #include <kddockwidgets/DockWidget.h>

We install the qt6 includes in include/kddockwidgets-qt6/kddockwidgets
and change the cmake target INTERFACE_INCLUDE_DIRECTORIES to be
   "${_IMPORT_PREFIX}/include/kddockwidgets-qt6"
so everything is transparent for the user
2021-08-11 15:22:29 +02:00
Allen Winter
35de0a2fef python/examples-qt6 - PySide6 and PyKDDockWidgetsQt6 version 2021-08-10 08:52:54 -04:00
Allen Winter
8a0e475f21 python/examples - "PySide" => "PySide2" 2021-08-10 08:43:12 -04:00
Allen Winter
63d14f2345 python/CMakeLists.txt - fix typo 2021-08-10 08:35:40 -04:00
Allen Winter
e91849759c python/CMakeLists.txt - use PyKDDockWidgetsQt6 namespace for Qt6
follow PySide6 naming convention
2021-08-10 08:29:50 -04:00
Allen Winter
ee3a3af408 Merge branch '1.4' 2021-08-10 08:21:11 -04:00
Sergio Martins
ffe5d5f6cb Merge branch '1.4' 2021-08-10 10:05:13 +01:00
Allen Winter
d40580c550 buildsystem - rename QT_MAJOR_VERSION to Qt_VERSION_MAJOR
as this is consistent with CMake style and other projects
2021-08-04 16:18:31 -04:00
Allen Winter
8e19b81eb9 cmake/KDAB/modules/KDQtInstallPaths.cmake - sync 2021-08-04 16:17:27 -04:00
Kevin Funk
d5191696e1 cmake: Remove explicit policy version settings
These are NEW by default already for the required CMake version
2021-08-04 07:55:54 +02:00
Allen Winter
1d01466181 Merge branch '1.4' 2021-08-03 07:18:37 -04:00
Allen Winter
aff4d426b8 Merge branch '1.4' 2021-08-02 19:15:00 -04:00
Allen Winter
2e7f0bf51d cmake/KDAB/module - sync 2021-08-02 19:11:39 -04:00
Allen Winter
fa551b84f6 Merge branch '1.4' 2021-08-01 11:19:17 -04:00
Allen Winter
31d4b62181 cmake/KDAB/modules/KDQtInstallPaths.cmake - sync 2021-07-30 13:04:12 -04:00
Allen Winter
7ec542a211 post-merge cleaning
rearrange top-level CMakeLists.txt
2021-07-29 17:04:45 -04:00
Allen Winter
e615d1c618 Merge branch '1.4' 2021-07-29 16:47:45 -04:00
Allen Winter
c86813b1cb CMakeLists.txt - remove CMake option KDDockWidgets_UNINSTALL
no longer needed since the uninstall target is created
only for "rooted" projects (ie. non-submodules)
2021-07-29 12:59:15 -04:00
Allen Winter
1fe651aaa7 CMakeLists.txt - re-organize. implement PROJECT_IS_ROOT
set KDDockWidget_IS_ROOT for detecting if this project
is being used inside another parent-parent (eg. submodule)
If not IS_ROOT then we don't build tests, examples, etc.
2021-07-29 12:56:39 -04:00
Allen Winter
ba7809236b CMakeLists.txt - follow using PYSIDE_CUSTOM_PREFIX 2021-07-29 12:55:09 -04:00
Allen Winter
84814b3c59 FindPySide2.cmake,FindPySide6.cmake - sync 2021-07-29 12:54:09 -04:00
Allen Winter
73ca6e6671 buildsystem - install the Python bindings into standard "site-packages"
Follow Python standards and install the bindings into
<prefix>/lib/pythonX.Y/site-packages

sync the Python cmake files with KDAB repository
2021-07-23 12:05:02 -04:00
Allen Winter
990fac9a0a open for 1.5 - new features 2021-07-16 13:02:05 -04:00
Allen Winter
31ec4cdecb Merge branch '1.4' 2021-07-16 12:59:32 -04:00
Allen Winter
e32aaccaac Merge branch '1.4' 2021-07-15 13:30:58 -04:00
Sergio Martins
3302401a36 Merge branch '1.4' 2021-07-13 17:34:58 +01:00
Renato Araujo Oliveira Filho
c0c1383254 Python: Create bindings for InitialOption
Added bindings for missing enums
Added InitialOption as value type

Task-Id: #198
2021-07-13 17:33:50 +01:00
Sergio Martins
13a7d27046 vscode: Fix the build dir paths
They changed in the cmake preset
2021-07-13 08:27:21 +01:00
74 changed files with 1378 additions and 292 deletions

View File

@@ -1,4 +1,4 @@
[codespell]
skip = ./build-*,.git
skip = ./build-*,.git,*.svg,rc_assets.py
interactive = 3
ignore-words-list = overlay,overlayed

2
.krazy
View File

@@ -19,5 +19,5 @@ SKIP /KDDockWidgetsConfig.cmake.in
#skip more files
SKIP CMakePresets.json
#skip the borrowed code in the cmake subdir
SKIP /cmake/Qt5Portability.cmake|/cmake/ECM/|/cmake/InstallLocation.cmake
SKIP /cmake/InstallLocation.cmake|/cmake/ECM/|/cmake/KDAB/

View File

@@ -19,12 +19,6 @@
# Build static versions of the libraries
# Default=false
#
# -DKDDockWidgets_UNINSTALL=[true|false]
# Setup the uninstall target.
# You may want to disable the uninstall target when you are using KDDockWidgets
# as a submodule directly and have a custom uninstall target of your own.
# Default=true
#
# -DKDDockWidgets_TESTS=[true|false]
# Build the test harness.
# Currently ignored unless you also set KDDockWidgets_DEVELOPER_MODE=True
@@ -45,7 +39,7 @@
# -DKDDockWidgets_PYTHON_BINDINGS=[true|false]
# Build/Generate python bindings. Always false for Debug builds
# (If your shiboken or pyside is installed in a non-standard locations
# try passing the SHIBOKEN_CUSTOM_PREFIX and PYSIDE2_CUSTOM_PREFIX variables.)
# try passing the SHIBOKEN_CUSTOM_PREFIX and PYSIDE_CUSTOM_PREFIX variables.)
# Default=false
#
# -DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=[path]
@@ -76,12 +70,6 @@
# Default=true
cmake_minimum_required(VERSION 3.7)
if(POLICY CMP0020)
cmake_policy(SET CMP0020 NEW)
endif()
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
# Allow using a non-KDAB install location.
set(KDAB_INSTALL True CACHE INTERNAL "Install to default KDAB Location")
@@ -102,65 +90,27 @@ endif()
set(${PROJECT_NAME}_VERSION_MAJOR 1)
set(${PROJECT_NAME}_VERSION_MINOR 4)
set(${PROJECT_NAME}_VERSION_PATCH 1)
set(${PROJECT_NAME}_VERSION_PATCH 95)
set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
set(PROJECT_VERSION ${${PROJECT_NAME}_VERSION}) #PROJECT_VERSION is needed by some ECM modules
set(${PROJECT_NAME}_SOVERSION "1.4")
set(${PROJECT_NAME}_SOVERSION "1.5")
include(FeatureSummary)
option(${PROJECT_NAME}_QT6 "Build against Qt 6" OFF)
option(${PROJECT_NAME}_DEVELOPER_MODE "Developer Mode" OFF)
option(${PROJECT_NAME}_PYTHON_BINDINGS "Build python bindings" OFF)
option(${PROJECT_NAME}_UNINSTALL "Enable the uninstall target" ON)
option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
option(${PROJECT_NAME}_STATIC "Build statically" OFF)
option(${PROJECT_NAME}_TESTS "Build the tests" OFF)
option(${PROJECT_NAME}_EXAMPLES "Build the examples" ON)
option(${PROJECT_NAME}_DOCS "Build the API documentation" OFF)
option(${PROJECT_NAME}_WERROR "Use -Werror (will be true for developer-mode unconditionally)" OFF)
option(${PROJECT_NAME}_X11EXTRAS "On Linux, link against QtX11Extras so we can detect if the compositor supports transparency. Not applicable to other platforms or Qt6." ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/ECM/modules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/KDAB/modules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Python")
#Always build the test harness in developer-mode
if(${PROJECT_NAME}_DEVELOPER_MODE)
set(${PROJECT_NAME}_TESTS ON)
set(${PROJECT_NAME}_WERROR ON)
include(ECMEnableSanitizers)
endif()
option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(IS_CLANG_BUILD TRUE)
else()
set(IS_CLANG_BUILD FALSE)
endif()
if(${PROJECT_NAME}_QT6)
find_package(Qt6Widgets REQUIRED)
find_package(Qt6Test REQUIRED)
set(QT_MAJOR_VERSION 6)
set(KDDockWidgets_LIBRARY_QTID "-qt6")
else()
find_package(Qt5Widgets 5.12 REQUIRED)
find_package(Qt5Test 5.12 REQUIRED)
set(QT_MAJOR_VERSION 5)
set(KDDockWidgets_LIBRARY_QTID "")
endif()
include(KDQtInstallPaths) #to set QT_INSTALL_FOO variables
if(${PROJECT_NAME}_QTQUICK)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Set a default build type if none was specified
set(default_build_type "Release")
@@ -174,6 +124,61 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
if(${PROJECT_NAME}_QT6)
set(Qt_VERSION_MAJOR 6)
set(QT_MIN_VERSION "6.0.0")
find_package(Qt6Widgets ${QT_MIN_VERSION} REQUIRED)
find_package(Qt6Test ${QT_MIN_VERSION} REQUIRED)
set(${PROJECT_NAME}_LIBRARY_QTID "-qt6")
else()
set(Qt_VERSION_MAJOR 5)
set(QT_MIN_VERSION "5.12")
find_package(Qt5Widgets ${QT_MIN_VERSION} REQUIRED)
find_package(Qt5Test ${QT_MIN_VERSION} REQUIRED)
set(${PROJECT_NAME}_LIBRARY_QTID "")
endif()
include(KDQtInstallPaths) #to set QT_INSTALL_FOO variables
set(${PROJECT_NAME}_DEPS "widgets")
if(${PROJECT_NAME}_QTQUICK)
find_package(Qt${Qt_VERSION_MAJOR}Quick)
find_package(Qt${Qt_VERSION_MAJOR}QuickControls2)
add_definitions(-DKDDOCKWIDGETS_QTQUICK)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} quick quickcontrols2")
else()
add_definitions(-DKDDOCKWIDGETS_QTWIDGETS)
endif()
if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6 AND ${PROJECT_NAME}_X11EXTRAS)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} x11extras")
endif()
#Always build the test harness in developer-mode
if(${PROJECT_NAME}_DEVELOPER_MODE)
set(${PROJECT_NAME}_TESTS ON)
set(${PROJECT_NAME}_WERROR ON)
include(ECMEnableSanitizers)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(IS_CLANG_BUILD TRUE)
else()
set(IS_CLANG_BUILD FALSE)
endif()
if(${PROJECT_NAME}_QTQUICK)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# Default to hidden visibility for symbols
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
macro(set_compiler_flags targetName)
if(${PROJECT_NAME}_DEVELOPER_MODE)
target_compile_definitions(${targetName} PUBLIC DOCKS_DEVELOPER_MODE PRIVATE QT_FORCE_ASSERTS)
@@ -191,22 +196,28 @@ macro(set_compiler_flags targetName)
if(${PROJECT_NAME}_WERROR AND (NOT MSVC OR IS_CLANG_BUILD)) # clang-cl accepts these too
target_compile_options(${targetName} PRIVATE -Werror -Wundef -Wno-error=deprecated-declarations)
endif()
endmacro()
set(${PROJECT_NAME}_DEPS "widgets")
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "Intel" AND NOT WIN32))
# Linker warnings should be treated as errors
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings ${CMAKE_MODULE_LINKER_FLAGS}")
if(${PROJECT_NAME}_QTQUICK)
find_package(Qt${QT_MAJOR_VERSION}Quick)
find_package(Qt${QT_MAJOR_VERSION}QuickControls2)
add_definitions(-DKDDOCKWIDGETS_QTQUICK)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} quick quickcontrols2")
else()
add_definitions(-DKDDOCKWIDGETS_QTWIDGETS)
endif()
string(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" compileflags)
if("${CMAKE_CXX_FLAGS} ${${compileflags}}" MATCHES "-fsanitize")
set(sanitizers_enabled TRUE)
else()
set(sanitizers_enabled FALSE)
endif()
if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} x11extras")
# cannot enable this for clang + sanitizers
if(NOT sanitizers_enabled OR NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Do not allow undefined symbols, even in non-symbolic shared libraries
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--no-undefined ${CMAKE_MODULE_LINKER_FLAGS}")
endif()
endif()
if(${PROJECT_NAME}_STATIC)
@@ -226,18 +237,22 @@ endif()
# setup default install locations
include(InstallLocation)
add_subdirectory(src)
if(${PROJECT_NAME}_PYTHON_BINDINGS)
if(CMAKE_BUILD_TYPE MATCHES "^[Dd]eb" OR ${PROJECT_NAME}_STATIC)
message(FATAL_ERROR "** Python Bindings are disabled in debug or static builds.")
endif()
add_subdirectory(python)
endif()
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(${PROJECT_NAME}_IS_ROOT_PROJECT TRUE)
# Generate .pri file for qmake users
if(CMAKE_VERSION VERSION_GREATER "3.11.99" AND NOT CMAKE_CONFIGURATION_TYPES) # Not working with VS generator or older cmake versions
message(STATUS "Building ${PROJECT_NAME} ${${PROJECT_NAME}_VERSION} in ${CMAKE_BUILD_TYPE} mode. Installing to ${CMAKE_INSTALL_PREFIX}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
install(FILES LICENSE.txt README.md DESTINATION ${INSTALL_DOC_DIR})
install(DIRECTORY LICENSES DESTINATION ${INSTALL_DOC_DIR})
# Generate .pri file for qmake users
#TODO: ECM does not support Qt6 yet
if(QT_MAJOR_VERSION EQUAL 5)
if(Qt_VERSION_MAJOR EQUAL 5 AND
CMAKE_VERSION VERSION_GREATER "3.11.99" AND NOT CMAKE_CONFIGURATION_TYPES) # Not working with VS generator or older cmake versions
include(ECMGeneratePriFile)
set(PROJECT_VERSION_STRING ${${PROJECT_NAME}_VERSION})
ecm_generate_pri_file(BASE_NAME KDDockWidgets
@@ -248,77 +263,80 @@ if(CMAKE_VERSION VERSION_GREATER "3.11.99" AND NOT CMAKE_CONFIGURATION_TYPES) #
)
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
endif()
else()
message(WARNING "Unable to generate the pri file for qmake users. Try updating CMake.")
endif()
install(FILES LICENSE.txt README.md DESTINATION ${INSTALL_DOC_DIR})
install(DIRECTORY LICENSES DESTINATION ${INSTALL_DOC_DIR})
add_subdirectory(src)
if(${PROJECT_NAME}_EXAMPLES)
if(${PROJECT_NAME}_QTQUICK)
add_subdirectory(examples/qtquick)
if(${PROJECT_NAME}_IS_ROOT_PROJECT)
if(${PROJECT_NAME}_EXAMPLES)
if(${PROJECT_NAME}_QTQUICK)
add_subdirectory(examples/qtquick)
else()
add_subdirectory(examples/dockwidgets)
add_subdirectory(examples/minimal)
add_subdirectory(examples/minimal-mdi)
set_compiler_flags(kddockwidgets_example)
set_compiler_flags(kddockwidgets_minimal_example)
endif()
endif()
if(${PROJECT_NAME}_DEVELOPER_MODE)
if(${PROJECT_NAME}_TESTS)
enable_testing()
add_subdirectory(tests)
# Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
if(NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (160 tests), so split it in more runs so we can use threads.
add_test(NAME tst_docks0 COMMAND tests_launcher 0 5)
add_test(NAME tst_docks1 COMMAND tests_launcher 1 5)
add_test(NAME tst_docks2 COMMAND tests_launcher 2 5)
add_test(NAME tst_docks3 COMMAND tests_launcher 3 5)
add_test(NAME tst_docks4 COMMAND tests_launcher 4 5)
add_test(NAME tst_docks5 COMMAND tests_launcher 5 5)
add_test(NAME tst_docks6 COMMAND tests_launcher 6 5)
add_test(NAME tst_docks7 COMMAND tests_launcher 7 5)
add_test(NAME tst_docks8 COMMAND tests_launcher 8 5)
add_test(NAME tst_docks9 COMMAND tests_launcher 9 5)
add_test(NAME tst_docks10 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks11 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks12 COMMAND tests_launcher 11 5)
add_test(NAME tst_docks13 COMMAND tests_launcher 12 5)
add_test(NAME tst_docks14 COMMAND tests_launcher 13 5)
add_test(NAME tst_docks15 COMMAND tests_launcher 14 5)
add_test(NAME tst_docks16 COMMAND tests_launcher 15 5)
add_test(NAME tst_docks17 COMMAND tests_launcher 16 5)
add_test(NAME tst_docks18 COMMAND tests_launcher 17 5)
add_test(NAME tst_docks19 COMMAND tests_launcher 18 5)
add_test(NAME tst_docks20 COMMAND tests_launcher 19 5)
add_test(NAME tst_docks21 COMMAND tests_launcher 20 5) # one more for rounding leftovers
endif()
if(NOT ${PROJECT_NAME}_QTQUICK)
# tst_multisplitter depends on QWidget
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
endif()
endif()
endif()
if(${PROJECT_NAME}_DOCS)
add_subdirectory(docs) # needs to go last, in case there are build source files
else()
add_subdirectory(examples/dockwidgets)
add_subdirectory(examples/minimal)
add_subdirectory(examples/minimal-mdi)
set_compiler_flags(kddockwidgets_example)
set_compiler_flags(kddockwidgets_minimal_example)
add_custom_target(docs
COMMAND ${CMAKE_COMMAND} -E echo "Sorry, there is no docs target since KDDockWidgets_DOCS=OFF."
"Re-run cmake with the -DKDDockWidgets_DOCS=True option if you want to generate the documentation.")
endif()
endif()
if(${PROJECT_NAME}_DEVELOPER_MODE)
if(${PROJECT_NAME}_TESTS)
enable_testing()
add_subdirectory(tests)
# Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
if(NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (160 tests), so split it in more runs so we can use threads.
add_test(NAME tst_docks0 COMMAND tests_launcher 0 5)
add_test(NAME tst_docks1 COMMAND tests_launcher 1 5)
add_test(NAME tst_docks2 COMMAND tests_launcher 2 5)
add_test(NAME tst_docks3 COMMAND tests_launcher 3 5)
add_test(NAME tst_docks4 COMMAND tests_launcher 4 5)
add_test(NAME tst_docks5 COMMAND tests_launcher 5 5)
add_test(NAME tst_docks6 COMMAND tests_launcher 6 5)
add_test(NAME tst_docks7 COMMAND tests_launcher 7 5)
add_test(NAME tst_docks8 COMMAND tests_launcher 8 5)
add_test(NAME tst_docks9 COMMAND tests_launcher 9 5)
add_test(NAME tst_docks10 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks11 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks12 COMMAND tests_launcher 11 5)
add_test(NAME tst_docks13 COMMAND tests_launcher 12 5)
add_test(NAME tst_docks14 COMMAND tests_launcher 13 5)
add_test(NAME tst_docks15 COMMAND tests_launcher 14 5)
add_test(NAME tst_docks16 COMMAND tests_launcher 15 5)
add_test(NAME tst_docks17 COMMAND tests_launcher 16 5)
add_test(NAME tst_docks18 COMMAND tests_launcher 17 5)
add_test(NAME tst_docks19 COMMAND tests_launcher 18 5)
add_test(NAME tst_docks20 COMMAND tests_launcher 19 5)
add_test(NAME tst_docks21 COMMAND tests_launcher 20 5) # one more for rounding leftovers
endif()
if(NOT ${PROJECT_NAME}_QTQUICK)
# tst_multisplitter depends on QWidget
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
endif()
endif()
endif()
if(${PROJECT_NAME}_DOCS)
add_subdirectory(docs) # needs to go last, in case there are build source files
#don't create the dummy docs target as it can conflict when used as a submodule
#else()
# add_custom_target(docs
# COMMAND ${CMAKE_COMMAND} -E echo "Sorry, there is no docs target since KDDockWidgets_DOCS=OFF."
# "Re-run cmake with the -DKDDockWidgets_DOCS=True option if you want to generate the documentation.")
endif()
if(${PROJECT_NAME}_UNINSTALL)
# Add uninstall target
include(ECMUninstallTarget)
endif()
if(${PROJECT_NAME}_PYTHON_BINDINGS)
if(CMAKE_BUILD_TYPE MATCHES "^[Dd]eb" OR ${PROJECT_NAME}_STATIC)
message(FATAL_ERROR "** Python Bindings are disabled in debug or static builds.")
endif()
add_subdirectory(python)
endif()
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@@ -74,6 +74,17 @@
"CMAKE_UNITY_BUILD" : "ON"
}
},
{
"name": "release-no-x11extras",
"displayName": "release-no-x11extras",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-release-no-x11extras",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_UNITY_BUILD" : "ON",
"KDDockWidgets_X11EXTRAS" : "OFF"
}
},
{
"name": "release-qtquick",
"displayName": "release-qtquick",

View File

@@ -1,5 +1,11 @@
* v1.5.0 (unreleased)
- Install the Python bindings to "site-packages"
- The Python bindings for Qt6 use the "PyKDDockWidgetsQt6" namespace
- Co-installable with Qt6
- Adds support for non-detachable central widget, MainWindowOption_HasCentralWidget. (#225)
* v1.4.1 (unreleased)
-
-
* v1.4.0 (16 July 2021)
- No longer supports Qt versions less than 5.12

View File

@@ -106,12 +106,18 @@ You can change the installation location by passing the option `-DCMAKE_INSTALL_
Using
=====
From your CMake project, add
From your CMake Qt5 project, add
```
find_package(KDDockWidgets CONFIG)
```
or for Qt6
```
find_package(KDDockWidgets-qt6 CONFIG)
```
and link to the imported target `KDAB::kddockwidgets`.
That's all you need to do (the imported target also brings in the include directories)

View File

@@ -24,7 +24,7 @@ if(NOT INSTALL_DATADIR)
set(INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR})
endif()
if(NOT INSTALL_DOC_DIR)
set(INSTALL_DOC_DIR ${CMAKE_INSTALL_DOCDIR})
set(INSTALL_DOC_DIR ${CMAKE_INSTALL_DOCDIR}${KDDockWidgets_LIBRARY_QTID})
endif()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

View File

@@ -5,43 +5,48 @@
# SPDX-License-Identifier: BSD-3-Clause
#
# Assumes you've already found Qt and Qt_VERSION_MAJOR is set
#
# Create variables for all the various install paths for the Qt version in use
# Make sure to have found Qt before using this.
# sets variables like QT_INSTALL_PREFIX, QT_INSTALL_DATA, QT_INSTALL_DOCS, etc.
# run qmake -query to see a full list
if(TARGET Qt${QT_MAJOR_VERSION}::qmake)
get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_MAJOR_VERSION}::qmake LOCATION)
if(NOT DEFINED Qt_VERSION_MAJOR)
message(FATAL_ERROR "Please set Qt_VERSION_MAJOR first (ie. set(Qt_VERSION_MAJOR 5))")
endif()
if(TARGET Qt${Qt_VERSION_MAJOR}::qmake)
get_target_property(QT_QMAKE_EXECUTABLE Qt${Qt_VERSION_MAJOR}::qmake LOCATION)
else()
message(FATAL_ERROR "No supported Qt version found. Make sure you find Qt before calling this")
message(FATAL_ERROR "No supported Qt version found. Make sure you find Qt before calling this")
endif()
execute_process(
COMMAND ${QT_QMAKE_EXECUTABLE} -query
RESULT_VARIABLE return_code
OUTPUT_VARIABLE ALL_VARS
COMMAND ${QT_QMAKE_EXECUTABLE} -query
RESULT_VARIABLE return_code
OUTPUT_VARIABLE ALL_VARS
)
if(NOT return_code EQUAL 0)
message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query")
message(FATAL_ERROR "QMake call failed: ${return_code}")
message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query")
message(FATAL_ERROR "QMake call failed: ${return_code}")
endif()
string(REPLACE "\n" ";" VARS_LIST ${ALL_VARS})
foreach(QVAL ${VARS_LIST})
if(QVAL MATCHES "QT_INSTALL_")
string(REPLACE ":" ";" QVAL_LIST ${QVAL})
list(LENGTH QVAL_LIST listlen)
list(GET QVAL_LIST 0 var)
if(WIN32 AND ${listlen} GREATER 2)
list(GET QVAL_LIST 2 path)
list(GET QVAL_LIST 1 drive)
set(path "${drive}:${path}")
else()
list(GET QVAL_LIST 1 path)
if(QVAL MATCHES "QT_INSTALL_")
string(REPLACE ":" ";" QVAL_LIST ${QVAL})
list(LENGTH QVAL_LIST listlen)
list(GET QVAL_LIST 0 var)
if(WIN32 AND ${listlen} GREATER 2)
list(GET QVAL_LIST 2 path)
list(GET QVAL_LIST 1 drive)
set(path "${drive}:${path}")
else()
list(GET QVAL_LIST 1 path)
endif()
if(NOT ${var}) #if set already on the command line for example
set(${var} ${path} CACHE PATH "Qt install path for ${var}")
endif()
endif()
if(NOT ${var}) #if set already on the command line for example
set(${var} ${path} CACHE PATH "Qt install path for ${var}")
endif()
endif()
endforeach()

View File

@@ -130,6 +130,11 @@ macro(CREATE_PYTHON_BINDINGS
OUTPUT_NAME ${MODULE_NAME}
LIBRARY_OUTPUT_DIRECTORY ${MODULE_OUTPUT_DIR}
)
if(APPLE)
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@loader_path")
elseif(NOT WIN32) #ie. linux
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
if(WIN32)
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".pyd")

View File

@@ -130,6 +130,11 @@ macro(CREATE_PYTHON_BINDINGS
OUTPUT_NAME ${MODULE_NAME}
LIBRARY_OUTPUT_DIRECTORY ${MODULE_OUTPUT_DIR}
)
if(APPLE)
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@loader_path")
elseif(NOT WIN32) #ie. linux
set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN")
endif()
if(WIN32)
set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX ".pyd")

View File

@@ -74,15 +74,15 @@ cmake . -DCMAKE_INSTALL_PREFIX=/usr -DKDDockWidgets_QT6=True -DCMAKE_BUILD_TYPE=
%files
%defattr(-,root,root)
%{_prefix}/share/doc/KDDockWidgets
%{_prefix}/share/doc/KDDockWidgets-qt6
%{_libdir}/libkddockwidgets-qt6.so.*
%files devel
%defattr(-,root,root,-)
%dir %{_includedir}/kddockwidgets
%{_includedir}/kddockwidgets/*
%dir %{_libdir}/cmake/KDDockWidgets
%{_libdir}/cmake/KDDockWidgets/*
%dir %{_includedir}/kddockwidgets-qt6
%{_includedir}/kddockwidgets-qt6/kddockwidgets/*
%dir %{_libdir}/cmake/KDDockWidgets-qt6
%{_libdir}/cmake/KDDockWidgets-qt6/*
%{_libdir}/libkddockwidgets-qt6.so
#%{_prefix}/mkspecs/modules/* ECMGeneratePriFile isn't ported to Qt6 yet

View File

@@ -9,17 +9,6 @@
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
# qhelpgenerator
find_program(QHELPGEN_EXECUTABLE qhelpgenerator
HINTS ${QT_INSTALL_BINS}
)
if(QHELPGEN_EXECUTABLE)
set(HAVE_QHELPGEN "YES")
else()
set(HAVE_QHELPGEN "NO")
message(STATUS "Unable to generate the API documentation in qch format. To fix, install the qthelpgenerator program which comes with Qt5.")
endif()
# Doxygen
find_package(Doxygen)
set_package_properties(Doxygen PROPERTIES
@@ -28,12 +17,6 @@ set_package_properties(Doxygen PROPERTIES
URL "https://www.doxygen.org"
PURPOSE "Needed to build the API documentation."
)
if(DOXYGEN_DOT_EXECUTABLE)
set(HAVE_DOT "YES")
else()
set(HAVE_DOT "NO")
message(STATUS "Unable to provide inheritance diagrams for the API documentation. To fix, install the graphviz project from https://www.graphviz.org")
endif()
if(DOXYGEN_FOUND)
add_subdirectory(api)

View File

@@ -9,6 +9,25 @@
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
# dot should come with Doxygen find_package(Doxygen)
if(DOXYGEN_DOT_EXECUTABLE)
set(HAVE_DOT "YES")
else()
set(HAVE_DOT "NO")
message(STATUS "Unable to provide inheritance diagrams for the API documentation. To fix, install the graphviz project from https://www.graphviz.org")
endif()
# qhelpgenerator
find_program(QHELPGEN_EXECUTABLE qhelpgenerator
HINTS ${QT_INSTALL_BINS}
)
if(QHELPGEN_EXECUTABLE)
set(HAVE_QHELPGEN "YES")
else()
set(HAVE_QHELPGEN "NO")
message(STATUS "Unable to generate the API documentation in qch format. To fix, install the qthelpgenerator program which comes with Qt.")
endif()
find_file(QDOC_QTCORE_TAG qtcore.tags
HINTS ${QT_INSTALL_DOCS}/qtcore
HINTS ${QT_INSTALL_DATA}/doc/qtcore
@@ -47,4 +66,6 @@ add_custom_command(
add_custom_target(kddockwidgets-api.qch ALL DEPENDS ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch)
add_custom_target(docs DEPENDS kddockwidgets-api.qch)
install(FILES ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch DESTINATION ${INSTALL_DOC_DIR})
set(QCH_INSTALL_DIR ${INSTALL_DOC_DIR} CACHE STRING "Install location of Qt Assistant help files.")
install(FILES ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch DESTINATION ${QCH_INSTALL_DIR})
install(FILES ${DOXYGEN_OUTPUT_DIR}/kddockwidgets.tags DESTINATION ${INSTALL_DOC_DIR})

View File

@@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "KDDockWidgets API Documentation"
PROJECT_NAME = "@PROJECT_NAME@ API Documentation"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
@@ -874,7 +874,9 @@ INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.cpp \
*.h \
*.md
*.dox \
*.md \
*.gif
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@@ -1446,7 +1448,7 @@ QCH_FILE = "@DOXYGEN_OUTPUT_DIR@/qch/kddockwidgets-api.qch"
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = com.kdab.KDDockWidgets.api.@KDDockWidgets_VERSION@
QHP_NAMESPACE = com.kdab.@PROJECT_NAME@.api.@KDDockWidgets_VERSION@
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
@@ -1455,7 +1457,7 @@ QHP_NAMESPACE = com.kdab.KDDockWidgets.api.@KDDockWidgets_VERSION@
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = KDDockWidgets-@KDDockWidgets_VERSION@
QHP_VIRTUAL_FOLDER = @PROJECT_NAME@-@KDDockWidgets_VERSION@
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
@@ -1758,7 +1760,7 @@ LATEX_OUTPUT = latex
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME =
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
@@ -1795,7 +1797,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4wide
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
@@ -1872,7 +1874,7 @@ PDF_HYPERLINKS = YES
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
@@ -2255,7 +2257,7 @@ TAGFILES = "@QDOC_TAG_DIR@/qtcore/qtcore.tags=https://doc.qt.io/qt
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE = kddockwidgets.tag
GENERATE_TAGFILE = "@DOXYGEN_OUTPUT_DIR@/kddockwidgets.tags"
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be

View File

@@ -10,6 +10,7 @@
*/
#include "MyFrameworkWidgetFactory.h"
#include "MyTitleBar_CSS.h"
#include <kddockwidgets/FrameworkWidgetFactory.h>
@@ -89,11 +90,13 @@ MySeparator::~MySeparator() = default;
KDDockWidgets::TitleBar * CustomWidgetFactory::createTitleBar(KDDockWidgets::Frame *frame) const
{
// Feel free to return MyTitleBar_CSS here instead, but just for education purposes!
return new MyTitleBar(frame);
}
KDDockWidgets::TitleBar * CustomWidgetFactory::createTitleBar(KDDockWidgets::FloatingWindow *fw) const
{
// Feel free to return MyTitleBar_CSS here instead, but just for education purposes!
return new MyTitleBar(fw);
}
@@ -101,4 +104,3 @@ Layouting::Separator * CustomWidgetFactory::createSeparator(Layouting::Widget *p
{
return new MySeparator(parent);
}

View File

@@ -56,9 +56,9 @@ MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowO
, m_dockwidget5DoesntCloseBeforeRestore(dockwidget5DoesntCloseBeforeRestore)
{
auto menubar = menuBar();
auto fileMenu = new QMenu(QStringLiteral("File"));
m_toggleMenu = new QMenu(QStringLiteral("Toggle"));
auto miscMenu = new QMenu(QStringLiteral("Misc"));
auto fileMenu = new QMenu(QStringLiteral("File"), this);
m_toggleMenu = new QMenu(QStringLiteral("Toggle"), this);
auto miscMenu = new QMenu(QStringLiteral("Misc"), this);
menubar->addMenu(fileMenu);
menubar->addMenu(m_toggleMenu);
@@ -115,6 +115,10 @@ MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowO
setAffinities({ affinityName });
createDockWidgets();
if (options & KDDockWidgets::MainWindowOption_HasCentralWidget) {
setPersistentCentralWidget(new MyWidget1());
}
}
MyMainWindow::~MyMainWindow()

View File

@@ -0,0 +1,86 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef EXAMPLETITLEBAR_CSS_H
#define EXAMPLETITLEBAR_CSS_H
#pragma once
#include <kddockwidgets/private/widgets/TitleBarWidget_p.h>
/**
* @brief Shows how to implement a custom titlebar which uses "Qt StyleSheets".
*
* Derive from KDDockWidgets::DefaultWidgetFactory and override the two createTitleBar() methods.
*
* To try it out, modify examples/dockwidgets/MyFrameworkWidgetFactory.cpp to return a MyTitleBar_CSS instance.
* Run the example with: ./bin/kddockwidgets_example -p
*
* WARNINGS:
* - Qt StyleSheets are not recommended for new applications. Often you are able to style 90% of
* the application but then hit a road block. QStyle is much more powerful and flexible.
* - The Qt maintainers have manifested intention to deprecated stylesheets.
* - Stylesheets are supported for built-in QWidgets (QPushButton, QComboBox, etc.), any widget
* that's not in Qt needs to be crafted by the user, that includes, for example, having to paint
* your background manually. KDDockWidget::TitleBarWidget does this for your convenience though.
* - Qt stylesheets don't react to property changes (known old bug in Qt), for example:
* QLineEdit[readOnly="true"] { color: gray }
* this won't trigger when readOnly changes to false, you need to set/unset. This is QTBUG-51236
* - KDDockWidgets::TitleBarWidget::isFocused is a property, there for needs to workaround the
* above bug by unsetting the sheet and setting it again.
*/
class MyTitleBar_CSS : public KDDockWidgets::TitleBarWidget
{
public:
explicit MyTitleBar_CSS(KDDockWidgets::Frame *frame)
: KDDockWidgets::TitleBarWidget(frame)
{
init();
}
explicit MyTitleBar_CSS(KDDockWidgets::FloatingWindow *fw)
: KDDockWidgets::TitleBarWidget(fw)
{
init();
}
~MyTitleBar_CSS() override;
void initStyleSheet()
{
// Or use qApp->setStyleSheet(), as you prefer
setStyleSheet(QStringLiteral("KDDockWidgets--TitleBarWidget {"
"background: blue"
"}"
"KDDockWidgets--TitleBarWidget:hover {"
"background: red"
"}"
"KDDockWidgets--TitleBarWidget[isFocused=true] {"
"background: green"
"}"));
}
void init()
{
initStyleSheet();
connect(this, &KDDockWidgets::TitleBar::isFocusedChanged, this, [this] {
// Workaround QTBUG-51236, this makes the [isFocused=true] syntax useful
setStyleSheet(QString());
initStyleSheet();
});
}
};
MyTitleBar_CSS::~MyTitleBar_CSS()
{
}
#endif

View File

@@ -120,9 +120,17 @@ int main(int argc, char **argv)
parser.addOption(dontCloseBeforeRestore);
QCommandLineOption showButtonsInTabBarIfTitleBarHidden("show-buttons-in-tabbar-if-titlebar-hidden",
QCoreApplication::translate("main", "If we're not using title bars we'll still show the close and float button in the tab bar"));
QCoreApplication::translate("main", "If we're not using title bars we'll still show the close and float button in the tab bar"));
parser.addOption(showButtonsInTabBarIfTitleBarHidden);
QCommandLineOption centralWidget("central-widget",
QCoreApplication::translate("main", "The main window will have a non-detachable central widget"));
parser.addOption(centralWidget);
QCommandLineOption ctxtMenuOnTabs("allow-switch-tabs-via-menu",
QCoreApplication::translate("main", "Allow switching tabs via context menu in tabs area"));
parser.addOption(ctxtMenuOnTabs);
#if defined(DOCKS_DEVELOPER_MODE)
parser.addOption(centralFrame);
@@ -164,6 +172,9 @@ int main(int argc, char **argv)
options = parser.isSet(centralFrame) ? MainWindowOption_HasCentralFrame
: MainWindowOption_None;
if (parser.isSet(centralWidget))
options |= MainWindowOption_HasCentralWidget;
if (parser.isSet(noQtTool))
internalFlags |= KDDockWidgets::Config::InternalFlag_DontUseQtToolWindowsForFloatingWindows;
@@ -230,6 +241,9 @@ int main(int argc, char **argv)
if (parser.isSet(tabsHaveCloseButton))
flags |= KDDockWidgets::Config::Flag_TabsHaveCloseButton;
if (parser.isSet(ctxtMenuOnTabs))
flags |= KDDockWidgets::Config::Flag_AllowSwitchingTabsViaMenu;
if (parser.isSet(doubleClickMaximize))
flags |= KDDockWidgets::Config::Flag_DoubleClickMaximizes;

View File

@@ -8,9 +8,7 @@
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
set(PYTHON_VERSION "3.7" CACHE STRING "Use specific python version to build the project.")
find_package(Python3 ${PYTHON_VERSION} REQUIRED COMPONENTS Interpreter Development)
set(PYTHON_BINDING_NAMESPACE "PyKDDockWidgets")
# Just to fix warnings with --warn-uninitialized
if(NOT DEFINED SHIBOKEN_CUSTOM_PREFIX) #look for shiboken in a custom location
@@ -21,15 +19,24 @@ if(NOT DEFINED PYSIDE_CUSTOM_PREFIX) #look for pyside in a custom location
endif()
if(${PROJECT_NAME}_QT6)
find_package(Shiboken6 REQUIRED)
find_package(PySide6 ${Qt6Widgets_VERSION} EXACT REQUIRED)
include(PySide6ModuleBuild)
set(PYSIDE_MAJOR_VERSION "6")
set(PYTHON_BINDING_NAMESPACE "${PYTHON_BINDING_NAMESPACE}Qt${PYSIDE_MAJOR_VERSION}")
set(QtWidgets_VERSION ${Qt6Widgets_VERSION})
else()
find_package(Shiboken2 REQUIRED)
find_package(PySide2 ${Qt5Widgets_VERSION} EXACT REQUIRED)
include(PySide2ModuleBuild)
set(PYSIDE_MAJOR_VERSION "2")
set(QtWidgets_VERSION ${Qt5Widgets_VERSION})
endif()
find_package(Python3 3.7 REQUIRED COMPONENTS Interpreter Development)
find_package(Shiboken${PYSIDE_MAJOR_VERSION} REQUIRED)
find_package(PySide${PYSIDE_MAJOR_VERSION} ${QtWidgets_VERSION} EXACT REQUIRED)
if(NOT ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX)
set(${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
endif()
set(BINDINGS_DIR "${INSTALL_LIBRARY_DIR}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/${PYTHON_BINDING_NAMESPACE}")
set(${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX "${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/${BINDINGS_DIR}")
include(PySide${PYSIDE_MAJOR_VERSION}ModuleBuild)
add_subdirectory(PyKDDockWidgets)
add_subdirectory(tests)

View File

@@ -48,9 +48,10 @@ set(PyKDDockWidgets_target_include_directories
# Libraries that will be necessary to link the target, this will used in the command 'target_link_libraries'
set(PyKDDockWidgets_target_link_libraries
KDAB::kddockwidgets
Qt${QT_MAJOR_VERSION}::Core
Qt${QT_MAJOR_VERSION}::Gui
Qt${QT_MAJOR_VERSION}::Widgets
Qt${Qt_VERSION_MAJOR}::Core
Qt${Qt_VERSION_MAJOR}::Gui
Qt${Qt_VERSION_MAJOR}::Widgets
${Python3_LIBRARIES}
)
# changes on these files should trigger a new generation
@@ -80,4 +81,19 @@ create_python_bindings(
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY)
# install
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/__init__.py
$<TARGET_FILE:KDAB::kddockwidgets>
DESTINATION
${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}
)
if(NOT WIN32)
install(
FILES
$<TARGET_LINKER_FILE:KDAB::kddockwidgets>
$<TARGET_SONAME_FILE:KDAB::kddockwidgets>
DESTINATION
${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}
)
endif()

View File

@@ -10,9 +10,28 @@
#
import sys
import os
__all__ = ['KDDockWidgets']
def setupLibraryPath():
if sys.platform != 'win32':
return
from shiboken2 import shiboken2
from PySide@PYSIDE_MAJOR_VERSION@ import QtCore
extra_dll_dirs = [ os.path.abspath(os.path.dirname(shiboken2.__file__)),
os.path.abspath(os.path.dirname(QtCore.__file__)),
os.path.abspath(os.path.dirname(__file__)) ]
if sys.version_info[0] == 3 and sys.version_info[1] >= 8:
for dll_dir in extra_dll_dirs:
os.add_dll_directory(dll_dir)
for dll_dir in extra_dll_dirs:
os.environ['PATH'] = os.fspath(dll_dir) + os.pathsep + os.environ['PATH']
# Preload PySide libraries to avoid missing libraries while loading KDDockWidgets
try:
from PySide@PYSIDE_MAJOR_VERSION@ import QtCore
@@ -22,3 +41,5 @@ try:
except Exception:
print("Failed to load PySide")
raise
setupLibraryPath()

View File

@@ -0,0 +1,163 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
from PyKDDockWidgetsQt6 import KDDockWidgets
from PySide6 import QtCore, QtWidgets, QtGui
from MyWidget1 import MyWidget1
from MyWidget2 import MyWidget2
from MyWidget3 import MyWidget3
def newMyWidget(parent = None):
randomNumber = QtCore.QRandomGenerator.global_().bounded(0, 100) + 1
if (randomNumber < 50):
if (randomNumber < 33):
return MyWidget1(parent)
else:
return MyWidget3(parent)
else:
return MyWidget2(parent)
class MyMainWindow(KDDockWidgets.MainWindow):
s_count = 0
s_menuCount = 0
def __init__(self, uniqueName, options = KDDockWidgets.MainWindowOption_None, dockWidget0IsNonClosable = False, nonDockableDockWidget9 = False, restoreIsRelative = False, maxSizeForDockWidget8 = False, affinityName = "", parent = None):
super().__init__(uniqueName, options, parent)
self.m_dockWidget0IsNonClosable = dockWidget0IsNonClosable
self.m_dockWidget9IsNonDockable = nonDockableDockWidget9
self.m_restoreIsRelative = restoreIsRelative
self.m_maxSizeForDockWidget8 = maxSizeForDockWidget8
self.m_dockwidgets = []
menubar = self.menuBar()
fileMenu = QtWidgets.QMenu("File")
self.m_toggleMenu = QtWidgets.QMenu("Toggle")
menubar.addMenu(fileMenu)
menubar.addMenu(self.m_toggleMenu)
newAction = fileMenu.addAction("New DockWidget")
newAction.triggered.connect(self._newDockWidget)
saveLayoutAction = fileMenu.addAction("Save Layout")
saveLayoutAction.triggered.connect(self._saveLayout)
restoreLayoutAction = fileMenu.addAction("Restore Layout")
restoreLayoutAction.triggered.connect(self._restoreLayout)
closeAllAction = fileMenu.addAction("Close All")
closeAllAction.triggered.connect(self._closeAll)
layoutEqually = fileMenu.addAction("Layout Equally")
layoutEqually.triggered.connect(self.layoutEqually)
quitAction = fileMenu.addAction("Quit")
quitAction.triggered.connect(QtWidgets.QApplication.instance().quit)
self.setAffinities([ affinityName ])
self.createDockWidgets()
def _newDockWidget(self):
MyMainWindow.s_menuCount += 1
w = newMyWidget(self)
w.setGeometry(100, 100, 400, 400)
dock = KDDockWidgets.DockWidget("new dock %d"%(MyMainWindow.s_menuCount))
dock.setWidget(w)
dock.resize(600, 600)
dock.show()
self.m_dockwidgets.append(dock)
def _saveLayout(self):
#saver = KDDockWidgets.LayoutSaver()
#result = saver.saveToFile("mylayout.json")
#print("Saving layout to disk. Result=", result)
print("Not available")
def _restoreLayout(self):
#options = KDDockWidgets.RestoreOption_None
#if self.m_restoreIsRelative:
# options |= KDDockWidgets.RestoreOption_RelativeToMainWindow
#saver = KDDockWidgets.LayoutSaver(options)
#saver.restoreFromFile("mylayout.json")
print("Not available")
def _closeAll(self):
for dw in self.m_dockwidgets:
dw.close()
def createDockWidgets(self):
if self.m_dockWidget9IsNonDockable:
numDockWidgets = 10
else:
numDockWidgets = 9
# numDockWidgets = 2
# Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget instances)
for i in range(numDockWidgets):
self.m_dockwidgets.append(self.newDockWidget())
# MainWindow::addDockWidget() attaches a dock widget to the main window:
initialOpts = KDDockWidgets.InitialOption(KDDockWidgets.InitialVisibilityOption.StartHidden, QtCore.QSize(500, 500))
self.addDockWidget(self.m_dockwidgets[0], KDDockWidgets.Location_OnBottom, None, initialOpts)
# Here, for finer granularity we specify right of dockwidgets[0]:
self.addDockWidget(self.m_dockwidgets[1], KDDockWidgets.Location_OnRight, self.m_dockwidgets[0])
self.addDockWidget(self.m_dockwidgets[2], KDDockWidgets.Location_OnLeft)
self.addDockWidget(self.m_dockwidgets[3], KDDockWidgets.Location_OnBottom)
self.addDockWidget(self.m_dockwidgets[4], KDDockWidgets.Location_OnBottom)
# Tab two dock widgets together
self.m_dockwidgets[3].addDockWidgetAsTab(self.m_dockwidgets[5])
# 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
# and we tab 7 with it.
self.m_dockwidgets[6].addDockWidgetAsTab(self.m_dockwidgets[7])
# Floating windows also support nesting, here we add 8 to the bottom of the group
self.m_dockwidgets[6].addDockWidgetToContainingWindow(self.m_dockwidgets[8], KDDockWidgets.Location_OnBottom)
floatingWindow = self.m_dockwidgets[6].window()
floatingWindow.move(100, 100)
def newDockWidget(self):
# Passing options is optional, we just want to illustrate Option_NotClosable here
options = KDDockWidgets.DockWidget.Option_None
if (MyMainWindow.s_count == 0) and self.m_dockWidget0IsNonClosable:
options |= KDDockWidgets.DockWidget.Option_NotClosable
if (MyMainWindow.s_count == 9) and self.m_dockWidget9IsNonDockable:
options |= KDDockWidgets.DockWidget.Option_NotDockable
dock = KDDockWidgets.DockWidget("DockWidget #%d"%(MyMainWindow.s_count), options)
dock.setAffinities(self.affinities()); # optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
if MyMainWindow.s_count == 1:
dock.setIcon(QtGui.QIcon.fromTheme("mail-message"))
myWidget = newMyWidget(self)
if (MyMainWindow.s_count == 8) and self.m_maxSizeForDockWidget8:
# Set a maximum size on dock #8
myWidget.setMaximumSize(200, 200)
dock.setWidget(myWidget)
if dock.options() & KDDockWidgets.DockWidget.Option_NotDockable:
dock.setTitle("DockWidget #%d (%s)" %(MyMainWindow.s_count, "non dockable"))
else:
dock.setTitle("DockWidget #%d"%(MyMainWindow.s_count))
dock.resize(600, 600)
self.m_toggleMenu.addAction(dock.toggleAction())
MyMainWindow.s_count += 1
return dock

View File

@@ -0,0 +1,51 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import PyKDDockWidgetsQt6
from PySide6 import QtWidgets, QtGui, QtCore
class MyWidget(QtWidgets.QWidget):
s_images = {}
def __init__(self, backgroundFile, logoFile, parent = None):
super().__init__(parent)
self.m_background = self._lookupImage(backgroundFile)
self.m_logo = self._lookupImage(logoFile)
def _lookupImage(self, imageName):
if imageName == "":
return None
if imageName not in MyWidget.s_images:
MyWidget.s_images[imageName] = QtGui.QImage(imageName)
return MyWidget.s_images[imageName]
def drawLogo(self, p):
if not self.m_logo:
return
ratio = self.m_logo.height() / (self.m_logo.width() * 1.0)
maxWidth = int(0.80 * self.size().width())
maxHeight = int(0.80 * self.size().height())
proposedHeight = int(maxWidth * ratio)
if (proposedHeight <= maxHeight):
width = maxWidth
else:
width = int(maxHeight / ratio)
height = int(width * ratio)
targetLogoRect = QtCore.QRect(0,0, width, height)
targetLogoRect.moveCenter(self.rect().center() + QtCore.QPoint(0, -int(self.size().height() * 0.00)))
p.drawImage(targetLogoRect, self.m_logo, self.m_logo.rect());

View File

@@ -0,0 +1,29 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import PyKDDockWidgetsQt6
from PySide6 import QtWidgets, QtGui
from MyWidget import MyWidget
class MyWidget1(MyWidget):
def __init__(self, parent = None):
super().__init__(":/assets/triangles.png", ":/assets/KDAB_bubble_white.png", parent)
def paintEvent(self, ev):
p = QtGui.QPainter(self)
p.fillRect(self.rect(), QtGui.QColor(0xCC, 0xCC, 0xCC))
p.drawImage(self.m_background.rect(), self.m_background, self.m_background.rect())
self.drawLogo(p)

View File

@@ -0,0 +1,27 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import PyKDDockWidgetsQt6
from PySide6 import QtWidgets, QtGui, QtCore
from MyWidget import MyWidget
class MyWidget2(MyWidget):
def __init__(self, parent = None):
super().__init__("", ":/assets/KDAB_bubble_blue.png", parent)
def paintEvent(self, ev):
p = QtGui.QPainter(self)
p.fillRect(self.rect(), QtCore.Qt.white);
self.drawLogo(p)

View File

@@ -0,0 +1,32 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import PyKDDockWidgetsQt6
from PySide6 import QtWidgets, QtGui, QtCore
from MyWidget import MyWidget
class MyWidget3(MyWidget):
def __init__(self, parent = None):
super().__init__(":/assets/base.png", ":/assets/KDAB_bubble_fulcolor.png", parent)
self.m_triangle = QtGui.QImage(":/assets/tri.png")
def paintEvent(self, ev):
p = QtGui.QPainter(self)
p.fillRect(self.rect(), QtGui.QColor(0xD5, 0xD5, 0xD5))
p.drawImage(self.m_background.rect(), self.m_background, self.m_background.rect())
targetRect = QtCore.QRect(QtCore.QPoint(self.width() - self.m_triangle.width(), self.height() - self.m_triangle.height()), self.m_triangle.size())
self.drawLogo(p)

View File

@@ -0,0 +1,8 @@
Running python example
======================
Generate resource file with:
~# rcc -g python -o rc_assets.py ../../examples/dockwidgets/resources_example.qrc
Run the app:
~# python3 main.py

View File

@@ -0,0 +1,35 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
from PyKDDockWidgetsQt6 import KDDockWidgets
from MyMainWindow import MyMainWindow
from PySide6 import QtWidgets, QtCore
import sys
#import rc_assets
if __name__ == "__main__":
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)
app = QtWidgets.QApplication(sys.argv)
app.setOrganizationName("KDAB")
app.setApplicationName("Test app")
app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
mainWindow = MyMainWindow("MyMainWindow", )
mainWindow.setWindowTitle("Main Window 1")
mainWindow.resize(1200, 1200)
mainWindow.show()
app.exec_()

View File

@@ -11,7 +11,7 @@
from PyKDDockWidgets import KDDockWidgets
from PySide import QtCore, QtWidgets, QtGui
from PySide2 import QtCore, QtWidgets, QtGui
from MyWidget1 import MyWidget1
from MyWidget2 import MyWidget2

View File

@@ -11,7 +11,7 @@
import PyKDDockWidgets
from PySide import QtWidgets, QtGui, QtCore
from PySide2 import QtWidgets, QtGui, QtCore
class MyWidget(QtWidgets.QWidget):
s_images = {}

View File

@@ -11,7 +11,7 @@
import PyKDDockWidgets
from PySide import QtWidgets, QtGui
from PySide2 import QtWidgets, QtGui
from MyWidget import MyWidget

View File

@@ -11,7 +11,7 @@
import PyKDDockWidgets
from PySide import QtWidgets, QtGui, QtCore
from PySide2 import QtWidgets, QtGui, QtCore
from MyWidget import MyWidget

View File

@@ -11,7 +11,7 @@
import PyKDDockWidgets
from PySide import QtWidgets, QtGui, QtCore
from PySide2 import QtWidgets, QtGui, QtCore
from MyWidget import MyWidget

View File

@@ -12,7 +12,7 @@
from PyKDDockWidgets import KDDockWidgets
from MyMainWindow import MyMainWindow
from PySide import QtWidgets, QtCore
from PySide2 import QtWidgets, QtCore
import sys
import rc_assets

View File

@@ -0,0 +1,40 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.py @ONLY)
set(TEST_PYTHONPATH
${CMAKE_BINARY_DIR}/python
${CMAKE_CURRENT_BINARY_DIR}
)
set(TEST_LIBRARYPATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
if(WIN32)
set(TEST_LIBRARY_VAR "PATH")
string(REPLACE "\\" "/" TEST_PYTHONPATH "${TEST_PYTHONPATH}")
string(REPLACE "\\" "/" TEST_LIBRARYPATH "${TEST_LIBRARYPATH}")
list(JOIN TEST_PYTHONPATH "\\;" TEST_PYTHONPATH)
list(JOIN TEST_LIBRARYPATH "\\;" TEST_LIBRARYPATH)
else()
set(TEST_LIBRARY_VAR "LD_LIBRARY_PATH")
list(JOIN TEST_PYTHONPATH ":" TEST_PYTHONPATH)
list(JOIN TEST_LIBRARYPATH ":" TEST_LIBRARYPATH)
endif()
set(PYTHON_ENV_COMMON "PYTHONPATH=${TEST_PYTHONPATH};${TEST_LIBRARY_VAR}=${TEST_LIBRARYPATH}")
file(GLOB TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tst_*.py)
foreach(test_file ${TEST_FILES})
get_filename_component(test_name ${test_file} NAME_WE)
add_test(${test_name} ${Python3_EXECUTABLE} ${test_file})
set_tests_properties(${test_name} PROPERTIES ENVIRONMENT "${PYTHON_ENV_COMMON}")
endforeach()

View File

@@ -0,0 +1,19 @@
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import os
import sys
class TstConfig(object):
bindingsNamespace = "@PYTHON_BINDING_NAMESPACE@"
def initLibraryPath():
if sys.platform == 'win32' and sys.version_info[0] == 3 and sys.version_info[1] >= 8:
os.add_dll_directory("@CMAKE_RUNTIME_OUTPUT_DIRECTORY@")

View File

@@ -0,0 +1,30 @@
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Renato Araujo <renato.araujo@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
import unittest
import importlib
import inspect
from config import TstConfig
class TestImportModules(unittest.TestCase):
def test_importModules(self):
m = importlib.import_module(TstConfig.bindingsNamespace + '.KDDockWidgets')
moduleSymbols = []
for t in inspect.getmembers(m):
moduleSymbols.append(t[0])
symbols = ['MainWindow', 'DockWidget']
for symbol in symbols:
self.assertIn(symbol, moduleSymbols)
if __name__ == '__main__':
TstConfig.initLibraryPath()
unittest.main()

View File

@@ -245,9 +245,16 @@ add_library(KDAB::kddockwidgets ALIAS kddockwidgets)
set_target_properties(kddockwidgets PROPERTIES OUTPUT_NAME "kddockwidgets${KDDockWidgets_LIBRARY_QTID}")
set_compiler_flags(kddockwidgets)
if(${PROJECT_NAME}_QT6)
set(DOCKS_INCLUDES_INSTALL_PATH "include/kddockwidgets-qt6")
else()
set(DOCKS_INCLUDES_INSTALL_PATH "include/")
endif()
target_include_directories(kddockwidgets
PUBLIC
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:${DOCKS_INCLUDES_INSTALL_PATH}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/fwd_headers>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
@@ -287,16 +294,16 @@ if(CMAKE_COMPILER_IS_GNUCXX OR IS_CLANG_BUILD)
endif()
if(${PROJECT_NAME}_QTQUICK)
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Quick Qt${QT_MAJOR_VERSION}::QuickControls2)
target_link_libraries(kddockwidgets PUBLIC Qt${Qt_VERSION_MAJOR}::Widgets Qt${Qt_VERSION_MAJOR}::Quick Qt${Qt_VERSION_MAJOR}::QuickControls2)
else()
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::Widgets)
target_link_libraries(kddockwidgets PUBLIC Qt${Qt_VERSION_MAJOR}::Widgets PRIVATE Qt${Qt_VERSION_MAJOR}::WidgetsPrivate)
endif()
if(WIN32)
target_link_libraries(kddockwidgets PRIVATE Qt${QT_MAJOR_VERSION}::GuiPrivate dwmapi)
elseif(NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6)
find_package(Qt${QT_MAJOR_VERSION}X11Extras)
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::X11Extras)
target_link_libraries(kddockwidgets PRIVATE Qt${Qt_VERSION_MAJOR}::GuiPrivate dwmapi)
elseif(NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6 AND ${PROJECT_NAME}_X11EXTRAS)
find_package(Qt${Qt_VERSION_MAJOR}X11Extras)
target_link_libraries(kddockwidgets PUBLIC Qt${Qt_VERSION_MAJOR}::X11Extras)
endif()
set_target_properties(kddockwidgets PROPERTIES
@@ -331,23 +338,23 @@ if(MSVC AND NOT ${PROJECT_NAME}_STATIC)
install(FILES "$<TARGET_PDB_FILE_DIR:kddockwidgets>/$<TARGET_PDB_FILE_NAME:kddockwidgets>" DESTINATION ${INSTALL_LIBRARY_DIR} CONFIGURATIONS Debug RelWithDebInfo)
endif()
install(FILES ${camelcase_HEADERS} DESTINATION include/kddockwidgets)
install(FILES ${DOCKS_INSTALLABLE_INCLUDES} DESTINATION include/kddockwidgets)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_INCLUDES} DESTINATION include/kddockwidgets/private)
install(FILES private/multisplitter/Item_p.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Separator_p.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/indicators/ClassicIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
install(FILES private/indicators/SegmentedIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
install(FILES ${camelcase_HEADERS} DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets)
install(FILES ${DOCKS_INSTALLABLE_INCLUDES} DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_INCLUDES} DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private)
install(FILES private/multisplitter/Item_p.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Separator_p.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
install(FILES private/indicators/ClassicIndicators_p.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/indicators)
install(FILES private/indicators/SegmentedIndicators_p.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/indicators)
if(KDDockWidgets_QTQUICK)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_QUICK_INCLUDES} DESTINATION include/kddockwidgets/private/quick)
install(FILES private/multisplitter/Separator_quick.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget_quick.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_QUICK_INCLUDES} DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/quick)
install(FILES private/multisplitter/Separator_quick.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget_quick.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
else()
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION include/kddockwidgets/private/widgets)
install(FILES private/multisplitter/Separator_qwidget.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget_qwidget.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/widgets)
install(FILES private/multisplitter/Separator_qwidget.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Widget_qwidget.h DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets/private/multisplitter)
endif()
# Generate library version files
@@ -356,21 +363,21 @@ ecm_setup_version(
${${PROJECT_NAME}_VERSION}
VARIABLE_PREFIX KDDOCKWIDGETS
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kddockwidgets_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfigVersion.cmake"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets${KDDockWidgets_LIBRARY_QTID}ConfigVersion.cmake"
SOVERSION ${${PROJECT_NAME}_SOVERSION}
COMPATIBILITY AnyNewerVersion
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kddockwidgets_version.h" DESTINATION include/kddockwidgets)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kddockwidgets_version.h" DESTINATION ${DOCKS_INCLUDES_INSTALL_PATH}/kddockwidgets)
install(EXPORT kddockwidgetsTargets
FILE KDDockWidgetsTargets.cmake
FILE KDDockWidgets${KDDockWidgets_LIBRARY_QTID}Targets.cmake
NAMESPACE KDAB::
DESTINATION ${INSTALL_LIBRARY_DIR}/cmake/KDDockWidgets
DESTINATION ${INSTALL_LIBRARY_DIR}/cmake/KDDockWidgets${KDDockWidgets_LIBRARY_QTID}
)
configure_file(KDDockWidgetsConfig.cmake.in KDDockWidgetsConfig.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfigVersion.cmake"
DESTINATION ${INSTALL_LIBRARY_DIR}/cmake/KDDockWidgets
configure_file(KDDockWidgetsConfig.cmake.in KDDockWidgets${KDDockWidgets_LIBRARY_QTID}Config.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets${KDDockWidgets_LIBRARY_QTID}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets${KDDockWidgets_LIBRARY_QTID}ConfigVersion.cmake"
DESTINATION ${INSTALL_LIBRARY_DIR}/cmake/KDDockWidgets${KDDockWidgets_LIBRARY_QTID}
)
if(${PROJECT_NAME}_DEVELOPER_MODE)
@@ -381,7 +388,7 @@ if(${PROJECT_NAME}_DEVELOPER_MODE)
if(NOT ${PROJECT_NAME}_QTQUICK AND KDDockWidgets_LINTER) # TODO: We can support it
add_executable(kddockwidgets_linter layoutlinter_main.cpp)
target_link_libraries(kddockwidgets_linter kddockwidgets Qt${QT_MAJOR_VERSION}::Widgets)
target_link_libraries(kddockwidgets_linter kddockwidgets Qt${Qt_VERSION_MAJOR}::Widgets)
endif()
endif()

View File

@@ -83,6 +83,7 @@ public:
Flag_KeepAboveIfNotUtilityWindow = 0x10000, ///< Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows, you might still want them to keep above and not minimize when you focus the main window.
Flag_CloseOnlyCurrentTab = 0x20000, ///< The TitleBar's close button will only close the current tab, instead of all of them
Flag_ShowButtonsOnTabBarIfTitleBarHidden = 0x40000, ///< When using Flag_HideTitleBarWhenTabsVisible the close/float buttons disappear with the title bar. With Flag_ShowButtonsOnTabBarIfHidden they'll be shown in the tab bar.
Flag_AllowSwitchingTabsViaMenu = 0x80000, ///< Allow switching tabs via a context menu when right clicking on the tab area
Flag_Default = Flag_AeroSnapWithClientDecos ///< The defaults
};
Q_DECLARE_FLAGS(Flags, Flag)

View File

@@ -83,6 +83,13 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, InitialOption opt
return;
}
if (isPersistentCentralDockWidget()) {
qWarning() << Q_FUNC_INFO << "Not supported with MainWindowOption_HasCentralWidget."
<< "MainWindowOption_HasCentralWidget can only have 1 widget in the center."
<< "Use MainWindowOption_HasCentralFrame instead, which is similar but supports tabbing.";
return;
}
Frame *frame = d->frame();
if (frame) {
@@ -182,6 +189,9 @@ bool DockWidgetBase::setFloating(bool floats)
return false;
}
if (floats && isPersistentCentralDockWidget())
return false;
if (floats) {
d->saveTabIndex();
if (isTabbed()) {
@@ -620,6 +630,9 @@ void DockWidgetBase::Private::close()
return;
}
if (m_isPersistentCentralDockWidget)
return;
// If it's overlayed and we're closing, we need to close the overlay
if (SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
auto mainWindow = sb->mainWindow();
@@ -837,6 +850,11 @@ void DockWidgetBase::setMDIZ(int z)
#endif
}
bool DockWidgetBase::isPersistentCentralDockWidget() const
{
return d->m_isPersistentCentralDockWidget;
}
LayoutSaver::DockWidget::Ptr DockWidgetBase::Private::serialize() const
{
auto ptr = LayoutSaver::DockWidget::dockWidgetForName(q->uniqueName());

View File

@@ -450,6 +450,10 @@ public:
/// only implemented for QtQuick
void setMDIZ(int z);
///@brief Returns whether this dock widget is the main window persistent central widget
///This only applies when using MainWindowOption_HasCentralWidget
bool isPersistentCentralDockWidget() const;
Q_SIGNALS:
#ifdef KDDOCKWIDGETS_QTWIDGETS
///@brief signal emitted when the parent changed

View File

@@ -55,7 +55,9 @@ enum MainWindowOption
{
MainWindowOption_None = 0, ///> No option set
MainWindowOption_HasCentralFrame = 1, ///> Makes the MainWindow always have a central frame, for tabbing documents
MainWindowOption_MDI = 2 ///> EXPERIMENTAL!!1 The layout will be MDI. DockWidgets can have arbitrary positions, not restricted by any layout
MainWindowOption_MDI = 2, ///> The layout will be MDI. DockWidgets can have arbitrary positions, not restricted by any layout
MainWindowOption_HasCentralWidget = 4 | MainWindowOption_HasCentralFrame, ///> Similar to MainWindowOption_HasCentralFrame but
///> you'll have a central widget which can't be detached (Similar to regular QMainWindow).
};
Q_DECLARE_FLAGS(MainWindowOptions, MainWindowOption)
Q_ENUM_NS(MainWindowOptions)
@@ -258,7 +260,8 @@ enum FrameOption
FrameOption_None = 0,
FrameOption_AlwaysShowsTabs = 1,
FrameOption_IsCentralFrame = 2,
FrameOption_IsOverlayed = 4
FrameOption_IsOverlayed = 4,
FrameOption_NonDockable = 8 ///> You can't DND and tab things into this Frame
};
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
Q_ENUM_NS(FrameOptions)

View File

@@ -11,14 +11,14 @@
include(CMakeFindDependencyMacro)
find_dependency(Qt5Widgets REQUIRED)
find_dependency(Qt@Qt_VERSION_MAJOR@Widgets REQUIRED)
if (@KDDockWidgets_QTQUICK@)
find_dependency(Qt5Quick REQUIRED)
find_dependency(Qt@Qt_VERSION_MAJOR@Quick REQUIRED)
endif()
if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN)
if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT @KDDockWidgets_QT6@)
find_dependency(Qt5X11Extras REQUIRED)
endif()
# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/KDDockWidgetsTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/KDDockWidgets@KDDockWidgets_LIBRARY_QTID@Targets.cmake")

View File

@@ -608,11 +608,6 @@ bool LayoutSaver::Frame::isValid() const
return false;
}
if (options > 3) {
qWarning() << Q_FUNC_INFO << "Invalid options" << options;
return false;
}
if (!dockWidgets.isEmpty()) {
if (currentTabIndex >= dockWidgets.size() || currentTabIndex < 0) {
qWarning() << Q_FUNC_INFO << "Invalid tab index" << currentTabIndex << dockWidgets.size();
@@ -657,7 +652,7 @@ QVariantMap LayoutSaver::Frame::toVariantMap() const
map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
map.insert(QStringLiteral("options"), options);
map.insert(QStringLiteral("currentTabIndex"), currentTabIndex);
map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
map.insert(QStringLiteral("dockWidgets"), dockWidgetNames(dockWidgets));
return map;
@@ -674,6 +669,7 @@ void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
id = map.value(QStringLiteral("id")).toString();
isNull = map.value(QStringLiteral("isNull")).toBool();
objectName = map.value(QStringLiteral("objectName")).toString();
mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
options = static_cast<QFlags<FrameOption>::Int>(map.value(QStringLiteral("options")).toUInt());
currentTabIndex = map.value(QStringLiteral("currentTabIndex")).toInt();
@@ -770,9 +766,11 @@ QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
map.insert(QStringLiteral("parentIndex"), parentIndex);
map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
map.insert(QStringLiteral("normalGeometry"), Layouting::rectToMap(normalGeometry));
map.insert(QStringLiteral("screenIndex"), screenIndex);
map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
map.insert(QStringLiteral("isVisible"), isVisible);
map.insert(QStringLiteral("windowState"), windowState);
if (!affinities.isEmpty())
map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
@@ -785,10 +783,12 @@ void LayoutSaver::FloatingWindow::fromVariantMap(const QVariantMap &map)
multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
parentIndex = map.value(QStringLiteral("parentIndex")).toInt();
geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
normalGeometry = Layouting::mapToRect(map.value(QStringLiteral("normalGeometry")).toMap());
screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
isVisible = map.value(QStringLiteral("isVisible")).toBool();
affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
windowState = Qt::WindowState(map.value(QStringLiteral("windowState"), Qt::WindowNoState).toInt());
// Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
@@ -802,11 +802,6 @@ bool LayoutSaver::MainWindow::isValid() const
if (!multiSplitterLayout.isValid())
return false;
if (options != MainWindowOption_None && options != MainWindowOption_HasCentralFrame) {
qWarning() << Q_FUNC_INFO << "Invalid option" << options;
return false;
}
return true;
}

View File

@@ -31,6 +31,13 @@
#include "private/LayoutSaver_p.h"
#include "private/DockWidgetBase_p.h"
// Or we can have a createDockWidget() in the factory
#ifdef KDDOCKWIDGETS_QTQUICK
# include "DockWidgetQuick.h"
#else
# include "DockWidget.h"
#endif
using namespace KDDockWidgets;
static LayoutWidget *createLayoutWidget(MainWindowBase *mainWindow, MainWindowOptions options)
@@ -44,10 +51,11 @@ static LayoutWidget *createLayoutWidget(MainWindowBase *mainWindow, MainWindowOp
class MainWindowBase::Private
{
public:
explicit Private(MainWindowBase *mainWindow, MainWindowOptions options)
explicit Private(MainWindowBase *mainWindow, const QString &uniqueName, MainWindowOptions options)
: m_options(options)
, q(mainWindow)
, m_layoutWidget(createLayoutWidget(mainWindow, options))
, m_persistentCentralDockWidget(createPersistentCentralDockWidget(uniqueName))
{
}
@@ -56,6 +64,38 @@ public:
return m_options & MainWindowOption_HasCentralFrame;
}
bool supportsPersistentCentralWidget() const
{
if (!dropArea()) {
// This is the MDI case
return false;
}
return (m_options & MainWindowOption_HasCentralWidget) == MainWindowOption_HasCentralWidget;
}
DockWidgetBase* createPersistentCentralDockWidget(const QString &uniqueName) const
{
if (!supportsPersistentCentralWidget())
return nullptr;
auto dw = new DockWidgetType(QStringLiteral("%1-persistentCentralDockWidget").arg(uniqueName));
dw->dptr()->m_isPersistentCentralDockWidget = true;
Frame *frame = dropArea()->m_centralFrame;
if (!frame) {
qWarning() << Q_FUNC_INFO << "Expected central frame";
return nullptr;
}
frame->addWidget(dw);
return dw;
}
DropAreaWithCentralFrame *dropArea() const
{
return qobject_cast<DropAreaWithCentralFrame *>(m_layoutWidget);
}
CursorPositions allowedResizeSides(SideBarLocation loc) const;
QRect rectForOverlay(Frame *, SideBarLocation) const;
@@ -69,12 +109,13 @@ public:
MainWindowBase *const q;
QPointer<DockWidgetBase> m_overlayedDockWidget;
LayoutWidget *const m_layoutWidget;
DockWidgetBase *const m_persistentCentralDockWidget;
};
MainWindowBase::MainWindowBase(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
WidgetType *parent, Qt::WindowFlags flags)
: QMainWindowOrQuick(parent, flags)
, d(new Private(this, options))
, d(new Private(this, uniqueName, options))
{
setUniqueName(uniqueName);
@@ -109,7 +150,11 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
return;
}
if (d->supportsCentralFrame()) {
if (d->supportsPersistentCentralWidget()) {
qWarning() << Q_FUNC_INFO << "Not supported with MainWindowOption_HasCentralWidget."
<< "MainWindowOption_HasCentralWidget can only have 1 widget in the center."
<< "Use MainWindowOption_HasCentralFrame instead, which is similar but supports tabbing";
} else if (d->supportsCentralFrame()) {
dropArea()->m_centralFrame->addWidget(widget);
} else {
qWarning() << Q_FUNC_INFO << "Not supported without MainWindowOption_HasCentralFrame";
@@ -327,7 +372,7 @@ SideBarLocation MainWindowBase::Private::preferredSideBar(DockWidgetBase *dw) co
}
const Layouting::LayoutBorderLocations borders = item->adjacentLayoutBorders();
const qreal aspectRatio = dw->width() / (dw->height() * 1.0);
const qreal aspectRatio = dw->width() / (std::max(1, dw->height()) * 1.0);
/// 1. It's touching all borders
if (borders == Layouting::LayoutBorderLocation_All) {
@@ -450,6 +495,9 @@ void MainWindowBase::moveToSideBar(DockWidgetBase *dw)
void MainWindowBase::moveToSideBar(DockWidgetBase *dw, SideBarLocation location)
{
if (dw->isPersistentCentralDockWidget())
return;
if (SideBar *sb = sideBar(location)) {
QScopedValueRollback<bool> rollback(dw->d->m_isMovingToSideBar, true);
dw->forceClose();
@@ -479,7 +527,7 @@ void MainWindowBase::restoreFromSideBar(DockWidgetBase *dw)
void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
{
if (!dw)
if (!dw || dw->isPersistentCentralDockWidget())
return;
const SideBar *sb = sideBarForDockWidget(dw);
@@ -718,3 +766,26 @@ QRect MainWindowBase::windowGeometry() const
return window()->geometry();
}
void MainWindowBase::setPersistentCentralWidget(QWidgetOrQuick *widget)
{
if (!d->supportsPersistentCentralWidget()) {
qWarning() << "MainWindow::setPersistentCentralWidget() requires MainWindowOption_HasCentralWidget";
return;
}
auto dw = d->m_persistentCentralDockWidget;
if (dw) {
dw->setWidget(widget);
} else {
qWarning() << Q_FUNC_INFO << "Unexpected null central dock widget";
}
}
QWidgetOrQuick *MainWindowBase::persistentCentralWidget() const
{
if (auto dw = d->m_persistentCentralDockWidget)
return dw->widget();
return nullptr;
}

View File

@@ -89,6 +89,21 @@ public:
KDDockWidgets::DockWidgetBase *relativeTo = nullptr,
KDDockWidgets::InitialOption initialOption = {});
/**
* @brief Sets a persistent central widget. It can't be detached.
*
* Requires passing MainWindowOption_HasCentralWidget in the CTOR.
* This is similar to the central frame concept of MainWindowOption_HasCentralFrame,
* with the difference that it won't show a tabs.
*
* @param widget The QWidget (or QQuickItem if built with QtQuick support) that you
* want to set.
*
* Example: kddockwidgets_example --central-widget
*/
Q_INVOKABLE void setPersistentCentralWidget(QWidgetOrQuick *widget);
QWidgetOrQuick *persistentCentralWidget() const;
/**
* @brief Returns the unique name that was passed via constructor.
* Used internally by the save/restore mechanism.

View File

@@ -148,6 +148,7 @@ public:
QAction *const toggleAction;
QAction *const floatAction;
LastPositions m_lastPositions;
bool m_isPersistentCentralDockWidget = false;
bool m_processingToggleAction = false;
bool m_updatingToggleAction = false;
bool m_updatingFloatAction = false;

View File

@@ -31,7 +31,16 @@ Frame *DropAreaWithCentralFrame::createCentralFrame(MainWindowOptions options)
{
Frame *frame = nullptr;
if (options & MainWindowOption_HasCentralFrame) {
frame = Config::self().frameworkWidgetFactory()->createFrame(nullptr, FrameOptions() | FrameOption_IsCentralFrame | FrameOption_AlwaysShowsTabs);
FrameOptions frameOptions = FrameOption_IsCentralFrame;
const bool hasPersistentCentralWidget = (options & MainWindowOption_HasCentralWidget) == MainWindowOption_HasCentralWidget;
if (hasPersistentCentralWidget) {
frameOptions |= FrameOption_NonDockable;
} else {
// With a persistent central widget we don't allow detaching it
frameOptions |= FrameOption_AlwaysShowsTabs;
}
frame = Config::self().frameworkWidgetFactory()->createFrame(nullptr, frameOptions);
frame->setObjectName(QStringLiteral("central frame"));
}

View File

@@ -28,6 +28,7 @@ public:
private:
friend class MainWindowBase;
friend class Frame;
Frame *const m_centralFrame = nullptr;
};

View File

@@ -369,6 +369,18 @@ void FloatingWindow::onVisibleFrameCountChanged(int count)
setVisible(count > 0);
}
Qt::WindowState FloatingWindow::windowStateOverride() const
{
Qt::WindowState state = Qt::WindowNoState;
if (isMaximizedOverride())
state = Qt::WindowMaximized;
else if (isMinimizedOverride())
state = Qt::WindowMinimized;
return state;
}
void FloatingWindow::updateTitleBarVisibility()
{
if (m_updatingTitleBarVisibility)
@@ -446,7 +458,21 @@ bool FloatingWindow::deserialize(const LayoutSaver::FloatingWindow &fw)
{
if (dropArea()->deserialize(fw.multiSplitterLayout)) {
updateTitleBarVisibility();
show();
if (fw.normalGeometry.isValid() && !isNormalWindowState(fw.windowState)) {
// Restore QWidgetPrivate's normalGeometry (no public API in QWidget)
setNormalGeometry(fw.normalGeometry);
}
// And show it:
if (fw.windowState & Qt::WindowMaximized) {
showMaximized();
} else if (fw.windowState & Qt::WindowMinimized) {
showMinimized();
} else {
showNormal();
}
return true;
} else {
return false;
@@ -458,11 +484,13 @@ LayoutSaver::FloatingWindow FloatingWindow::serialize() const
LayoutSaver::FloatingWindow fw;
fw.geometry = geometry();
fw.normalGeometry = normalGeometry();
fw.isVisible = isVisible();
fw.multiSplitterLayout = dropArea()->serialize();
fw.screenIndex = screenNumberForWidget(this);
fw.screenSize = screenSizeForWidget(this);
fw.affinities = affinities();
fw.windowState = windowStateOverride();
auto mainWindow = qobject_cast<MainWindowBase *>(parentWidget());
fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
@@ -559,6 +587,36 @@ QMargins FloatingWindow::contentMargins() const
return { 4, 4, 4, 4 };
}
bool FloatingWindow::isMaximizedOverride() const
{
return QWidgetAdapter::isMaximized();
}
bool FloatingWindow::isMinimizedOverride() const
{
return QWidgetAdapter::isMinimized();
}
void FloatingWindow::showMaximized()
{
QWidgetAdapter::showMaximized();
}
void FloatingWindow::showNormal()
{
QWidgetAdapter::showNormal();
}
void FloatingWindow::showMinimized()
{
QWidgetAdapter::showMinimized();
}
QRect FloatingWindow::normalGeometry() const
{
return QWidgetAdapter::normalGeometry();
}
int FloatingWindow::userType() const
{
if (Frame *f = singleFrame())

View File

@@ -176,6 +176,40 @@ public:
///@brief Returns the contents margins
QMargins contentMargins() const;
///@brief Allows the user to override QWindow::isMaximized()
/// Needed to workaround window managers that don't support maximizing/minimizing Qt::Tool windows.
/// By default this just calls QWindow::isMaximized()
/// @sa QTBUG-95478
virtual bool isMaximizedOverride() const;
///@brief Allows the user to override QWindow::isMinimized()
/// Needed to workaround window managers that don't support maximizing/minimizing Qt::Tool windows.
/// By default this just calls QWindow::isMinimized()
/// @sa QTBUG-95478
virtual bool isMinimizedOverride() const;
///@brief By default equivalent to QWindow::showMaximized()
/// But allows the user to override it and workaround exotic window manager bugs
/// @sa QTBUG-95478
virtual void showMaximized();
///@brief By default equivalent to QWindow::showNormal()
/// But allows the user to override it and workaround exotic window manager bugs
/// @sa QTBUG-95478
virtual void showNormal();
///@brief By default equivalent to QWindow::showMinimized()
/// But allows the user to override it and workaround exotic window manager bugs
/// @sa QTBUG-95478
virtual void showMinimized();
///@brief By default equivalent to QWidget::normalGeometry()
/// Derived classes can implement something different here, to workaround window manager issues with Qt::Tool
/// Also useful for QtQuick to eventually preserve normal geometry upon save/restore of a maximized window. As
/// QWindow has no notion of normal geometry, so we need to implement it here.
/// @sa QTBUG-95478
virtual QRect normalGeometry() const;
///@brief Allows the user app to specify which window flags to use, instead of KDDWs default ones
///Bugs caused by this won't be supported, as the amount of combinations that could go wrong can
///be open ended
@@ -211,6 +245,7 @@ private:
bool m_updatingTitleBarVisibility = false;
QMetaObject::Connection m_layoutDestroyedConnection;
QAbstractNativeEventFilter *m_nchittestFilter = nullptr;
Qt::WindowState windowStateOverride() const;
#ifdef Q_OS_WIN
int m_lastHitTest = 0;
#endif

View File

@@ -31,6 +31,7 @@
#include "Utils_p.h"
#include "WidgetResizeHandler_p.h"
#include "MDILayoutWidget_p.h"
#include "DropAreaWithCentralFrame_p.h"
#include <QCloseEvent>
#include <QTimer>
@@ -147,7 +148,8 @@ void Frame::insertWidget(DockWidgetBase *dockWidget, int index, InitialOption ad
{
Q_ASSERT(dockWidget);
if (containsDockWidget(dockWidget)) {
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
if (!dockWidget->isPersistentCentralDockWidget())
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
return;
}
if (m_layoutItem)
@@ -652,7 +654,37 @@ Frame *Frame::deserialize(const LayoutSaver::Frame &f)
if (!f.isValid())
return nullptr;
auto frame = Config::self().frameworkWidgetFactory()->createFrame(/*parent=*/nullptr, FrameOptions(f.options));
const FrameOptions options = FrameOptions(f.options);
Frame *frame = nullptr;
const bool isPersistentCentralFrame = options & FrameOption::FrameOption_IsCentralFrame;
auto widgetFactory = Config::self().frameworkWidgetFactory();
if (isPersistentCentralFrame) {
// Don't create a new Frame if we're restoring the Persistent Central frame (the one created
// by MainWindowOption_HasCentralFrame). It already exists.
if (f.mainWindowUniqueName.isEmpty()) {
// Can happen with older serialization formats
qWarning() << Q_FUNC_INFO << "Frame is the persistent central frame but doesn't have"
<< "an associated window name";
} else {
if (MainWindowBase *mw = DockRegistry::self()->mainWindowByName(f.mainWindowUniqueName)) {
frame = mw->dropArea()->m_centralFrame;
if (!frame) {
// Doesn't happen...
qWarning() << "Main window" << f.mainWindowUniqueName << "doesn't have central frame";
}
} else {
// Doesn't happen...
qWarning() << Q_FUNC_INFO << "Couldn't find main window"
<< f.mainWindowUniqueName;
}
}
}
if (!frame)
frame = widgetFactory->createFrame(/*parent=*/nullptr, options);
frame->setObjectName(f.objectName);
for (const auto &savedDock : qAsConst(f.dockWidgets)) {
@@ -680,6 +712,9 @@ LayoutSaver::Frame Frame::serialize() const
frame.currentTabIndex = currentTabIndex();
frame.id = id(); // for coorelation purposes
if (MainWindowBase *mw = mainWindow())
frame.mainWindowUniqueName = mw->uniqueName();
for (DockWidgetBase *dock : docks)
frame.dockWidgets.push_back(dock->d->serialize());

View File

@@ -166,6 +166,12 @@ public:
return m_options & FrameOption_IsCentralFrame;
}
/// @brief Returns whether you can DND dock widgets over this frame and tab into it
bool isDockable() const
{
return !(m_options & FrameOption_NonDockable);
}
/**
* @brief whether the tab widget will always show tabs, even if there's only 1 dock widget
*

View File

@@ -212,6 +212,10 @@ struct LayoutSaver::Frame
int currentTabIndex;
QString id; // for coorelation purposes
/// Might be empty if not in a main window. Used so we don't create a frame when restoring
/// the persistent central frame, that's never deleted when restoring
QString mainWindowUniqueName;
LayoutSaver::DockWidget::List dockWidgets;
};
@@ -250,12 +254,14 @@ struct LayoutSaver::FloatingWindow
QStringList affinities;
int parentIndex = -1;
QRect geometry;
QRect normalGeometry;
int screenIndex;
QSize screenSize; // for relative-size restoring
bool isVisible = true;
// The instance that was created during a restore:
KDDockWidgets::FloatingWindow *floatingWindowInstance = nullptr;
Qt::WindowState windowState = Qt::WindowNoState;
};
struct LayoutSaver::MainWindow

View File

@@ -130,7 +130,7 @@ void TitleBar::toggleMaximized()
if (!m_floatingWindow)
return;
if (m_floatingWindow->isMaximized())
if (m_floatingWindow->isMaximizedOverride())
m_floatingWindow->showNormal();
else
m_floatingWindow->showMaximized();

View File

@@ -43,6 +43,7 @@ class DOCKS_EXPORT TitleBar : public QWidgetAdapter, public Draggable
Q_PROPERTY(bool closeButtonEnabled READ closeButtonEnabled WRITE setCloseButtonEnabled NOTIFY closeButtonEnabledChanged)
Q_PROPERTY(bool floatButtonVisible READ floatButtonVisible WRITE setFloatButtonVisible NOTIFY floatButtonVisibleChanged)
Q_PROPERTY(QString floatButtonToolTip READ floatButtonToolTip NOTIFY floatButtonToolTipChanged)
Q_PROPERTY(bool isFocused READ isFocused NOTIFY isFocusedChanged)
public:
typedef QVector<TitleBar *> List;

View File

@@ -110,6 +110,11 @@ inline bool usesUtilityWindows()
return !dontUse;
}
inline bool isNormalWindowState(Qt::WindowStates states)
{
return !(states & Qt::WindowMaximized) && !(states & Qt::WindowFullScreen);
}
inline bool usesFallbackMouseGrabber()
{
#ifdef KDDOCKWIDGETS_QTWIDGETS

View File

@@ -108,7 +108,7 @@ void ClassicIndicators::updateIndicatorsVisibility(bool visible)
// Only allow to dock to center if the affinities match
auto tabbingAllowedFunc = Config::self().tabbingAllowedFunc();
m_tabIndicatorVisible = m_innerIndicatorsVisible && windowBeingDragged && DockRegistry::self()->affinitiesMatch(m_hoveredFrame->affinities(), windowBeingDragged->affinities());
m_tabIndicatorVisible = m_innerIndicatorsVisible && windowBeingDragged && DockRegistry::self()->affinitiesMatch(m_hoveredFrame->affinities(), windowBeingDragged->affinities()) && m_hoveredFrame->isDockable();
if (m_tabIndicatorVisible && tabbingAllowedFunc) {
const DockWidgetBase::List source = windowBeingDragged->dockWidgets();
const DockWidgetBase::List target = m_hoveredFrame->dockWidgets();

View File

@@ -257,7 +257,6 @@ QSize QWidgetAdapter::minimumSize() const
QSize QWidgetAdapter::maximumSize() const
{
if (m_isWrapper) {
const auto children = childItems();
if (!children.isEmpty()) {
@@ -299,6 +298,27 @@ QRect QWidgetAdapter::geometry() const
return KDDockWidgets::Private::geometry(this);
}
QRect QWidgetAdapter::normalGeometry() const
{
// TODO: There's no such concept in QWindow, do we need to workaround for QtQuick ?
return QWidgetAdapter::geometry();
}
void QWidgetAdapter::setNormalGeometry(QRect geo)
{
if (!isTopLevel())
return;
if (QWindow *w = windowHandle()) {
if (isNormalWindowState(w->windowStates())) {
w->setGeometry(geo);
} else {
// Nothing better at this point, as QWindow doesn't have this concept
qDebug() << Q_FUNC_INFO << "TODO";
}
}
}
QRect QWidgetAdapter::rect() const
{
return QRectF(0, 0, width(), height()).toRect();
@@ -418,6 +438,14 @@ bool QWidgetAdapter::isMaximized() const
return false;
}
bool QWidgetAdapter::isMinimized() const
{
if (QWindow *w = windowHandle())
return w->windowStates() & Qt::WindowMinimized;
return false;
}
bool KDDockWidgets::QWidgetAdapter::isActiveWindow() const
{
if (QWindow *w = windowHandle())

View File

@@ -122,6 +122,8 @@ public:
virtual void setGeometry(QRect);
QRect frameGeometry() const;
QRect geometry() const;
QRect normalGeometry() const;
void setNormalGeometry(QRect);
QRect rect() const;
QPoint pos() const;
void show();
@@ -184,6 +186,7 @@ public:
return isWindow();
}
bool isMaximized() const;
bool isMinimized() const;
bool isActiveWindow() const;
Q_INVOKABLE void showMaximized();
Q_INVOKABLE void showMinimized();

View File

@@ -27,6 +27,8 @@
#include <QMouseEvent>
#include <QWindow>
#include <QtWidgets/private/qwidget_p.h>
using namespace KDDockWidgets;
QWidgetAdapter::QWidgetAdapter(QWidget *parent, Qt::WindowFlags f)
@@ -129,4 +131,18 @@ QWidget *KDDockWidgets::Private::widgetForWindow(QWindow *window)
return window->property("kddockwidgets_qwidget").value<QWidget *>();
}
void QWidgetAdapter::setNormalGeometry(QRect geo)
{
if (isNormalWindowState(windowState())) {
setGeometry(geo);
} else {
QWidgetPrivate *priv = QWidgetPrivate::get(this);
if (priv->extra && priv->extra->topextra) {
priv->topData()->normalGeometry = geo;
} else {
qWarning() << Q_FUNC_INFO << "Failing to set normal geometry";
}
}
}
LayoutGuestWidget::~LayoutGuestWidget() = default;

View File

@@ -103,6 +103,7 @@ protected:
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void closeEvent(QCloseEvent *) override;
virtual void setNormalGeometry(QRect);
virtual bool onResize(QSize newSize);
virtual void onLayoutRequest();

View File

@@ -27,6 +27,7 @@
#include <QTabBar>
#include <QHBoxLayout>
#include <QAbstractButton>
#include <QMenu>
using namespace KDDockWidgets;
@@ -38,6 +39,9 @@ TabWidgetWidget::TabWidgetWidget(Frame *parent)
setTabBar(static_cast<QTabBar *>(m_tabBar->asWidget()));
setTabsClosable(Config::self().flags() & Config::Flag_TabsHaveCloseButton);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QTabWidget::customContextMenuRequested, this, &TabWidgetWidget::showContextMenu);
// In case tabs closable is set by the factory, a tabClosedRequested() is emitted when the user presses [x]
connect(this, &QTabWidget::tabCloseRequested, this, [this](int index) {
if (DockWidgetBase *dw = dockwidgetAt(index)) {
@@ -201,3 +205,34 @@ void TabWidgetWidget::updateMargins()
m_cornerWidgetLayout->setContentsMargins(QMargins(0, 0, 2, 0) * factor);
m_cornerWidgetLayout->setSpacing(int(2 * factor));
}
void TabWidgetWidget::showContextMenu(QPoint pos)
{
if (!(Config::self().flags() & Config::Flag_AllowSwitchingTabsViaMenu))
return;
QTabBar *tabBar = QTabWidget::tabBar();
// We don't want context menu if there is only one tab
if (tabBar->count() <= 1)
return;
// Click on a tab => No menu
if (tabBar->tabAt(pos) >= 0)
return;
// Right click is allowed only on the tabs area
QRect tabAreaRect = tabBar->rect();
tabAreaRect.setWidth(this->width());
if (!tabAreaRect.contains(pos))
return;
QMenu menu(this);
for (int i = 0; i < tabBar->count(); ++i) {
QAction *action = menu.addAction(tabText(i), this, [this, i] {
setCurrentIndex(i);
});
if (i == currentIndex())
action->setDisabled(true);
}
menu.exec(mapToGlobal(pos));
}

View File

@@ -65,6 +65,10 @@ protected:
DockWidgetBase *dockwidgetAt(int index) const override;
int currentIndex() const override;
/// @brief Shows the context menu. Override to implement your own context menu.
/// By default it's used to honour Config::Flag_AllowSwitchingTabsViaMenu
virtual void showContextMenu(QPoint pos);
private:
void updateMargins();
void setupTabBarButtons();

View File

@@ -236,6 +236,8 @@ void TitleBarWidget::paintEvent(QPaintEvent *)
QPainter p(this);
QStyleOptionDockWidget titleOpt;
titleOpt.initFrom(this);
style()->drawPrimitive(QStyle::PE_Widget, &titleOpt, &p, this);
titleOpt.title = title();
titleOpt.rect = iconRect().isEmpty() ? rect().adjusted(2, 0, -buttonAreaWidth(), 0)
: rect().adjusted(iconRect().right(), 0, -buttonAreaWidth(), 0);
@@ -275,12 +277,12 @@ void TitleBarWidget::updateMaximizeButton()
{
if (auto fw = floatingWindow()) {
auto factory = Config::self().frameworkWidgetFactory();
const TitleBarButtonType iconType = fw->isMaximized() ? TitleBarButtonType::Normal
: TitleBarButtonType::Maximize;
const TitleBarButtonType iconType = fw->isMaximizedOverride() ? TitleBarButtonType::Normal
: TitleBarButtonType::Maximize;
m_maximizeButton->setIcon(factory->iconForButtonType(iconType, devicePixelRatioF()));
m_maximizeButton->setVisible(supportsMaximizeButton());
m_maximizeButton->setToolTip(fw->isMaximized() ? tr("Restore") : tr("Maximize"));
m_maximizeButton->setToolTip(fw->isMaximizedOverride() ? tr("Restore") : tr("Maximize"));
} else {
m_maximizeButton->setVisible(false);
}

View File

@@ -33,12 +33,12 @@ option(KDDockWidgets_FUZZER "Builds the fuzzer" ON)
# tst_docks
set(TESTING_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_resources.qrc)
add_executable(tst_docks tst_docks.cpp ${TESTING_SRCS} ${TESTING_RESOURCES})
target_link_libraries(tst_docks kddockwidgets Qt${QT_MAJOR_VERSION}::Test)
target_link_libraries(tst_docks kddockwidgets Qt${Qt_VERSION_MAJOR}::Test)
set_compiler_flags(tst_docks)
if(NOT ${PROJECT_NAME}_QTQUICK)
add_executable(tst_multisplitter tst_multisplitter.cpp)
target_link_libraries(tst_multisplitter kddockwidgets Qt${QT_MAJOR_VERSION}::Test)
target_link_libraries(tst_multisplitter kddockwidgets Qt${Qt_VERSION_MAJOR}::Test)
set_compiler_flags(tst_multisplitter)
if(KDDockWidgets_FUZZER)
add_subdirectory(fuzzer)
@@ -47,4 +47,4 @@ endif()
# tests_launcher
add_executable(tests_launcher tests_launcher.cpp)
target_link_libraries(tests_launcher Qt${QT_MAJOR_VERSION}::Core)
target_link_libraries(tests_launcher Qt${Qt_VERSION_MAJOR}::Core)

View File

@@ -12,5 +12,5 @@
add_executable(fuzzer main.cpp Fuzzer.cpp Operations.cpp ../Testing.cpp)
set_property(TARGET fuzzer PROPERTY CXX_STANDARD 17)
target_link_libraries(fuzzer kddockwidgets Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Test)
target_link_libraries(fuzzer kddockwidgets Qt${Qt_VERSION_MAJOR}::Widgets Qt${Qt_VERSION_MAJOR}::Test)
set_compiler_flags(fuzzer)

View File

@@ -27,9 +27,9 @@ using namespace KDDockWidgets;
using namespace KDDockWidgets::Testing;
using namespace KDDockWidgets::Testing::Operations;
static QString operationTypeStr(OperationType ot)
static QString operationTypeStr(OperationType optype)
{
return QMetaEnum::fromType<OperationType>().valueToKey(ot);
return QMetaEnum::fromType<OperationType>().valueToKey(optype);
}
OperationBase::OperationBase(KDDockWidgets::Testing::Operations::OperationType type, Fuzzer *fuzzer)

View File

@@ -619,6 +619,43 @@ void TestDocks::tst_restoreMaximizedState()
QCOMPARE(m->windowHandle()->windowState(), Qt::WindowMaximized);
}
void TestDocks::tst_restoreFloatingMaximizedState()
{
EnsureTopLevelsDeleted e;
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_TitleBarHasMaximizeButton);
auto dock1 = createDockWidget("dock1", new MyWidget("one"));
const QRect originalNormalGeometry = dock1->floatingWindow()->normalGeometry();
dock1->floatingWindow()->showMaximized();
qDebug() << originalNormalGeometry;
QCOMPARE(dock1->floatingWindow()->windowHandle()->windowState(), Qt::WindowMaximized);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
saver.restoreLayout(saved);
QCOMPARE(dock1->floatingWindow()->windowHandle()->windowState(), Qt::WindowMaximized);
QCOMPARE(dock1->floatingWindow()->normalGeometry(), originalNormalGeometry);
dock1->floatingWindow()->showNormal();
QCOMPARE(dock1->floatingWindow()->normalGeometry(), originalNormalGeometry);
}
void TestDocks::tst_restoreFloatingMinimizedState()
{
EnsureTopLevelsDeleted e;
auto dock1 = createDockWidget("dock1", new MyWidget("one"));
dock1->floatingWindow()->showMinimized();
QCOMPARE(dock1->floatingWindow()->windowHandle()->windowState(), Qt::WindowMinimized);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
saver.restoreLayout(saved);
QCOMPARE(dock1->floatingWindow()->windowHandle()->windowState(), Qt::WindowMinimized);
}
void TestDocks::tst_setFloatingSimple()
{
EnsureTopLevelsDeleted e;
@@ -3709,6 +3746,23 @@ void TestDocks::tst_restoreSideBySide()
}
}
void TestDocks::tst_restoreWithCentralFrameWithTabs()
{
auto m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralFrame, "tst_restoreTwice");
auto dock1 = createDockWidget("1", new QPushButton("1"));
auto dock2 = createDockWidget("2", new QPushButton("2"));
m->addDockWidgetAsTab(dock1);
m->addDockWidgetAsTab(dock2);
QCOMPARE(DockRegistry::self()->frames().size(), 1);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
QVERIFY(saver.restoreLayout(saved));
QCOMPARE(DockRegistry::self()->frames().size(), 1);
}
void TestDocks::tst_restoreWithPlaceholder()
{
// Float dock1, save and restore, then unfloat and see if dock2 goes back to where it was
@@ -5083,6 +5137,7 @@ void TestDocks::tst_restoreSideBar()
serialized = saver.serializeLayout();
m1.reset();
delete fw1;
}
@@ -5199,6 +5254,7 @@ void TestDocks::tst_sidebarOverlayGetsHiddenOnClick()
Tests::clickOn(widget2->mapToGlobal(widget2->rect().bottomLeft() + QPoint(5, -5)), widget2);
QVERIFY(!dw1->isOverlayed());
m1.reset();
delete dw1;
}
@@ -5218,8 +5274,6 @@ void TestDocks::tst_sidebarOverlayGetsHiddenOnClick()
const QPoint localPt(100, 250);
Tests::clickOn(m1->mapToGlobal(m1->rect().topLeft() + localPt), m1->childAt(localPt));
QVERIFY(!dw1->isOverlayed());
delete dw1;
}
}
@@ -7322,3 +7376,25 @@ void TestDocks::tst_closeTabOfCentralFrame()
dock1->close();
QVERIFY(frame->QWidgetAdapter::isVisible());
}
void TestDocks::tst_persistentCentralWidget()
{
EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(500, 500), MainWindowOption_HasCentralWidget);
auto dockwidgets = m->dropArea()->dockWidgets();
QCOMPARE(dockwidgets.size(), 1);
auto dw = dockwidgets.constFirst();
dw->close();
QVERIFY(dw->isOpen());
QVERIFY(dw->isPersistentCentralDockWidget());
dw->setFloating(true);
QVERIFY(!dw->isFloating());
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
QVERIFY(!saved.isEmpty());
QVERIFY(saver.restoreLayout(saved));
}

View File

@@ -91,6 +91,8 @@ private Q_SLOTS:
void tst_restoreEmpty();
void tst_restoreCentralFrame();
void tst_restoreMaximizedState();
void tst_restoreFloatingMaximizedState();
void tst_restoreFloatingMinimizedState();
void tst_shutdown();
void tst_closeDockWidgets();
void tst_layoutEqually();
@@ -104,6 +106,7 @@ private Q_SLOTS:
void tst_restoreNestedAndTabbed();
void tst_restoreCrash();
void tst_restoreSideBySide();
void tst_restoreWithCentralFrameWithTabs();
void tst_restoreWithPlaceholder();
void tst_restoreWithAffinity();
void tst_marginsAfterRestore();
@@ -238,6 +241,7 @@ private Q_SLOTS:
void tst_addMDIDockWidget();
void tst_redockToMDIRestoresPosition();
void tst_persistentCentralWidget();
#ifdef KDDOCKWIDGETS_QTWIDGETS
// TODO: Port these to QtQuick