Compare commits

...

44 Commits

Author SHA1 Message Date
Renato Araujo Oliveira Filho
1e7a2110b1 Fix python bindings build
Task-Id: KDABCI-692
2021-01-18 11:58:42 -03:00
Sergio Martins
243146fe49 Update ChangeLog 2021-01-17 23:24:58 +00:00
Sergio Martins
370d139dfc Fix Flag_NativeTitleBar
It shouldn't use the FloatingWindow::isInDragArea() code path
2021-01-17 23:11:41 +00:00
Sergio Martins
c4e7db34a4 Minor readability improvement 2021-01-17 22:51:37 +00:00
Sergio Martins
a4f6b72157 Fix FloatingWindow::isInDragArea() returning false even with HTCAPTION
If the last clicked position is HTCAPTION then we're sure we're
dragging. Problem with comparing the rect is that mouse events are
async, and by the time we use the mouse event pos the window is already
somewhere else. So just use HTCAPTION, which is 100% correct.

Fixes #103
2021-01-17 16:28:11 +00:00
Sergio Martins
de3ca6dba5 vscode: Remove launch.json
We'll do this per workspace file instead
2021-01-17 14:10:19 +00:00
Sergio Martins
bdfcfdd0fe Update ChangeLog 2021-01-16 22:39:11 +00:00
Sergio Martins
dbf357ce66 Fix restoring window maximized state
Fixes #81
2021-01-16 22:37:30 +00:00
Sergio Martins
e950cf7fc3 tests: Add a test for issue #81 2021-01-16 21:38:36 +00:00
Sergio Martins
8dfad1c910 vscode: Update the workspace files
Now autogenerated from CMakePresets.json.
Added the Qt6 one too.
2021-01-16 19:40:36 +00:00
Sergio Martins
107d4e973c Remove a script I'm no longer using 2021-01-16 15:50:27 +00:00
Sergio Martins
e33766a4fa examples: Illustrate Config::Flag_CloseOnlyCurrentTab 2021-01-16 13:19:37 +00:00
Sergio Martins
6a550c6c4a Added Config::Flag_CloseOnlyCurrentTab
When clicking on the TitleBar's close button it closes all the
dock widgets in the tab, by default. With the new option it will
close only the current tab
2021-01-16 13:06:29 +00:00
Sergio Martins
a0fc2a8ce7 Minor logic readability fix
They are mutually exclusive
2021-01-16 12:00:43 +00:00
Sergio Martins
ab257b3468 float docked widgets that have LayoutSaverOption::Skip before restore 2021-01-15 21:30:38 +00:00
Sergio Martins
e6bdb28484 Added convenience DockWidgetBase::skipsRestore() 2021-01-15 20:17:45 +00:00
Sergio Martins
c52446f3b0 Merge branch '1.2' 2021-01-15 09:27:17 +00:00
Sergio Martins
9cadfb26d9 Fix toggleAction() when dock widget is in the sidebar
It should toggle the overlay, not dock the widget

Fixes #139
2021-01-15 09:24:11 +00:00
Sergio Martins
7d5ea8f908 tests++ 2021-01-15 09:20:58 +00:00
Sergio Martins
161b33370e Add more tests 2021-01-15 09:04:05 +00:00
Sergio Martins
8dd7a90b34 Add convenience DockWidgetBase::isInSideBar() 2021-01-15 08:52:17 +00:00
Sergio Martins
7ff865a36f tests: Add test for #139 2021-01-15 08:44:21 +00:00
Sergio Martins
d0b8ee606a Honour LayoutSaverOption::Skip for floating windows with many dock widgets
If all dock widgets in the floating window have the Skip flag then the
FloatingWindow can too
2021-01-14 18:33:29 +00:00
Sergio Martins
c6c3b16fd2 LayoutSaver: Add some utility functions 2021-01-14 18:11:29 +00:00
Sergio Martins
64a6cbb2e4 Add all|anyDockWidgetsHave overloads taking a LayoutSaverOption 2021-01-14 18:01:54 +00:00
Sergio Martins
e1cf532437 tests: Add an XFAIL for a LayoutSaverOption::Skip case 2021-01-14 17:57:37 +00:00
Sergio Martins
67f2127710 Add one more unit-test regarding DeleteOnClose 2021-01-14 17:51:02 +00:00
Sergio Martins
9111b424a1 TitleBar: Also update the auto/hide button
For completeness, doesn't mean it's actually needed.
For issue #137.
2021-01-14 11:39:15 +00:00
Sergio Martins
772b51216f Merge branch '1.2' 2021-01-13 23:15:03 +00:00
Sergio Martins
a79a2f5ecb Fix restoring non-closable state
For issue #137

A simpler solution than on master, doesn't include the refactoring.
2021-01-13 23:13:39 +00:00
Sergio Martins
585c0d64ed Fix close button enable state not being restored with LayoutSaver
Fixes #137
2021-01-13 23:07:49 +00:00
Sergio Martins
7ddb95a417 Add a single TitleBar::updateButtons()
updateCloseButton() can now be private
2021-01-13 23:07:49 +00:00
Sergio Martins
02648eb54e Update ChangeLog re PySide6 2021-01-13 22:51:18 +00:00
Sergio Martins
54a1050fbb Add unit-test for issue #137 2021-01-13 22:23:44 +00:00
Sergio Martins
f997b2d2f0 qtquick: Fix build 2021-01-13 13:31:33 +00:00
Sergio Martins
8f61e57b57 Add DockWidget::Option::Option_DeleteOnClose 2021-01-13 12:54:18 +00:00
Sergio Martins
cfcff6f2d7 Minor rename 2021-01-12 22:49:07 +00:00
Sergio Martins
44d7cc0588 Add FloatingWindow::allDockWidgetsHave(option) and the any* variant 2021-01-12 22:42:38 +00:00
Sergio Martins
c91275d091 vscode: Use workspace files instead of project settings
So we can build for QtQuick too with different settings
2021-01-12 15:20:29 +00:00
Sergio Martins
69c88919c0 cmake: Export a compile_commands.json which helps vscode
Need to disable unity build otherwise intellisense can't find
our source files in the compile_commands.json. Only for the dev-*
presets anyway
2021-01-12 14:25:11 +00:00
Sergio Martins
e0e6f55868 Introduce DockWidgetBase::LayoutSaverOptions enum
The first enumerator is "Skip", meaning the dock widget won't
be affected by save/restore. It won't disappear while restoring,
and won't be shown if already hidden. (only applies to floating widgets)
2021-01-11 23:33:32 +00:00
Sergio Martins
7698584ee0 Fix potential invalid index when restoring layout
We save the index of each FloatingWindow when saving a layout,
but when restoring we might not want to restore all FloatingWindows,
for example, if we use a LayoutSaver with another affinity.

So, the index in Position::deserialize() should be a index to
LayoutSaver::Layout::floatingWindows, and not to DockRegistry::self()->floatingWindows()
since the later might be smaller.
2021-01-11 21:55:47 +00:00
Sergio Martins
d034722ba9 LayoutSaver: Add some utilities 2021-01-11 21:16:02 +00:00
Sergio Martins
e1e07c95ba Merge pull request #134 from KDAB/python6
Added PySide6 support
2021-01-11 19:35:53 +00:00
34 changed files with 911 additions and 314 deletions

68
.vscode/launch.json vendored
View File

@@ -1,68 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) QtWidgets example",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/kddockwidgets_example",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) QtQuick example",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/kddockwidgets_example_quick",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "(vs) kddockwidgets_example",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/kddockwidgets_example.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"sourceFileMap" : {
"C:\\Users\\qt\\work\\qt\\qtbase\\src" : "D:\\Qt\\5.14.2\\Src\\qtbase\\src"
}
},
{
"name": "(vs) tst_docks",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/tst_docks.exe",
"args": ["tst_moreTitleBarCornerCases", "-platform", "windows"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false
}
]
}

10
.vscode/settings.json vendored
View File

@@ -1,10 +0,0 @@
{
"C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools",
"files.associations": {
"qevent": "cpp"
},
"cmake.configureSettings": {
"KDDockWidgets_DEVELOPER_MODE" : "ON",
"KDDockWidgets_QTQUICK" : true
}
}

View File

@@ -9,7 +9,9 @@
"cacheVariables": { "cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug", "CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_DEVELOPER_MODE": "ON", "KDDockWidgets_DEVELOPER_MODE": "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'" "ECM_ENABLE_SANITIZERS" : "'address;undefined'",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"KDDockWidgets_UNITY_BUILD" : "OFF"
} }
}, },
{ {
@@ -53,7 +55,9 @@
"CMAKE_BUILD_TYPE": "Debug", "CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_QTQUICK": "ON", "KDDockWidgets_QTQUICK": "ON",
"KDDockWidgets_DEVELOPER_MODE": "ON", "KDDockWidgets_DEVELOPER_MODE": "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'" "ECM_ENABLE_SANITIZERS" : "'address;undefined'",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"KDDockWidgets_UNITY_BUILD" : "OFF"
} }
}, },
{ {
@@ -109,6 +113,8 @@
"CMAKE_BUILD_TYPE": "Debug", "CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_QT6": "ON", "KDDockWidgets_QT6": "ON",
"KDDockWidgets_DEVELOPER_MODE": "ON", "KDDockWidgets_DEVELOPER_MODE": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"KDDockWidgets_UNITY_BUILD" : "OFF",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'" "ECM_ENABLE_SANITIZERS" : "'address;undefined'"
}, },
"environment": { "environment": {

View File

@@ -4,9 +4,16 @@
- The enum KDDockWidgets::AddingOption has been deprecated, use - The enum KDDockWidgets::AddingOption has been deprecated, use
KDDockWidgets::InitialVisibilityOption instead KDDockWidgets::InitialVisibilityOption instead
- You can now pass a preferred initial size to MainWindow::addDockWidget() (#95) - You can now pass a preferred initial size to MainWindow::addDockWidget() (#95)
- Added DockWidgetBase::Option_DeleteOnClose
- Added Config::Flag_CloseOnlyCurrentTab
- PySide6 support
- Layout restorer now restores maximzied/minimized state too (#81)
- Fixed dock indicators sometimes not appearing on Windows (#103)
- Fixed Flag_NativeTitleBar not working
* v1.2.1 (unreleased) * v1.2.1 (unreleased)
- Support for resizing dock widgets when they are in overlay/popup mode (autohide/sidebar feature) - Support for resizing dock widgets when they are in overlay/popup mode (autohide/sidebar feature)
- Fixed title bar close button enabled state not being restored with Layout saver (#137)
* v1.2.0 (17 December 2020) * v1.2.0 (17 December 2020)
- Wayland support - Wayland support

View File

@@ -0,0 +1,42 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"C_Cpp.default.compileCommands": "${workspaceFolder}/build-dev-qtquick/compile_commands.json",
"C_Cpp.default.cStandard": "c17",
"files.trimTrailingWhitespace": true
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "cmake",
"command": "cmake",
"args": [
"--preset=dev-qtquick"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build"
},
{
"type": "shell",
"label": "make",
"command": "cmake",
"args": [
"--build",
"${workspaceFolder}/build-dev-qtquick"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build"
}
]
}
}

View File

@@ -0,0 +1,42 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"C_Cpp.default.compileCommands": "${workspaceFolder}/build-dev-qtwidgets/compile_commands.json",
"C_Cpp.default.cStandard": "c17",
"files.trimTrailingWhitespace": true
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "cmake",
"command": "cmake",
"args": [
"--preset=dev-qtwidgets"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build"
},
{
"type": "shell",
"label": "make",
"command": "cmake",
"args": [
"--build",
"${workspaceFolder}/build-dev-qtwidgets"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build"
}
]
}
}

View File

@@ -0,0 +1,48 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"C_Cpp.default.compileCommands": "${workspaceFolder}/build-dev-qtwidgets6/compile_commands.json",
"C_Cpp.default.cStandard": "c17",
"files.trimTrailingWhitespace": true
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "cmake",
"command": "cmake",
"args": [
"--preset=dev-qtwidgets6"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build",
"problemMatcher": [
"$gcc"
]
},
{
"type": "shell",
"label": "make",
"command": "cmake",
"args": [
"--build",
"${workspaceFolder}/build-dev-qtwidgets6"
],
"options": {
"cwd": "${workspaceFolder}/"
},
"group": "build",
"problemMatcher": [
"$gcc"
]
}
]
}
}

View File

@@ -1,157 +0,0 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-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.
*/
/**
* This is an helper script which simply reads CMakePresets.json and builds those
* presets. It's just for quickly checking that every supported setup builds
* without having to wait for CI (or in case you don't have access to KDAB CI)
*
* Usage:
* $ dart build-all.dart <kddw-source-directory> [--unity] [--tests]
*/
import 'dart:io';
import 'dart:convert';
String s_sourceDirectory = "";
bool s_testUnityVariations = false;
bool s_runTests = true;
class Preset {
final String name;
final String buildDir;
Preset.fromJson(var jsonData)
: name = jsonData['name'],
buildDir = jsonData['binaryDir'] {
}
String buildDirectory() {
return buildDir.replaceAll("\${sourceDir}", s_sourceDirectory);
}
List<String> cmakeConfigArguments(bool isUnityBuild) {
return [
"-G",
"Ninja",
"-B",
buildDirectory(),
"-S",
s_sourceDirectory,
"--preset=" + name,
'-DKDDockWidgets_UNITY_BUILD=${isUnityBuild ? "ON" : "OFF"}'
];
}
List<String> cmakeBuildArguments() {
return ["--build", buildDirectory()];
}
// Builds twice. One with unity build and one without.
Future<bool> build() async {
if (!await buildSingle(true)) return false;
if (s_testUnityVariations) if (!await buildSingle(false)) return false;
if (s_runTests && !await runTests()) {
return false;
}
return true;
}
Future<bool> buildSingle(bool isUnityBuild) async {
if (!await runCMake(cmakeConfigArguments(isUnityBuild))) {
return false;
}
if (!await runCMake(cmakeBuildArguments())) {
return false;
}
return true;
}
Future<bool> runTests() async {
print("Running: ctest");
final savedCwd = Directory.current;
Directory.current = buildDirectory();
ProcessResult result = await Process.run('ctest', ["-j8"]);
Directory.current = savedCwd;
if (result.exitCode != 0) {
print(result.stdout);
print(result.stderr);
return false;
}
return true;
}
}
/// Returns the contents of the CMakePresets.json file
String cmakePresetsJson(presetsFile) {
var file = File(presetsFile);
if (!file.existsSync()) {
throw Exception('Not existent file');
}
return file.readAsStringSync();
}
List<Preset> readPresets(var presetsFile) {
var presets = List<Preset>();
final jsonData = jsonDecode(cmakePresetsJson(presetsFile));
for (var presetData in jsonData['configurePresets']) {
presets.add(Preset.fromJson(presetData));
}
return presets;
}
Future<bool> runCMake(var cmd) async {
print("Running: cmake " + cmd.join(' '));
ProcessResult result = await Process.run('cmake', cmd);
if (result.exitCode != 0) {
print(result.stdout);
print(result.stderr);
return false;
}
return true;
}
Future<int> main(List<String> arguments) async {
if (arguments.length == 0) {
print("Usage: build-all.dart <src-directory> [--unity] [--tests]");
return 1;
}
s_sourceDirectory = arguments[0];
s_testUnityVariations = arguments.contains("--unity");
s_runTests = arguments.contains("--tests");
final presetsFile = s_sourceDirectory + '/CMakePresets.json';
if (FileSystemEntity.typeSync(presetsFile) == FileSystemEntityType.notFound) {
print('ERROR: CMakePresets.json file not found in the source directory');
return 1;
}
var presets = readPresets(presetsFile);
for (var preset in presets) {
if (preset.name == 'python')
continue; // TODO: blacklisted as it's not building on my setup yet
if (!await preset.build()) {
return 1;
}
}
print("Success!!");
return 0;
}

View File

@@ -52,13 +52,14 @@ static MyWidget *newMyWidget()
MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options, MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative, bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
bool maxSizeForDockWidget8, bool maxSizeForDockWidget8, bool dockwidget5DoesntCloseBeforeRestore,
const QString &affinityName, QWidget *parent) const QString &affinityName, QWidget *parent)
: MainWindow(uniqueName, options, parent) : MainWindow(uniqueName, options, parent)
, m_dockWidget0IsNonClosable(dockWidget0IsNonClosable) , m_dockWidget0IsNonClosable(dockWidget0IsNonClosable)
, m_dockWidget9IsNonDockable(nonDockableDockWidget9) , m_dockWidget9IsNonDockable(nonDockableDockWidget9)
, m_restoreIsRelative(restoreIsRelative) , m_restoreIsRelative(restoreIsRelative)
, m_maxSizeForDockWidget8(maxSizeForDockWidget8) , m_maxSizeForDockWidget8(maxSizeForDockWidget8)
, m_dockwidget5DoesntCloseBeforeRestore(dockwidget5DoesntCloseBeforeRestore)
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
qsrand(time(nullptr)); qsrand(time(nullptr));
@@ -164,13 +165,18 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
// Passing options is optional, we just want to illustrate Option_NotClosable here // Passing options is optional, we just want to illustrate Option_NotClosable here
KDDockWidgets::DockWidget::Options options = KDDockWidgets::DockWidget::Option_None; KDDockWidgets::DockWidget::Options options = KDDockWidgets::DockWidget::Option_None;
KDDockWidgets::DockWidget::LayoutSaverOptions layoutSaverOptions = KDDockWidgets::DockWidget::LayoutSaverOption::None;
if (count == 0 && m_dockWidget0IsNonClosable) if (count == 0 && m_dockWidget0IsNonClosable)
options |= KDDockWidgets::DockWidget::Option_NotClosable; options |= KDDockWidgets::DockWidget::Option_NotClosable;
if (count == 9 && m_dockWidget9IsNonDockable) if (count == 9 && m_dockWidget9IsNonDockable)
options |= KDDockWidgets::DockWidget::Option_NotDockable; options |= KDDockWidgets::DockWidget::Option_NotDockable;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options); if (count == 5 && m_dockwidget5DoesntCloseBeforeRestore)
layoutSaverOptions |= KDDockWidgets::DockWidget::LayoutSaverOption::Skip;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options, layoutSaverOptions);
dock->setAffinities(affinities()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets dock->setAffinities(affinities()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
if (count == 1) if (count == 1)

View File

@@ -20,7 +20,7 @@ class MyMainWindow : public KDDockWidgets::MainWindow
public: public:
explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options, explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative, bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
bool maxSizeForDockWidget8, bool maxSizeForDockWidget8, bool dockwidget5DoesntCloseBeforeRestore,
const QString &affinityName = {}, // Usually not needed. Just here to show the feature. const QString &affinityName = {}, // Usually not needed. Just here to show the feature.
QWidget *parent = nullptr); QWidget *parent = nullptr);
~MyMainWindow() override; ~MyMainWindow() override;
@@ -33,5 +33,6 @@ private:
const bool m_dockWidget9IsNonDockable; const bool m_dockWidget9IsNonDockable;
const bool m_restoreIsRelative; const bool m_restoreIsRelative;
const bool m_maxSizeForDockWidget8; const bool m_maxSizeForDockWidget8;
const bool m_dockwidget5DoesntCloseBeforeRestore;
KDDockWidgets::DockWidget::List m_dockwidgets; KDDockWidgets::DockWidget::List m_dockwidgets;
}; };

View File

@@ -111,6 +111,14 @@ int main(int argc, char **argv)
QCommandLineOption autoHideSupport("w", QCoreApplication::translate("main", "Enables auto-hide/minimization to side-bar support")); QCommandLineOption autoHideSupport("w", QCoreApplication::translate("main", "Enables auto-hide/minimization to side-bar support"));
parser.addOption(autoHideSupport); parser.addOption(autoHideSupport);
QCommandLineOption closeOnlyCurrentTab("close-only-current-tab",
QCoreApplication::translate("main", "The title bar's close button will only close the current tab instead of all. Illustrates using Config::Flag_CloseOnlyCurrentTab"));
parser.addOption(closeOnlyCurrentTab);
QCommandLineOption dontCloseBeforeRestore("dont-close-widget-before-restore",
QCoreApplication::translate("main", "DockWidget #5 wont be closed before a restore. Illustrates LayoutSaverOption::DontCloseBeforeRestore"));
parser.addOption(dontCloseBeforeRestore);
#if defined(DOCKS_DEVELOPER_MODE) #if defined(DOCKS_DEVELOPER_MODE)
parser.addOption(centralFrame); parser.addOption(centralFrame);
@@ -177,6 +185,9 @@ int main(int argc, char **argv)
if (parser.isSet(autoHideSupport)) if (parser.isSet(autoHideSupport))
flags |= Config::Flag_AutoHideSupport; flags |= Config::Flag_AutoHideSupport;
if (parser.isSet(closeOnlyCurrentTab))
flags |= Config::Flag_CloseOnlyCurrentTab;
if (parser.isSet(noTitleBars)) if (parser.isSet(noTitleBars))
flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible; flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible;
@@ -227,6 +238,7 @@ int main(int argc, char **argv)
const bool restoreIsRelative = parser.isSet(relativeRestore); const bool restoreIsRelative = parser.isSet(relativeRestore);
const bool nonDockableDockWidget9 = parser.isSet(nonDockable); const bool nonDockableDockWidget9 = parser.isSet(nonDockable);
const bool maxSizeForDockWidget8 = parser.isSet(maxSizeOption); const bool maxSizeForDockWidget8 = parser.isSet(maxSizeOption);
const bool dontCloseDockWidget5BeforeRestore = parser.isSet(dontCloseBeforeRestore);
const bool usesMainWindowsWithAffinity = parser.isSet(multipleMainWindows); const bool usesMainWindowsWithAffinity = parser.isSet(multipleMainWindows);
#ifdef KDDOCKWIDGETS_SUPPORTS_NESTED_MAINWINDOWS #ifdef KDDOCKWIDGETS_SUPPORTS_NESTED_MAINWINDOWS
@@ -236,7 +248,8 @@ int main(int argc, char **argv)
#endif #endif
MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0, MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0,
nonDockableDockWidget9, restoreIsRelative, maxSizeForDockWidget8); nonDockableDockWidget9, restoreIsRelative, maxSizeForDockWidget8,
dontCloseDockWidget5BeforeRestore);
mainWindow.setWindowTitle("Main Window 1"); mainWindow.setWindowTitle("Main Window 1");
mainWindow.resize(1200, 1200); mainWindow.resize(1200, 1200);
mainWindow.show(); mainWindow.show();
@@ -254,7 +267,8 @@ int main(int argc, char **argv)
auto mainWindow2 = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options, auto mainWindow2 = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options,
nonClosableDockWidget0, nonDockableDockWidget9, nonClosableDockWidget0, nonDockableDockWidget9,
restoreIsRelative, maxSizeForDockWidget8, affinity); restoreIsRelative, maxSizeForDockWidget8,
dontCloseDockWidget5BeforeRestore, affinity);
if (affinity.isEmpty()) if (affinity.isEmpty())
mainWindow2->setWindowTitle("Main Window 2"); mainWindow2->setWindowTitle("Main Window 2");
else else
@@ -267,7 +281,8 @@ int main(int argc, char **argv)
const QString affinity = QStringLiteral("Inner-DockWidgets-2"); const QString affinity = QStringLiteral("Inner-DockWidgets-2");
auto dockableMainWindow = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options, auto dockableMainWindow = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options,
false, false, restoreIsRelative, false, affinity); false, false, restoreIsRelative, false,
false, affinity);
dockableMainWindow->setAffinities({ affinity }); dockableMainWindow->setAffinities({ affinity });

View File

@@ -75,4 +75,4 @@ create_python_bindings(
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY)
# install # install
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)

View File

@@ -80,6 +80,7 @@ public:
Flag_AutoHideSupport = 0x8000 | Flag_TitleBarNoFloatButton, ///< Supports minimizing dock widgets to the side-bar. Flag_AutoHideSupport = 0x8000 | Flag_TitleBarNoFloatButton, ///< Supports minimizing dock widgets to the side-bar.
///< By default it also turns off the float button, but you can remove Flag_TitleBarNoFloatButton to have both. ///< By default it also turns off the float button, but you can remove Flag_TitleBarNoFloatButton to have both.
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_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_Default = Flag_AeroSnapWithClientDecos ///< The defaults Flag_Default = Flag_AeroSnapWithClientDecos ///< The defaults
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)

View File

@@ -49,7 +49,8 @@ public:
* when visible, or stays without a parent when hidden. This allows to support docking * when visible, or stays without a parent when hidden. This allows to support docking
* to different main windows. * to different main windows.
*/ */
explicit DockWidget(const QString &uniqueName, Options options = DockWidgetBase::Options()); explicit DockWidget(const QString &uniqueName, Options options = DockWidgetBase::Options(),
LayoutSaverOptions layoutSaverOptions = LayoutSaverOptions());
///@brief destructor ///@brief destructor
~DockWidget() override; ~DockWidget() override;

View File

@@ -40,11 +40,13 @@ using namespace KDDockWidgets;
class DockWidgetBase::Private class DockWidgetBase::Private
{ {
public: public:
Private(const QString &dockName, DockWidgetBase::Options options_, DockWidgetBase *qq) Private(const QString &dockName, DockWidgetBase::Options options_,
LayoutSaverOptions layoutSaverOptions_, DockWidgetBase *qq)
: name(dockName) : name(dockName)
, title(dockName) , title(dockName)
, q(qq) , q(qq)
, options(options_) , options(options_)
, layoutSaverOptions(layoutSaverOptions_)
, toggleAction(new QAction(q)) , toggleAction(new QAction(q))
, floatAction(new QAction(q)) , floatAction(new QAction(q))
{ {
@@ -98,6 +100,11 @@ public:
return nullptr; return nullptr;
} }
SideBar* sideBar() const
{
return DockRegistry::self()->sideBarForDockWidget(q);
}
QPoint defaultCenterPosForFloating(); QPoint defaultCenterPosForFloating();
void updateTitle(); void updateTitle();
@@ -126,6 +133,7 @@ public:
QWidgetOrQuick *widget = nullptr; QWidgetOrQuick *widget = nullptr;
DockWidgetBase *const q; DockWidgetBase *const q;
DockWidgetBase::Options options; DockWidgetBase::Options options;
const LayoutSaverOptions layoutSaverOptions;
QAction *const toggleAction; QAction *const toggleAction;
QAction *const floatAction; QAction *const floatAction;
LastPositions m_lastPositions; LastPositions m_lastPositions;
@@ -135,9 +143,10 @@ public:
QSize m_lastOverlayedSize = QSize(0, 0); QSize m_lastOverlayedSize = QSize(0, 0);
}; };
DockWidgetBase::DockWidgetBase(const QString &name, Options options) DockWidgetBase::DockWidgetBase(const QString &name, Options options,
LayoutSaverOptions layoutSaverOptions)
: QWidgetAdapter(nullptr, Qt::Tool) : QWidgetAdapter(nullptr, Qt::Tool)
, d(new Private(name, options, this)) , d(new Private(name, options, layoutSaverOptions, this))
{ {
d->init(); d->init();
DockRegistry::self()->registerDockWidget(this); DockRegistry::self()->registerDockWidget(this);
@@ -343,6 +352,11 @@ DockWidgetBase::Options DockWidgetBase::options() const
return d->options; return d->options;
} }
DockWidgetBase::LayoutSaverOptions DockWidgetBase::layoutSaverOptions() const
{
return d->layoutSaverOptions;
}
void DockWidgetBase::setOptions(Options options) void DockWidgetBase::setOptions(Options options)
{ {
if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) { if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) {
@@ -354,7 +368,7 @@ void DockWidgetBase::setOptions(Options options)
d->options = options; d->options = options;
Q_EMIT optionsChanged(options); Q_EMIT optionsChanged(options);
if (auto tb = titleBar()) if (auto tb = titleBar())
tb->updateCloseButton(); tb->updateButtons();
} }
} }
@@ -523,6 +537,11 @@ SideBarLocation DockWidgetBase::sideBarLocation() const
return DockRegistry::self()->sideBarLocationForDockWidget(this); return DockRegistry::self()->sideBarLocationForDockWidget(this);
} }
bool DockWidgetBase::isInSideBar() const
{
return sideBarLocation() != SideBarLocation::None;
}
bool DockWidgetBase::hasPreviousDockedLocation() const bool DockWidgetBase::hasPreviousDockedLocation() const
{ {
return d->m_lastPositions.isValid(); return d->m_lastPositions.isValid();
@@ -538,6 +557,11 @@ DockWidgetBase *DockWidgetBase::byName(const QString &uniqueName)
return DockRegistry::self()->dockByName(uniqueName); return DockRegistry::self()->dockByName(uniqueName);
} }
bool DockWidgetBase::skipsRestore() const
{
return d->layoutSaverOptions & LayoutSaverOption::Skip;
}
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow() FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
{ {
if (auto fw = floatingWindow()) if (auto fw = floatingWindow())
@@ -645,12 +669,18 @@ void DockWidgetBase::Private::updateTitle()
void DockWidgetBase::Private::toggle(bool enabled) void DockWidgetBase::Private::toggle(bool enabled)
{ {
if (SideBar *sb = sideBar()) {
// The widget is in the sidebar, let's toggle its overlayed state
sb->toggleOverlay(q);
} else {
// The most common case. The dock widget is not in the sidebar. just close or open it.
if (enabled) { if (enabled) {
show(); show();
} else { } else {
q->close(); q->close();
} }
} }
}
void DockWidgetBase::Private::updateToggleAction() void DockWidgetBase::Private::updateToggleAction()
{ {
@@ -708,6 +738,9 @@ void DockWidgetBase::Private::close()
sb->removeDockWidget(q); sb->removeDockWidget(q);
} }
} }
if (options & DockWidgetBase::Option_DeleteOnClose)
q->deleteLater();
} }
bool DockWidgetBase::Private::restoreToPreviousPosition() bool DockWidgetBase::Private::restoreToPreviousPosition()

View File

@@ -75,10 +75,19 @@ public:
enum Option { enum Option {
Option_None = 0, ///< No option, the default Option_None = 0, ///< No option, the default
Option_NotClosable = 1, ///< The DockWidget can't be closed on the [x], only programatically Option_NotClosable = 1, ///< The DockWidget can't be closed on the [x], only programatically
Option_NotDockable = 2 ///< The DockWidget can't be docked, it's always floating Option_NotDockable = 2, ///< The DockWidget can't be docked, it's always floating
Option_DeleteOnClose = 4 ///< Deletes the DockWidget when closed
}; };
Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_FLAGS(Options, Option)
/// @brief Options which will affect LayoutSaver save/restore
enum class LayoutSaverOption {
None = 0, ///< Just use the defaults
Skip = 1, ///< The dock widget won't participate in save/restore. Currently only available for floating windows.
};
Q_DECLARE_FLAGS(LayoutSaverOptions, LayoutSaverOption)
enum class IconPlace { enum class IconPlace {
TitleBar = 1, TitleBar = 1,
TabBar = 2, TabBar = 2,
@@ -91,12 +100,15 @@ public:
/** /**
* @brief constructs a new DockWidget * @brief constructs a new DockWidget
* @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text. * @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text.
* @param options optional options controlling behaviour * @param options options controlling certain behaviours
* @param layoutSaverOptions options to control save/restore
* *
* There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow * There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow
* when visible, or stays without a parent when hidden. * when visible, or stays without a parent when hidden.
*/ */
explicit DockWidgetBase(const QString &uniqueName, Options options = DockWidgetBase::Options()); explicit DockWidgetBase(const QString &uniqueName,
Options options = DockWidgetBase::Options(),
LayoutSaverOptions layoutSaverOptions = LayoutSaverOptions());
///@brief destructor ///@brief destructor
~DockWidgetBase() override; ~DockWidgetBase() override;
@@ -198,6 +210,10 @@ public:
*/ */
Options options() const; Options options() const;
/// @brief returns the per-dockwidget options which will affect LayoutSaver
/// These are the options which were passed to the constructor
KDDockWidgets::DockWidgetBase::LayoutSaverOptions layoutSaverOptions() const;
/** /**
* @brief Setter for the options. * @brief Setter for the options.
* Only Option_NotClosable is allowed to change after construction. For the other options use * Only Option_NotClosable is allowed to change after construction. For the other options use
@@ -322,7 +338,7 @@ public:
bool isMainWindow() const; bool isMainWindow() const;
/** /**
* @brief Returns whether this dock widget is docked into a main window. * @brief Returns whether this dock widget is docked into a main window (as opposed to floating)
* *
* Note that isFloating() returning false might either mean the dock widget is docked into a * Note that isFloating() returning false might either mean the dock widget is docked into a
* main window or into a floating window (groupped/nested with other dock widgets. Use this function * main window or into a floating window (groupped/nested with other dock widgets. Use this function
@@ -331,6 +347,7 @@ public:
bool isInMainWindow() const; bool isInMainWindow() const;
/// @brief Returns the main window this dock widget is in. nullptr if it's not inside a main window /// @brief Returns the main window this dock widget is in. nullptr if it's not inside a main window
/// Also returns nullptr if it's minimized to a sidebar
MainWindowBase *mainWindow() const; MainWindowBase *mainWindow() const;
///@brief Returns whether This or any child of this dock widget is focused ///@brief Returns whether This or any child of this dock widget is focused
@@ -350,15 +367,22 @@ public:
*/ */
void moveToSideBar(); void moveToSideBar();
/// @brief Returns whether this dock widget is overlayed on top of the main window, instead of /// @brief Returns whether this dock widget is overlayed from the side-bar.
/// docked into the layout. This is only relevant when using the auto-hide and side-bar feature. ///
/// This is only relevant when using the auto-hide and side-bar feature.
/// Not to be confused with "floating", which means top-level window.
bool isOverlayed() const; bool isOverlayed() const;
///@brief Returns whether this dock widget is in a side bar, and which. ///@brief Returns whether this dock widget is in a side bar, and which.
/// SideBarLocation::None is returned if it's not in a sidebar. /// SideBarLocation::None is returned if it's not in a sidebar.
/// This is only relevant when using the auto-hide and side-bar feature. /// This is only relevant when using the auto-hide and side-bar feature.
/// @sa isInSideBar
SideBarLocation sideBarLocation() const; SideBarLocation sideBarLocation() const;
/// @brief Returns where this dockwidget is in a sidebar
/// Similar to sideBarLocation(), but returns a bool
bool isInSideBar() const;
/// @brief Returns whether this floating dock widget knows its previous docked location /// @brief Returns whether this floating dock widget knows its previous docked location
/// Result only makes sense if it's floating. /// Result only makes sense if it's floating.
/// ///
@@ -375,6 +399,9 @@ public:
/// nullptr is returned if the dock widget isn't found. /// nullptr is returned if the dock widget isn't found.
static DockWidgetBase* byName(const QString &uniqueName); static DockWidgetBase* byName(const QString &uniqueName);
/// @brief Returns whether this widget has the LayoutSaverOption::Skip flag
bool skipsRestore() const;
Q_SIGNALS: Q_SIGNALS:
#ifdef KDDOCKWIDGETS_QTWIDGETS #ifdef KDDOCKWIDGETS_QTWIDGETS
///@brief signal emitted when the parent changed ///@brief signal emitted when the parent changed

View File

@@ -40,8 +40,8 @@ public:
QQuickItem *const m_visualItem; QQuickItem *const m_visualItem;
}; };
DockWidgetQuick::DockWidgetQuick(const QString &name, Options options) DockWidgetQuick::DockWidgetQuick(const QString &name, Options options, LayoutSaverOptions layoutSaverOptions)
: DockWidgetBase(name, options) : DockWidgetBase(name, options, layoutSaverOptions)
, d(new Private(this)) , d(new Private(this))
{ {
// To mimic what QtWidgets does when creating a new QWidget. // To mimic what QtWidgets does when creating a new QWidget.

View File

@@ -45,7 +45,8 @@ public:
* There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow * There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow
* when visible, or stays without a parent when hidden. * when visible, or stays without a parent when hidden.
*/ */
explicit DockWidgetQuick(const QString &uniqueName, Options options = {}); explicit DockWidgetQuick(const QString &uniqueName, Options options = {},
LayoutSaverOptions layoutSaverOptions = LayoutSaverOptions());
///@brief destructor ///@brief destructor
~DockWidgetQuick() override; ~DockWidgetQuick() override;

View File

@@ -69,6 +69,9 @@ public:
return m_affinityNames.isEmpty() || affinities.isEmpty() || DockRegistry::self()->affinitiesMatch(m_affinityNames, affinities); return m_affinityNames.isEmpty() || affinities.isEmpty() || DockRegistry::self()->affinitiesMatch(m_affinityNames, affinities);
} }
void floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames);
template <typename T> template <typename T>
void deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel); void deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel);
void deleteEmptyFrames(); void deleteEmptyFrames();
@@ -229,9 +232,12 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
if (d->m_restoreOptions & RestoreOption_RelativeToMainWindow) if (d->m_restoreOptions & RestoreOption_RelativeToMainWindow)
layout.scaleSizes(); layout.scaleSizes();
d->floatWidgetsWhichSkipRestore(layout.mainWindowNames());
// Hide all dockwidgets and unparent them from any layout before starting restore // Hide all dockwidgets and unparent them from any layout before starting restore
// We only close the stuff that the loaded JSON knows about. Unknown widgets might be newer. // We only close the stuff that the loaded JSON knows about. Unknown widgets might be newer.
d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetNames()),
d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetsToClose()),
d->m_dockRegistry->mainWindows(layout.mainWindowNames()), d->m_dockRegistry->mainWindows(layout.mainWindowNames()),
d->m_affinityNames); d->m_affinityNames);
@@ -258,14 +264,15 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
} }
// 2. Restore FloatingWindows // 2. Restore FloatingWindows
for (const LayoutSaver::FloatingWindow &fw : qAsConst(layout.floatingWindows)) { for (LayoutSaver::FloatingWindow &fw : layout.floatingWindows) {
if (!d->matchesAffinity(fw.affinities)) if (!d->matchesAffinity(fw.affinities) || fw.skipsRestore())
continue; continue;
MainWindowBase *parent = fw.parentIndex == -1 ? nullptr MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
: DockRegistry::self()->mainwindows().at(fw.parentIndex); : DockRegistry::self()->mainwindows().at(fw.parentIndex);
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent); auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
fw.floatingWindowInstance = floatingWindow;
d->deserializeWindowGeometry(fw, floatingWindow); d->deserializeWindowGeometry(fw, floatingWindow);
if (!floatingWindow->deserialize(fw)) { if (!floatingWindow->deserialize(fw)) {
qWarning() << Q_FUNC_INFO << "Failed to deserialize floating window"; qWarning() << Q_FUNC_INFO << "Failed to deserialize floating window";
@@ -332,6 +339,24 @@ void LayoutSaver::Private::deserializeWindowGeometry(const T &saved, QWidgetOrQu
topLevel->setVisible(saved.isVisible); topLevel->setVisible(saved.isVisible);
} }
void LayoutSaver::Private::floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames)
{
// Widgets with the DockWidget::LayoutSaverOption::Skip flag skip restore completely.
// If they were visible before they need to remain visible now.
// If they were previously docked we need to float them, as the main window they were on will
// be loading a new layout.
for (MainWindowBase *mw : DockRegistry::self()->mainWindows(mainWindowNames)) {
const KDDockWidgets::DockWidgetBase::List docks = mw->multiSplitter()->dockWidgets();
for (auto dw : docks) {
if (dw->skipsRestore()) {
dw->setFloating(true);
}
}
}
}
void LayoutSaver::Private::deleteEmptyFrames() void LayoutSaver::Private::deleteEmptyFrames()
{ {
// After a restore it can happen that some DockWidgets didn't exist, so weren't restored. // After a restore it can happen that some DockWidgets didn't exist, so weren't restored.
@@ -471,6 +496,14 @@ LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
return mainWindows.at(index); return mainWindows.at(index);
} }
LayoutSaver::FloatingWindow LayoutSaver::Layout::floatingWindowForIndex(int index) const
{
if (index < 0 || index >= floatingWindows.size())
return {};
return floatingWindows.at(index);
}
QStringList LayoutSaver::Layout::mainWindowNames() const QStringList LayoutSaver::Layout::mainWindowNames() const
{ {
QStringList names; QStringList names;
@@ -493,6 +526,35 @@ QStringList LayoutSaver::Layout::dockWidgetNames() const
return names; return names;
} }
QStringList LayoutSaver::Layout::dockWidgetsToClose() const
{
// Before restoring a layout we close all dock widgets, unless they're a floating window with the DontCloseBeforeRestore flag
QStringList names;
names.reserve(allDockWidgets.size());
auto registry = DockRegistry::self();
for (const auto &dw : allDockWidgets) {
if (DockWidgetBase *dockWidget = registry->dockByName(dw->uniqueName)) {
bool doClose = true;
if (dockWidget->skipsRestore()) {
if (auto fw = dockWidget->floatingWindow()) {
if (fw->allDockWidgetsHave(DockWidgetBase::LayoutSaverOption::Skip)) {
// All dock widgets in this floating window skips float, so we can honour it for all.
doClose = false;
}
}
}
if (doClose)
names << dw->uniqueName;
}
}
return names;
}
bool LayoutSaver::Frame::isValid() const bool LayoutSaver::Frame::isValid() const
{ {
if (isNull) if (isNull)
@@ -528,6 +590,26 @@ bool LayoutSaver::Frame::isValid() const
return true; return true;
} }
bool LayoutSaver::Frame::hasSingleDockWidget() const
{
return dockWidgets.size() == 1;
}
bool LayoutSaver::Frame::skipsRestore() const
{
return std::all_of(dockWidgets.cbegin(), dockWidgets.cend(), [] (LayoutSaver::DockWidget::Ptr dw) {
return dw->skipsRestore();
});
}
LayoutSaver::DockWidget::Ptr LayoutSaver::Frame::singleDockWidget() const
{
if (!hasSingleDockWidget())
return {};
return dockWidgets.first();
}
void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo) void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo)
{ {
scalingInfo.applyFactorsTo(geometry); scalingInfo.applyFactorsTo(geometry);
@@ -583,6 +665,14 @@ void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
lastPosition.scaleSizes(scalingInfo); lastPosition.scaleSizes(scalingInfo);
} }
bool LayoutSaver::DockWidget::skipsRestore() const
{
if (DockWidgetBase *dw = DockRegistry::self()->dockByName(uniqueName))
return dw->skipsRestore();
return false;
}
QVariantMap LayoutSaver::DockWidget::toVariantMap() const QVariantMap LayoutSaver::DockWidget::toVariantMap() const
{ {
QVariantMap map; QVariantMap map;
@@ -621,6 +711,21 @@ bool LayoutSaver::FloatingWindow::isValid() const
return true; return true;
} }
bool LayoutSaver::FloatingWindow::hasSingleDockWidget() const
{
return multiSplitterLayout.hasSingleDockWidget();
}
LayoutSaver::DockWidget::Ptr LayoutSaver::FloatingWindow::singleDockWidget() const
{
return multiSplitterLayout.singleDockWidget();
}
bool LayoutSaver::FloatingWindow::skipsRestore() const
{
return multiSplitterLayout.skipsRestore();
}
void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo) void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
{ {
scalingInfo.applyFactorsTo(/*by-ref*/geometry); scalingInfo.applyFactorsTo(/*by-ref*/geometry);
@@ -698,6 +803,7 @@ QVariantMap LayoutSaver::MainWindow::toVariantMap() const
map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize)); map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
map.insert(QStringLiteral("isVisible"), isVisible); map.insert(QStringLiteral("isVisible"), isVisible);
map.insert(QStringLiteral("affinities"), stringListToVariant(affinities)); map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
map.insert(QStringLiteral("windowState"), windowState);
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) { for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc); const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc);
@@ -718,6 +824,7 @@ void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap()); screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
isVisible = map.value(QStringLiteral("isVisible")).toBool(); isVisible = map.value(QStringLiteral("isVisible")).toBool();
affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList()); 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: // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
const QString affinityName = map.value(QStringLiteral("affinityName")).toString(); const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
@@ -747,6 +854,26 @@ bool LayoutSaver::MultiSplitter::isValid() const
return true; return true;
} }
bool LayoutSaver::MultiSplitter::hasSingleDockWidget() const
{
return frames.size() == 1 && frames.cbegin()->hasSingleDockWidget();
}
LayoutSaver::DockWidget::Ptr LayoutSaver::MultiSplitter::singleDockWidget() const
{
if (!hasSingleDockWidget())
return {};
return frames.cbegin()->singleDockWidget();
}
bool LayoutSaver::MultiSplitter::skipsRestore() const
{
return std::all_of(frames.cbegin(), frames.cend(), [] (const LayoutSaver::Frame &frame) {
return frame.skipsRestore();
});
}
void LayoutSaver::MultiSplitter::scaleSizes(const ScalingInfo &) void LayoutSaver::MultiSplitter::scaleSizes(const ScalingInfo &)
{ {
// scalingInfo.applyFactorsTo(/*by-ref*/size); // scalingInfo.applyFactorsTo(/*by-ref*/size);

View File

@@ -36,6 +36,8 @@
namespace KDDockWidgets { namespace KDDockWidgets {
class FloatingWindow;
template <typename T> template <typename T>
typename T::List fromVariantList(const QVariantList &listV) typename T::List fromVariantList(const QVariantList &listV)
{ {
@@ -137,6 +139,8 @@ struct DOCKS_EXPORT LayoutSaver::DockWidget
return dw; return dw;
} }
bool skipsRestore() const;
QVariantMap toVariantMap() const; QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map); void fromVariantMap(const QVariantMap &map);
@@ -173,6 +177,12 @@ struct LayoutSaver::Frame
{ {
bool isValid() const; bool isValid() const;
bool hasSingleDockWidget() const;
bool skipsRestore() const;
/// @brief in case this frame only has one frame, returns the name of that dock widget
LayoutSaver::DockWidget::Ptr singleDockWidget() const;
/// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow. /// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo); void scaleSizes(const ScalingInfo &scalingInfo);
@@ -192,6 +202,11 @@ struct LayoutSaver::Frame
struct LayoutSaver::MultiSplitter struct LayoutSaver::MultiSplitter
{ {
bool isValid() const; bool isValid() const;
bool hasSingleDockWidget() const;
LayoutSaver::DockWidget::Ptr singleDockWidget() const;
bool skipsRestore() const;
/// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow. /// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo); void scaleSizes(const ScalingInfo &scalingInfo);
@@ -208,6 +223,10 @@ struct LayoutSaver::FloatingWindow
bool isValid() const; bool isValid() const;
bool hasSingleDockWidget() const;
LayoutSaver::DockWidget::Ptr singleDockWidget() const;
bool skipsRestore() const;
/// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow. /// Iterates through the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &); void scaleSizes(const ScalingInfo &);
@@ -221,6 +240,9 @@ struct LayoutSaver::FloatingWindow
int screenIndex; int screenIndex;
QSize screenSize; // for relative-size restoring QSize screenSize; // for relative-size restoring
bool isVisible = true; bool isVisible = true;
// The instance that was created during a restore:
KDDockWidgets::FloatingWindow *floatingWindowInstance = nullptr;
}; };
struct LayoutSaver::MainWindow struct LayoutSaver::MainWindow
@@ -245,6 +267,7 @@ public:
int screenIndex; int screenIndex;
QSize screenSize; // for relative-size restoring QSize screenSize; // for relative-size restoring
bool isVisible; bool isVisible;
Qt::WindowState windowState = Qt::WindowNoState;
ScalingInfo scalingInfo; ScalingInfo scalingInfo;
}; };
@@ -301,9 +324,11 @@ public:
static LayoutSaver::Layout* s_currentLayoutBeingRestored; static LayoutSaver::Layout* s_currentLayoutBeingRestored;
LayoutSaver::MainWindow mainWindowForIndex(int index) const; LayoutSaver::MainWindow mainWindowForIndex(int index) const;
LayoutSaver::FloatingWindow floatingWindowForIndex(int index) const;
QStringList mainWindowNames() const; QStringList mainWindowNames() const;
QStringList dockWidgetNames() const; QStringList dockWidgetNames() const;
QStringList dockWidgetsToClose() const;
int serializationVersion = KDDOCKWIDGETS_SERIALIZATION_VERSION; int serializationVersion = KDDOCKWIDGETS_SERIALIZATION_VERSION;
LayoutSaver::MainWindow::List mainWindows; LayoutSaver::MainWindow::List mainWindows;

View File

@@ -594,6 +594,12 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
} }
} }
if (mw.windowState != Qt::WindowNoState) {
if (auto w = windowHandle()) {
w->setWindowState(mw.windowState);
}
}
// Commented-out for now, we dont' want to restore the popup/overlay. popups are perishable // Commented-out for now, we dont' want to restore the popup/overlay. popups are perishable
//if (!mw.overlayedDockWidget.isEmpty()) //if (!mw.overlayedDockWidget.isEmpty())
// overlayOnSideBar(DockRegistry::self()->dockByName(mw.overlayedDockWidget)); // overlayOnSideBar(DockRegistry::self()->dockByName(mw.overlayedDockWidget));
@@ -613,6 +619,8 @@ LayoutSaver::MainWindow MainWindowBase::serialize() const
m.screenSize = screenSizeForWidget(this); m.screenSize = screenSizeForWidget(this);
m.multiSplitterLayout = dropArea()->serialize(); m.multiSplitterLayout = dropArea()->serialize();
m.affinities = d->affinities; m.affinities = d->affinities;
m.windowState = windowHandle() ? windowHandle()->windowState()
: Qt::WindowNoState;
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) { for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
if (SideBar *sb = sideBar(loc)) { if (SideBar *sb = sideBar(loc)) {

View File

@@ -605,7 +605,7 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
switch (e->type()) { switch (e->type()) {
case QEvent::NonClientAreaMouseButtonPress: { case QEvent::NonClientAreaMouseButtonPress: {
if (auto fw = qobject_cast<FloatingWindow*>(o)) { if (auto fw = qobject_cast<FloatingWindow*>(o)) {
if (fw->isInDragArea(Qt5Qt6Compat::eventGlobalPos(me))) { if (KDDockWidgets::usesNativeTitleBar() || fw->isInDragArea(Qt5Qt6Compat::eventGlobalPos(me))) {
m_nonClientDrag = true; m_nonClientDrag = true;
return activeState()->handleMouseButtonPress(draggableForQObject(o), Qt5Qt6Compat::eventGlobalPos(me), me->pos()); return activeState()->handleMouseButtonPress(draggableForQObject(o), Qt5Qt6Compat::eventGlobalPos(me), me->pos());
} }

View File

@@ -118,7 +118,7 @@ private:
Draggable::List m_draggables; Draggable::List m_draggables;
Draggable *m_draggable = nullptr; Draggable *m_draggable = nullptr;
QPointer<QWidget> m_draggableGuard; // Just so we know if the draggable was destroyed for some reason QPointer<WidgetType> m_draggableGuard; // Just so we know if the draggable was destroyed for some reason
std::unique_ptr<WindowBeingDragged> m_windowBeingDragged; std::unique_ptr<WindowBeingDragged> m_windowBeingDragged;
DropArea *m_currentDropArea = nullptr; DropArea *m_currentDropArea = nullptr;
bool m_nonClientDrag = false; bool m_nonClientDrag = false;

View File

@@ -20,6 +20,7 @@
#include "DockRegistry_p.h" #include "DockRegistry_p.h"
#include "Config.h" #include "Config.h"
#include "FrameworkWidgetFactory.h" #include "FrameworkWidgetFactory.h"
#include "DragController_p.h"
#include <QCloseEvent> #include <QCloseEvent>
#include <QAbstractNativeEventFilter> #include <QAbstractNativeEventFilter>
@@ -215,10 +216,19 @@ void FloatingWindow::setupWindow()
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS) #if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result) bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result)
{ {
if (!m_inDtor && !m_deleteScheduled && KDDockWidgets::usesAeroSnapWithCustomDecos()) { if (m_inDtor || m_deleteScheduled)
return QWidget::nativeEvent(eventType, message, result);
if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
// To enable aero snap we need to tell Windows where's our custom title bar // To enable aero snap we need to tell Windows where's our custom title bar
if (WidgetResizeHandler::handleWindowsNativeEvent(this, eventType, message, result)) if (WidgetResizeHandler::handleWindowsNativeEvent(this, eventType, message, result))
return true; return true;
} else if (KDDockWidgets::usesNativeTitleBar()) {
auto msg = static_cast<MSG *>(message);
if (msg->message == WM_SIZING) {
// Cancel any drag if we're resizing
Q_EMIT DragController::instance()->dragCanceled();
}
} }
return QWidget::nativeEvent(eventType, message, result); return QWidget::nativeEvent(eventType, message, result);
@@ -301,8 +311,8 @@ bool FloatingWindow::isInDragArea(QPoint globalPoint) const
// A click near the border will still send a Qt::NonClientMousePressEvent. We shouldn't // A click near the border will still send a Qt::NonClientMousePressEvent. We shouldn't
// interpret that as a drag, as it's for a native resize. // interpret that as a drag, as it's for a native resize.
// Keep track of how we handled the WM_NCHITTEST // Keep track of how we handled the WM_NCHITTEST
if (m_lastHitTest != 0 && m_lastHitTest != HTCAPTION) if (usesAeroSnapWithCustomDecos())
return false; return m_lastHitTest == HTCAPTION;
#endif #endif
return dragRect().contains(globalPoint); return dragRect().contains(globalPoint);
@@ -341,6 +351,14 @@ bool FloatingWindow::hasSingleDockWidget() const
return frame->dockWidgetCount() == 1; return frame->dockWidgetCount() == 1;
} }
Frame *FloatingWindow::singleFrame() const
{
const Frame::List frames = this->frames();
return frames.isEmpty() ? nullptr
: frames.first();
}
bool FloatingWindow::beingDeleted() const bool FloatingWindow::beingDeleted() const
{ {
if (m_deleteScheduled || m_inDtor) if (m_deleteScheduled || m_inDtor)
@@ -395,6 +413,8 @@ void FloatingWindow::updateTitleBarVisibility()
for (Frame *frame : frames()) for (Frame *frame : frames())
frame->updateTitleBarVisibility(); frame->updateTitleBarVisibility();
m_titleBar->updateButtons();
} else { } else {
visible = false; visible = false;
} }
@@ -501,3 +521,35 @@ bool FloatingWindow::event(QEvent *ev)
return QWidgetAdapter::event(ev); return QWidgetAdapter::event(ev);
} }
bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::Option option) const
{
const Frame::List frames = this->frames();
return std::all_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->allDockWidgetsHave(option);
});
}
bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::Option option) const
{
const Frame::List frames = this->frames();
return std::any_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->anyDockWidgetsHas(option);
});
}
bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::LayoutSaverOption option) const
{
const Frame::List frames = this->frames();
return std::all_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->allDockWidgetsHave(option);
});
}
bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption option) const
{
const Frame::List frames = this->frames();
return std::any_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->anyDockWidgetsHas(option);
});
}

View File

@@ -97,6 +97,9 @@ public:
*/ */
bool hasSingleDockWidget() const; bool hasSingleDockWidget() const;
/// @brief If this floating window has only one Frame, it's returned, otherwise nullptr
Frame* singleFrame() const;
/** /**
* @brief Returns whether a deleteLater has already been issued * @brief Returns whether a deleteLater has already been issued
*/ */
@@ -131,6 +134,18 @@ public:
*/ */
QRect dragRect() const; QRect dragRect() const;
///@brief Returns whether all dock widgets have the specified option set
bool allDockWidgetsHave(DockWidgetBase::Option) const;
///@brief Returns whether at least one dock widget has the specified option set
bool anyDockWidgetsHas(DockWidgetBase::Option) const;
///@brief Returns whether all dock widgets have the specified layout saver option set
bool allDockWidgetsHave(DockWidgetBase::LayoutSaverOption) const;
///@brief Returns whether at least one dock widget has the specified layout saver option set
bool anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption) const;
Q_SIGNALS: Q_SIGNALS:
void activatedChanged(); void activatedChanged();
void numFramesChanged(); void numFramesChanged();

View File

@@ -720,3 +720,37 @@ TabWidget *Frame::tabWidget() const
{ {
return m_tabWidget; return m_tabWidget;
} }
///@brief Returns whether all dock widgets have the specified option set
bool Frame::allDockWidgetsHave(DockWidgetBase::Option option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::all_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->options() & option;
});
}
///@brief Returns whether at least one dock widget has the specified option set
bool Frame::anyDockWidgetsHas(DockWidgetBase::Option option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::any_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->options() & option;
});
}
bool Frame::allDockWidgetsHave(DockWidgetBase::LayoutSaverOption option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::all_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->layoutSaverOptions() & option;
});
}
bool Frame::anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::any_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->layoutSaverOptions() & option;
});
}

View File

@@ -22,6 +22,7 @@
#include "kddockwidgets/docks_export.h" #include "kddockwidgets/docks_export.h"
#include "kddockwidgets/QWidgetAdapter.h" #include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/FocusScope.h" #include "kddockwidgets/FocusScope.h"
#include "kddockwidgets/DockWidgetBase.h"
#include "../LayoutSaver_p.h" #include "../LayoutSaver_p.h"
#include "multisplitter/Widget.h" #include "multisplitter/Widget.h"
@@ -239,6 +240,18 @@ public:
*/ */
virtual QRect dragRect() const; virtual QRect dragRect() const;
///@brief Returns whether all dock widgets have the specified option set
bool allDockWidgetsHave(DockWidgetBase::Option) const;
///@brief Returns whether at least one dock widget has the specified option set
bool anyDockWidgetsHas(DockWidgetBase::Option) const;
///@brief Returns whether all dock widgets have the specified layout saver option set
bool allDockWidgetsHave(DockWidgetBase::LayoutSaverOption) const;
///@brief Returns whether at least one dock widget has the specified layout saver option set
bool anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption) const;
Q_SIGNALS: Q_SIGNALS:
void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *); void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *);
void numDockWidgetsChanged(); void numDockWidgetsChanged();

View File

@@ -126,10 +126,13 @@ void Position::deserialize(const LayoutSaver::Position &lp)
if (index == -1) { if (index == -1) {
continue; // Skip continue; // Skip
} else { } else {
const auto floatingWindows = DockRegistry::self()->floatingWindows(); auto serializedFw = LayoutSaver::Layout::s_currentLayoutBeingRestored->floatingWindowForIndex(index);
if (index >= 0 && index < floatingWindows.size()) { if (serializedFw.isValid()) {
FloatingWindow *fw = floatingWindows.at(index); if (FloatingWindow *fw = serializedFw.floatingWindowInstance) {
layout = fw->multiSplitter(); layout = fw->multiSplitter();
} else {
continue;
}
} else { } else {
qWarning() << "Invalid floating window position to restore" << index; qWarning() << "Invalid floating window position to restore" << index;
continue; continue;

View File

@@ -51,10 +51,7 @@ TitleBar::TitleBar(FloatingWindow *parent)
, m_floatingWindow(parent) , m_floatingWindow(parent)
, m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport) , m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
{ {
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateCloseButton); connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateButtons);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateFloatButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMinimizeButton);
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton); connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::activatedChanged , this, &TitleBar::isFocusedChanged); connect(m_floatingWindow, &FloatingWindow::activatedChanged , this, &TitleBar::isFocusedChanged);
init(); init();
@@ -69,8 +66,8 @@ void TitleBar::init()
// repaint // repaint
update(); update();
}); });
updateCloseButton();
updateFloatButton(); updateButtons();
} }
TitleBar::~TitleBar() TitleBar::~TitleBar()
@@ -91,6 +88,15 @@ bool TitleBar::onDoubleClicked()
return false; return false;
} }
void TitleBar::updateButtons()
{
updateCloseButton();
updateFloatButton();
updateMaximizeButton();
updateMinimizeButton();
updateAutoHideButton();
}
void TitleBar::updateCloseButton() void TitleBar::updateCloseButton()
{ {
@@ -284,17 +290,41 @@ QIcon TitleBar::icon() const
void TitleBar::onCloseClicked() void TitleBar::onCloseClicked()
{ {
const bool closeOnlyCurrentTab = Config::self().flags() & Config::Flag_CloseOnlyCurrentTab;
if (m_frame) { if (m_frame) {
if (closeOnlyCurrentTab) {
if (DockWidgetBase *dw = m_frame->currentDockWidget()) {
dw->close();
} else {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
}
} else {
if (m_frame->isTheOnlyFrame() && !m_frame->isInMainWindow()) { if (m_frame->isTheOnlyFrame() && !m_frame->isInMainWindow()) {
m_frame->window()->close(); m_frame->window()->close();
} else { } else {
m_frame->close(); m_frame->close();
} }
} }
} else if (m_floatingWindow) {
if (m_floatingWindow) if (closeOnlyCurrentTab) {
if (Frame *f = m_floatingWindow->singleFrame()) {
if (DockWidgetBase *dw = f->currentDockWidget()) {
dw->close();
} else {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
}
} else {
m_floatingWindow->close(); m_floatingWindow->close();
} }
} else {
m_floatingWindow->close();
}
}
}
bool TitleBar::isFloating() const bool TitleBar::isFloating() const
{ {

View File

@@ -99,7 +99,7 @@ public:
FloatingWindow *floatingWindow() const { return m_floatingWindow; } FloatingWindow *floatingWindow() const { return m_floatingWindow; }
/// @brief updates the close button enabled state /// @brief updates the close button enabled state
void updateCloseButton(); void updateButtons();
Q_SIGNALS: Q_SIGNALS:
void titleChanged(); void titleChanged();
@@ -123,7 +123,6 @@ protected:
QString floatButtonToolTip() const; QString floatButtonToolTip() const;
virtual void updateMaximizeButton() {} virtual void updateMaximizeButton() {}
virtual void updateMinimizeButton() {} virtual void updateMinimizeButton() {}
virtual void updateAutoHideButton() {} virtual void updateAutoHideButton() {}
@@ -140,6 +139,7 @@ protected:
private: private:
friend class ::TestDocks; friend class ::TestDocks;
void updateFloatButton(); void updateFloatButton();
void updateCloseButton();
void setCloseButtonEnabled(bool); void setCloseButtonEnabled(bool);
void setFloatButtonVisible(bool); void setFloatButtonVisible(bool);
void setFloatButtonToolTip(const QString &); void setFloatButtonToolTip(const QString &);

View File

@@ -39,8 +39,8 @@ public:
QVBoxLayout *const layout; QVBoxLayout *const layout;
}; };
DockWidget::DockWidget(const QString &name, Options options) DockWidget::DockWidget(const QString &name, Options options, LayoutSaverOptions layoutSaverOptions)
: DockWidgetBase(name, options) : DockWidgetBase(name, options, layoutSaverOptions)
, d(new Private(this)) , d(new Private(this))
{ {
connect(this, &DockWidgetBase::widgetChanged, this, [this] (QWidget *w) { connect(this, &DockWidgetBase::widgetChanged, this, [this] (QWidget *w) {

View File

@@ -111,6 +111,7 @@ private Q_SLOTS:
void tst_resizeWindow(); void tst_resizeWindow();
void tst_restoreEmpty(); void tst_restoreEmpty();
void tst_restoreCentralFrame(); void tst_restoreCentralFrame();
void tst_restoreMaximizedState();
void tst_shutdown(); void tst_shutdown();
void tst_doubleClose(); void tst_doubleClose();
void tst_dockInternal(); void tst_dockInternal();
@@ -130,8 +131,13 @@ private Q_SLOTS:
void tst_lastFloatingPositionIsRestored(); void tst_lastFloatingPositionIsRestored();
void tst_restoreSimple(); void tst_restoreSimple();
void tst_restoreSimplest(); void tst_restoreSimplest();
void tst_restoreNonClosable();
void tst_invalidLayoutAfterRestore(); void tst_invalidLayoutAfterRestore();
void tst_dontCloseDockWidgetBeforeRestore();
void tst_dontCloseDockWidgetBeforeRestore2();
void tst_dontCloseDockWidgetBeforeRestore3();
void tst_closeOnlyCurrentTab();
void tst_tabWidgetCurrentIndex(); void tst_tabWidgetCurrentIndex();
void tst_doubleClickTabToDetach(); void tst_doubleClickTabToDetach();
void tst_propagateResize2(); void tst_propagateResize2();
@@ -237,6 +243,7 @@ private Q_SLOTS:
void tst_dragByTabBar_data(); void tst_dragByTabBar_data();
void tst_titleBarFocusedWhenTabsChange(); void tst_titleBarFocusedWhenTabsChange();
void tst_dock2FloatingWidgetsTabbed(); void tst_dock2FloatingWidgetsTabbed();
void tst_deleteOnClose();
#ifdef KDDOCKWIDGETS_QTWIDGETS #ifdef KDDOCKWIDGETS_QTWIDGETS
// TODO: Port these to QtQuick // TODO: Port these to QtQuick
@@ -252,6 +259,7 @@ private Q_SLOTS:
void tst_negativeAnchorPositionWhenEmbedded_data(); void tst_negativeAnchorPositionWhenEmbedded_data();
void tst_closeRemovesFromSideBar(); void tst_closeRemovesFromSideBar();
void tst_restoreSideBar(); void tst_restoreSideBar();
void tst_toggleActionOnSideBar();
// And fix these // And fix these
void tst_floatingWindowDeleted(); void tst_floatingWindowDeleted();
@@ -535,8 +543,8 @@ void TestDocks::tst_detachPos()
// Tests a situation where detaching a dock widget would send it to a bogus position // Tests a situation where detaching a dock widget would send it to a bogus position
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(501, 500), MainWindowOption_None); auto m = createMainWindow(QSize(501, 500), MainWindowOption_None);
auto dock1 = createDockWidget("1", new MyWidget(QStringLiteral("1"), Qt::black), {}, /** show = */false); // we're creating the dock widgets without showing them as floating initially, so it doesn't record the previous floating position auto dock1 = createDockWidget("1", new MyWidget(QStringLiteral("1"), Qt::black), {}, {}, /** show = */false); // we're creating the dock widgets without showing them as floating initially, so it doesn't record the previous floating position
auto dock2 = createDockWidget("2", new MyWidget(QStringLiteral("2"), Qt::black), {}, /** show = */false); auto dock2 = createDockWidget("2", new MyWidget(QStringLiteral("2"), Qt::black), {}, {}, /** show = */false);
QVERIFY(!dock1->isVisible()); QVERIFY(!dock1->isVisible());
QVERIFY(!dock2->isVisible()); QVERIFY(!dock2->isVisible());
@@ -821,6 +829,24 @@ void TestDocks::tst_restoreCentralFrame()
QVERIFY(!frame->titleBar()->isVisible()); QVERIFY(!frame->titleBar()->isVisible());
} }
void TestDocks::tst_restoreMaximizedState()
{
EnsureTopLevelsDeleted e;
auto m = createMainWindow();
m->showMaximized();
QCOMPARE(m->windowHandle()->windowState(), Qt::WindowMaximized);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
m->showNormal();
QVERIFY(m->windowHandle()->windowState() != Qt::WindowMaximized);
saver.restoreLayout(saved);
QCOMPARE(m->windowHandle()->windowState(), Qt::WindowMaximized);
}
void TestDocks::tst_setFloatingSimple() void TestDocks::tst_setFloatingSimple()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
@@ -1285,7 +1311,7 @@ void TestDocks::tst_28NestedWidgets()
int i = 0; int i = 0;
for (DockDescriptor &desc : docksToCreate) { for (DockDescriptor &desc : docksToCreate) {
desc.createdDock = createDockWidget(QString("%1").arg(i), new QPushButton(QString("%1").arg(i).toLatin1()), {}, false); desc.createdDock = createDockWidget(QString("%1").arg(i), new QPushButton(QString("%1").arg(i).toLatin1()), {}, {}, false);
DockWidgetBase *relativeTo = nullptr; DockWidgetBase *relativeTo = nullptr;
if (desc.relativeToIndex != -1) if (desc.relativeToIndex != -1)
@@ -1357,7 +1383,7 @@ void TestDocks::tst_startHidden()
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, /*show=*/false); auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/false);
m->addDockWidget(dock1, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden); m->addDockWidget(dock1, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden);
delete dock1; delete dock1;
} }
@@ -1367,8 +1393,8 @@ void TestDocks::tst_startHidden2()
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
{ {
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, false); auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, false); auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
auto dropArea = m->dropArea(); auto dropArea = m->dropArea();
MultiSplitter *layout = dropArea; MultiSplitter *layout = dropArea;
@@ -1396,9 +1422,9 @@ void TestDocks::tst_startHidden2()
{ {
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, false); auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, false); auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, false); auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, {}, false);
auto dropArea = m->dropArea(); auto dropArea = m->dropArea();
MultiSplitter *layout = dropArea; MultiSplitter *layout = dropArea;
@@ -1480,9 +1506,9 @@ void TestDocks::tst_negativeAnchorPosition2()
auto dropArea = m->dropArea(); auto dropArea = m->dropArea();
MultiSplitter *layout = dropArea; MultiSplitter *layout = dropArea;
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, /*show=*/false); auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/false);
auto dock2 = createDockWidget("2", new QPushButton("2"), {}, /*show=*/false); auto dock2 = createDockWidget("2", new QPushButton("2"), {}, {}, /*show=*/false);
auto dock3 = createDockWidget("3", new QPushButton("3"), {}, /*show=*/false); auto dock3 = createDockWidget("3", new QPushButton("3"), {}, {}, /*show=*/false);
m->addDockWidget(dock1, Location_OnLeft); m->addDockWidget(dock1, Location_OnLeft);
m->addDockWidget(dock2, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden); m->addDockWidget(dock2, Location_OnRight, nullptr, InitialVisibilityOption::StartHidden);
@@ -1694,8 +1720,8 @@ void TestDocks::tst_addAsPlaceholder()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, false); auto dock1 = createDockWidget("dock1", new QPushButton("one"), {}, {}, false);
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, false); auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
m->addDockWidget(dock1, Location_OnBottom); m->addDockWidget(dock1, Location_OnBottom);
m->addDockWidget(dock2, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden); m->addDockWidget(dock2, Location_OnTop, nullptr, InitialVisibilityOption::StartHidden);
@@ -1724,7 +1750,7 @@ void TestDocks::tst_removeItem()
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("dock1", new QPushButton("one")); auto dock1 = createDockWidget("dock1", new QPushButton("one"));
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, false); auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, {}, false);
auto dock3 = createDockWidget("dock3", new QPushButton("three")); auto dock3 = createDockWidget("dock3", new QPushButton("three"));
m->addDockWidget(dock1, Location_OnBottom); m->addDockWidget(dock1, Location_OnBottom);
@@ -3451,6 +3477,57 @@ void TestDocks::tst_restoreSimplest()
QVERIFY(layout->checkSanity()); QVERIFY(layout->checkSanity());
} }
void TestDocks::tst_restoreNonClosable()
{
// Tests that restoring state also restores the Option_NotClosable option
{
// Basic case:
EnsureTopLevelsDeleted e;
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("one", new QTextEdit(), DockWidgetBase::Option_NotClosable);
QCOMPARE(dock1->options(), DockWidgetBase::Option_NotClosable);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
QVERIFY(saver.restoreLayout(saved));
QCOMPARE(dock1->options(), DockWidgetBase::Option_NotClosable);
}
{
// Case from issue #137
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None);
auto dock1 = createDockWidget("1", new QTextEdit());
auto dock2 = createDockWidget("2", new QTextEdit(), DockWidgetBase::Option_NotClosable);
auto dock3 = createDockWidget("3", new QTextEdit());
m->addDockWidget(dock1, Location_OnLeft);
m->addDockWidget(dock2, Location_OnLeft);
m->addDockWidget(dock3, Location_OnLeft);
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
dock2->setFloating(true);
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
TitleBar *tb = dock2->frame()->actualTitleBar();
QVERIFY(tb->isVisible());
QVERIFY(!tb->closeButtonEnabled());
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
QVERIFY(saver.restoreLayout(saved));
QCOMPARE(dock2->options(), DockWidgetBase::Option_NotClosable);
tb = dock2->frame()->actualTitleBar();
QVERIFY(tb->isVisible());
QVERIFY(!tb->closeButtonEnabled());
}
}
void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation() void TestDocks::tst_resizeViaAnchorsAfterPlaceholderCreation()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
@@ -3781,10 +3858,10 @@ void TestDocks::tst_restoreWithAffinity()
auto m2 = createMainWindow(QSize(500, 500)); auto m2 = createMainWindow(QSize(500, 500));
m2->setAffinities({ "a2" }); m2->setAffinities({ "a2" });
auto dock1 = createDockWidget("1", new QPushButton("1"), {}, true, "a1"); auto dock1 = createDockWidget("1", new QPushButton("1"), {}, {}, true, "a1");
m1->addDockWidget(dock1, Location_OnLeft); m1->addDockWidget(dock1, Location_OnLeft);
auto dock2 = createDockWidget("2", new QPushButton("2"), {}, true, "a2"); auto dock2 = createDockWidget("2", new QPushButton("2"), {}, {}, true, "a2");
dock2->setFloating(true); dock2->setFloating(true);
dock2->show(); dock2->show();
@@ -3893,7 +3970,7 @@ void TestDocks::tst_restoreWithDockFactory()
// Now try with a factory func // Now try with a factory func
DockWidgetFactoryFunc func = [] (const QString &) { DockWidgetFactoryFunc func = [] (const QString &) {
return createDockWidget("1", new QPushButton("1"), {}, /*show=*/ false); return createDockWidget("1", new QPushButton("1"), {}, {}, /*show=*/ false);
}; };
KDDockWidgets::Config::self().setDockWidgetFactoryFunc(func); KDDockWidgets::Config::self().setDockWidgetFactoryFunc(func);
@@ -4988,7 +5065,7 @@ void TestDocks::tst_closeRemovesFromSideBar()
QVERIFY(!dw1->isOverlayed()); QVERIFY(!dw1->isOverlayed());
QVERIFY(!dw1->isVisible()); QVERIFY(!dw1->isVisible());
QVERIFY(dw1->sideBarLocation() != SideBarLocation::None); QVERIFY(dw1->isInSideBar());
SideBar *sb = m1->sideBarForDockWidget(dw1); SideBar *sb = m1->sideBarForDockWidget(dw1);
QVERIFY(sb); QVERIFY(sb);
@@ -5072,6 +5149,40 @@ void TestDocks::tst_restoreSideBar()
delete fw1; delete fw1;
} }
void TestDocks::tst_toggleActionOnSideBar()
{
// When a dock widget is in the sidebar and we use DockWidget::toggleAction() then it should
// toggle visibility without removing it from the sidebar
EnsureTopLevelsDeleted e;
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport);
auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1");
auto dw1 = new DockWidgetType("1");
m1->addDockWidget(dw1, Location_OnBottom);
dw1->moveToSideBar();
QVERIFY(!dw1->isVisible());
QVERIFY(!dw1->isOverlayed());
QVERIFY(dw1->isInSideBar());
QVERIFY(!dw1->isInMainWindow());
QAction *action = dw1->toggleAction();
action->trigger();
QVERIFY(dw1->isVisible());
QVERIFY(dw1->isOverlayed());
QVERIFY(dw1->isInMainWindow());
QVERIFY(dw1->isInSideBar());
action->trigger();
QVERIFY(!dw1->isOverlayed());
QVERIFY(!dw1->isInMainWindow());
QVERIFY(dw1->isInSideBar());
QVERIFY(!dw1->isInMainWindow());
}
void TestDocks::tst_embeddedMainWindow() void TestDocks::tst_embeddedMainWindow()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
@@ -5790,6 +5901,155 @@ void TestDocks::tst_invalidLayoutAfterRestore()
layout->checkSanity(); layout->checkSanity();
} }
void TestDocks::tst_dontCloseDockWidgetBeforeRestore()
{
EnsureTopLevelsDeleted e;
auto m = createMainWindow();
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, DockWidgetBase::LayoutSaverOption::Skip);
auto dock4 = createDockWidget("4", new QPushButton("4"), {}, {}, /*show=*/ false);
m->addDockWidget(dock1, Location_OnBottom);
m->addDockWidget(dock2, Location_OnBottom);
// Dock #3 floats, while #1 and #2 are docked.
dock3->setFloating(true);
QVERIFY(dock3->isOpen());
QVERIFY(dock3->isFloating());
QVERIFY(!dock3->isInMainWindow());
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
dock3->close();
// Not open anymore
QVERIFY(!dock3->isOpen());
QVERIFY(saver.restoreLayout(saved));
// #3 is still closed, the restore will skip it
QVERIFY(!dock3->isOpen());
QVERIFY(!dock3->isInMainWindow());
auto dock5 = createDockWidget("5", new QPushButton("5"), {}, DockWidgetBase::LayoutSaverOption::Skip);
dock4->show();
dock5->show();
QVERIFY(saver.restoreLayout(saved));
QVERIFY(!dock4->isOpen());
QVERIFY(dock5->isOpen()); // #5 is still open, it ignored restore
}
void TestDocks::tst_dontCloseDockWidgetBeforeRestore2()
{
// In this case we have a floating window with two dock widgets tabbed, both having LayoutSaverOption::Skip
// Meaning the whole window should be skipped
EnsureTopLevelsDeleted e;
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
auto dock3 = createDockWidget("dock3", new QPushButton("three"), {}, DockWidgetBase::LayoutSaverOption::Skip);
dock2->close();
dock3->close();
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout(); // This layout has 0 docks visible
dock2->show();
dock3->show();
QVERIFY(saver.restoreLayout(saved));
QVERIFY(dock2->isVisible()); // They're still visible
QVERIFY(dock3->isVisible());
// Now tab and restore again
dock2->addDockWidgetAsTab(dock3);
QVERIFY(saver.restoreLayout(saved));
QVERIFY(dock2->isOpen());
QVERIFY(dock3->isOpen());
QVERIFY(dock3->isVisible());
QCOMPARE(dock3->frame(), dock2->frame());
}
void TestDocks::tst_dontCloseDockWidgetBeforeRestore3()
{
EnsureTopLevelsDeleted e;
auto m = createMainWindow();
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
auto dock2 = createDockWidget("dock2", new QPushButton("two"), {}, DockWidgetBase::LayoutSaverOption::Skip);
dock1->close();
dock2->close();
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout(); // This layout has 0 docks visible
m->addDockWidget(dock1, Location_OnBottom);
m->addDockWidget(dock2, Location_OnBottom);
QVERIFY(saver.restoreLayout(saved));
QVERIFY(!dock1->isOpen()); // Gets closed by the restore
QVERIFY(dock2->isOpen()); // Dock2 remains open, it ignores restore
QVERIFY(dock2->isFloating());
}
void TestDocks::tst_closeOnlyCurrentTab()
{
{
// Case of a floating window with tabs
EnsureTopLevelsDeleted e;
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_CloseOnlyCurrentTab);
auto dock1 = createDockWidget("1", new QPushButton("1"));
auto dock2 = createDockWidget("2", new QPushButton("2"));
auto dock3 = createDockWidget("3", new QPushButton("3"));
/// Floating window with 3 tabs
dock1->addDockWidgetAsTab(dock2);
dock1->addDockWidgetAsTab(dock3);
TitleBar *tb = dock1->titleBar();
QVERIFY(tb->isVisible());
dock1->setAsCurrentTab();
Frame *frame = dock1->frame();
QCOMPARE(frame->currentIndex(), 0);
tb->onCloseClicked();
QVERIFY(!dock1->isOpen());
QVERIFY(dock2->isOpen());
QVERIFY(dock3->isOpen());
}
{
// Case of a floating window with tabs
EnsureTopLevelsDeleted e;
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_CloseOnlyCurrentTab);
auto m = createMainWindow();
auto dock1 = createDockWidget("1", new QPushButton("1"));
auto dock2 = createDockWidget("2", new QPushButton("2"));
auto dock3 = createDockWidget("3", new QPushButton("3"));
m->addDockWidget(dock1, Location_OnLeft);
m->addDockWidget(dock2, Location_OnRight);
dock2->addDockWidgetAsTab(dock3);
Frame *frame = dock2->frame();
QCOMPARE(frame->currentIndex(), 1);
TitleBar *tb = frame->titleBar();
QVERIFY(tb->isVisible());
tb->onCloseClicked();
QVERIFY(!dock3->isOpen());
QVERIFY(dock2->isOpen());
QVERIFY(dock1->isOpen());
QCOMPARE(frame->dockWidgetCount(), 1);
}
}
void TestDocks::tst_tabWidgetCurrentIndex() void TestDocks::tst_tabWidgetCurrentIndex()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;
@@ -6487,4 +6747,37 @@ void TestDocks::tst_dock2FloatingWidgetsTabbed()
} }
} }
void TestDocks::tst_deleteOnClose()
{
{
// Tests that DockWidget::close() deletes itself if Option_DeleteOnClose is set
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new MyWidget2(QSize(400, 400)), DockWidgetBase::Option_DeleteOnClose);
dock1->show();
dock1->close();
QVERIFY(Testing::waitForDeleted(dock1));
}
{
// Tests that if it's closed via LayoutSaver it's also destroyed when having Option_DeleteOnClose
QPointer<DockWidgetBase> dock1 = createDockWidget("1", new MyWidget2(QSize(400, 400)), DockWidgetBase::Option_DeleteOnClose, {}, /*show=*/ false);
QPointer<DockWidgetBase> dock2 = createDockWidget("2", new MyWidget2(QSize(400, 400)), {}, {}, /*show=*/ false);
LayoutSaver saver;
const QByteArray saved = saver.serializeLayout();
dock1->show();
dock2->show();
QVERIFY(dock1->isVisible());
QVERIFY(dock2->isVisible());
QVERIFY(saver.restoreLayout(saved));
QVERIFY(!dock1->isVisible());
QVERIFY(!dock2->isVisible());
QVERIFY(Testing::waitForDeleted(dock1));
QVERIFY(dock2.data());
delete dock2;
}
}
#include "tst_docks.moc" #include "tst_docks.moc"

View File

@@ -70,11 +70,13 @@ KDDockWidgets::Tests::createMainWindow(QSize sz, KDDockWidgets::MainWindowOption
} }
DockWidgetBase *KDDockWidgets::Tests::createDockWidget(const QString &name, QWidgetOrQuick *w, DockWidgetBase *KDDockWidgets::Tests::createDockWidget(const QString &name, QWidgetOrQuick *w,
DockWidgetBase::Options options, bool show, DockWidgetBase::Options options,
DockWidgetBase::LayoutSaverOptions layoutSaverOptions,
bool show,
const QString &affinityName) const QString &affinityName)
{ {
w->setFocusPolicy(Qt::StrongFocus); w->setFocusPolicy(Qt::StrongFocus);
auto dock = new DockWidgetType(name, options); auto dock = new DockWidgetType(name, options, layoutSaverOptions);
dock->setAffinityName(affinityName); dock->setAffinityName(affinityName);
dock->setWidget(w); dock->setWidget(w);
dock->setObjectName(name); dock->setObjectName(name);
@@ -132,7 +134,7 @@ std::unique_ptr<MainWindowBase> KDDockWidgets::Tests::createMainWindow(QVector<D
int i = 0; int i = 0;
for (DockDescriptor &desc : docks) { for (DockDescriptor &desc : docks) {
desc.createdDock = createDockWidget(QStringLiteral("%1-%2").arg(i).arg(count), createGuestWidget(i), {}, false); desc.createdDock = createDockWidget(QStringLiteral("%1-%2").arg(i).arg(count), createGuestWidget(i), {}, {}, false);
DockWidgetBase *relativeTo = nullptr; DockWidgetBase *relativeTo = nullptr;
if (desc.relativeToIndex != -1) if (desc.relativeToIndex != -1)
relativeTo = docks.at(desc.relativeToIndex).createdDock; relativeTo = docks.at(desc.relativeToIndex).createdDock;

View File

@@ -136,8 +136,8 @@ std::unique_ptr<MainWindowBase> createMainWindow(QSize sz = {1000, 1000},
std::unique_ptr<KDDockWidgets::MainWindowBase> createMainWindow(QVector<DockDescriptor> &docks); std::unique_ptr<KDDockWidgets::MainWindowBase> createMainWindow(QVector<DockDescriptor> &docks);
KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QWidgetOrQuick *w, KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QWidgetOrQuick *w,
DockWidgetBase::Options options = {}, bool show = true, DockWidgetBase::Options options = {}, DockWidgetBase::LayoutSaverOptions layoutSaverOptions = {},
const QString &affinityName = {}); bool show = true, const QString &affinityName = {});
KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QColor color = Qt::black); KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QColor color = Qt::black);
void nestDockWidget(DockWidgetBase *dock, DropArea *dropArea, Frame *relativeTo, void nestDockWidget(DockWidgetBase *dock, DropArea *dropArea, Frame *relativeTo,