Compare commits
166 Commits
v0.1
...
old_layout
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86d1a52bbf | ||
|
|
2d007c558a | ||
|
|
dc4a1975ab | ||
|
|
144897e831 | ||
|
|
e87f6d001e | ||
|
|
8cc53ca5bb | ||
|
|
1b337b5391 | ||
|
|
e383bc797e | ||
|
|
43a25ef93b | ||
|
|
19e519912b | ||
|
|
68f0c64954 | ||
|
|
e8db004874 | ||
|
|
6ee3cfbb2d | ||
|
|
ab0fc1e328 | ||
|
|
899ca6af6a | ||
|
|
c8c56d293c | ||
|
|
b236d744e3 | ||
|
|
425d859f5b | ||
|
|
d58abf170f | ||
|
|
225aa6a098 | ||
|
|
b0665cd003 | ||
|
|
3a501d1b5c | ||
|
|
acfd53672c | ||
|
|
afb3edfd1d | ||
|
|
b2c3442233 | ||
|
|
fc63f843b7 | ||
|
|
f77d052dbb | ||
|
|
cc1e1eaa51 | ||
|
|
ca4d63ed2e | ||
|
|
6564be122f | ||
|
|
eabcdfb945 | ||
|
|
5b91021745 | ||
|
|
dbd90b8a02 | ||
|
|
9904d847f2 | ||
|
|
dedda6cb14 | ||
|
|
2143b09206 | ||
|
|
947d638cb5 | ||
|
|
63e662323f | ||
|
|
90b893e46a | ||
|
|
802c6206ce | ||
|
|
ce6fd20efd | ||
|
|
af5bb4a3e4 | ||
|
|
ee786f57c5 | ||
|
|
170f4efbb2 | ||
|
|
d97b001ec8 | ||
|
|
043b481ddd | ||
|
|
2155e11bd7 | ||
|
|
670da82164 | ||
|
|
3f732e91a7 | ||
|
|
a286432176 | ||
|
|
1d7c0316ab | ||
|
|
564a7091e1 | ||
|
|
ca211f2494 | ||
|
|
435e61288e | ||
|
|
822f48ccdd | ||
|
|
6fb0694aab | ||
|
|
bab0ff085e | ||
|
|
5b0eb93cda | ||
|
|
758b4de5cc | ||
|
|
b78becfc01 | ||
|
|
15d9f074fa | ||
|
|
fdb95e344a | ||
|
|
69ada1bdad | ||
|
|
950ef943b7 | ||
|
|
21660d9fc5 | ||
|
|
b0525ed1f1 | ||
|
|
e7661cca91 | ||
|
|
2458b1c0f8 | ||
|
|
efdd8f9f81 | ||
|
|
27e53ca68d | ||
|
|
63da84e343 | ||
|
|
579b222418 | ||
|
|
61303b14a7 | ||
|
|
7a2aa43b2d | ||
|
|
cf0b8fd029 | ||
|
|
a35e088988 | ||
|
|
232e96ee0e | ||
|
|
b43081851f | ||
|
|
10621f3b4a | ||
|
|
723491d9b6 | ||
|
|
7d56860325 | ||
|
|
d6997eaf7f | ||
|
|
59bb0d8e71 | ||
|
|
37fe065bcf | ||
|
|
aded290573 | ||
|
|
fca010ce2a | ||
|
|
3771aa7a40 | ||
|
|
1cac350547 | ||
|
|
4c15335339 | ||
|
|
6da1a65591 | ||
|
|
6338e6b03e | ||
|
|
48ef73d6d3 | ||
|
|
52e53c4fd9 | ||
|
|
81c180fbfd | ||
|
|
07f9e9a0df | ||
|
|
6dd8a0cda1 | ||
|
|
25f3e83fc3 | ||
|
|
195c8121c0 | ||
|
|
2b1518b1c2 | ||
|
|
ca98d376b7 | ||
|
|
16b564a005 | ||
|
|
c3483ba480 | ||
|
|
162bf0e828 | ||
|
|
3bbeec06f8 | ||
|
|
2f3b7642ed | ||
|
|
068eb7cc25 | ||
|
|
8c37c3a7ec | ||
|
|
e2d47001db | ||
|
|
8898ec82a4 | ||
|
|
9ca03b65f0 | ||
|
|
b80a795968 | ||
|
|
8aaffa26c6 | ||
|
|
da3917ae64 | ||
|
|
0003b508d6 | ||
|
|
d5162efa31 | ||
|
|
a71361341c | ||
|
|
08cbbd5178 | ||
|
|
94c02a1fe9 | ||
|
|
8a05980cd5 | ||
|
|
45b2fb63bc | ||
|
|
4a6b73cae1 | ||
|
|
d738fbd154 | ||
|
|
7c10ab37f4 | ||
|
|
460d3f9ec1 | ||
|
|
4c19ccade7 | ||
|
|
03cc8abd94 | ||
|
|
8447cefc14 | ||
|
|
141cbc55f4 | ||
|
|
873978a99e | ||
|
|
b8279633ac | ||
|
|
cfa1cc47b6 | ||
|
|
4ca140c0cc | ||
|
|
d505eeb0a5 | ||
|
|
df692686e7 | ||
|
|
a25435fc2d | ||
|
|
fbfbf2d801 | ||
|
|
63a75d7be9 | ||
|
|
2ff2b78f5a | ||
|
|
6be2490c08 | ||
|
|
f37ecc0765 | ||
|
|
3b20f99092 | ||
|
|
9f5c7b5e6a | ||
|
|
4b0b11acee | ||
|
|
9b8709724c | ||
|
|
e5df8db045 | ||
|
|
9c6fd9ba0e | ||
|
|
927510dfff | ||
|
|
a29a33a46d | ||
|
|
53238e4f29 | ||
|
|
6be6aee56a | ||
|
|
7452520662 | ||
|
|
cdc70ec34b | ||
|
|
4a2a718f78 | ||
|
|
617bfa4801 | ||
|
|
71160b38c6 | ||
|
|
41d07ef8c6 | ||
|
|
1723f001c9 | ||
|
|
f2123ffb41 | ||
|
|
572a5cc6b5 | ||
|
|
04d91863c6 | ||
|
|
89dbd20e1d | ||
|
|
7bb0a29aea | ||
|
|
af1a26ef59 | ||
|
|
2d0b7f1bee | ||
|
|
f351e5ed9c | ||
|
|
945dbc1f27 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -40,3 +40,5 @@ kddockwidgets_basic_quick
|
||||
/src/KDDockWidgetsConfigVersion.cmake
|
||||
build-*
|
||||
/examples/dockwidgets/kddockwidgets_example
|
||||
.cmake
|
||||
CTestTestfile.cmake
|
||||
|
||||
8
.krazy
8
.krazy
@@ -1,4 +1,4 @@
|
||||
CHECKSETS qt5,c++,foss
|
||||
CHECKSETS qt5,c++
|
||||
|
||||
#KDAB-specific checks
|
||||
EXTRA kdabcopyright
|
||||
@@ -7,10 +7,12 @@ EXTRA kdabcopyright
|
||||
#EXTRA defines,null
|
||||
|
||||
#exclude checks now being done by clazy or clang-tools
|
||||
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects
|
||||
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects,dpointer,inline
|
||||
#exclude more checks
|
||||
EXCLUDE style
|
||||
EXCLUDE style
|
||||
|
||||
#skip CMake files
|
||||
SKIP /KDDockWidgetsConfig.cmake.in
|
||||
#skip the borrowed code in the cmake subdir
|
||||
SKIP /cmake/Qt5Portability.cmake|/cmake/ECM/
|
||||
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.12.0")
|
||||
project(KDDockWidgets VERSION 0.1 LANGUAGES CXX)
|
||||
else()
|
||||
project(KDDockWidgets
|
||||
VERSION 0.1
|
||||
DESCRIPTION "An advanced docking system for Qt"
|
||||
HOMEPAGE_URL "https://github.com/KDAB/KDDockWidgets"
|
||||
LANGUAGES CXX)
|
||||
endif()
|
||||
|
||||
project(KDDockWidgets
|
||||
VERSION 0.1
|
||||
DESCRIPTION "An advanced docking system for Qt"
|
||||
HOMEPAGE_URL "https://github.com/KDAB/KDDockWidgets"
|
||||
LANGUAGES CXX)
|
||||
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
|
||||
option(OPTION_DEVELOPER_MODE "Developer Mode" OFF)
|
||||
option(OPTION_ASAN_SUPPORT "Activate support for using ASAN" OFF)
|
||||
option(OPTION_SANITIZER_SUPPORT "Activate support for using sanitizers" OFF)
|
||||
# option(OPTION_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
|
||||
|
||||
find_package(Qt5Widgets)
|
||||
@@ -22,7 +27,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_DIR})
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
if (OPTION_ASAN_SUPPORT)
|
||||
if (OPTION_SANITIZER_SUPPORT)
|
||||
include(ECMEnableSanitizers)
|
||||
endif()
|
||||
|
||||
@@ -41,9 +46,11 @@ else()
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(examples/dockwidgets)
|
||||
|
||||
if (OPTION_DEVELOPER_MODE)
|
||||
if (NOT OPTION_QTQUICK)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
|
||||
10
Changelog
Normal file
10
Changelog
Normal file
@@ -0,0 +1,10 @@
|
||||
* v1.0 (November 4th, 2019)
|
||||
- Initial Release
|
||||
|
||||
* v1.1 (, 2020)
|
||||
Features:
|
||||
- Allow tab re-ordering with mouse, via KDDockWidgets::Config::Flag_AllowReorderTabs.
|
||||
- Allow tabs to have a close button, via KDDockWidgets::Config::Flag_TabsHaveCloseButton.
|
||||
- Add support for lazy resize, via KDDockWidgets::Config::Flag_LazyResize. Resize is only done when mouse is released.
|
||||
Fixes:
|
||||
- Fix dock widgets not filling their complete available space
|
||||
@@ -1,4 +1,4 @@
|
||||
The KDDockWidgets software is Copyright (C) 2018-2019 Klaralvdalens Datakonsult AB.
|
||||
The KDDockWidgets software is Copyright (C) 2018-2020 Klaralvdalens Datakonsult AB.
|
||||
|
||||
You may use, distribute and copy the KDDockWidgets software under the terms of
|
||||
the GNU General Public License version 2 or under the terms of GNU General
|
||||
@@ -354,7 +354,7 @@ Public License instead of this License.
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -998,7 +998,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -1017,13 +1017,13 @@ might be different; for a GUI interface, you would use an "about box".
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
12
README.md
12
README.md
@@ -60,9 +60,14 @@ $ ./kddockwidgets_example
|
||||
|
||||
```
|
||||
|
||||
Supported Qt versions
|
||||
======================
|
||||
KDDockWidgets requires Qt >= 5.9
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
KDDockWidgets is (C) 2018-2019, Klarälvdalens Datakonsult AB, and is available
|
||||
KDDockWidgets is (C) 2018-2020, Klarälvdalens Datakonsult AB, and is available
|
||||
under the terms of the GPLv2 (see LICENSE.GPL.txt).
|
||||
|
||||
Contact KDAB at <info@kdab.com> if you need different licensing options.
|
||||
@@ -75,6 +80,9 @@ contributions will require a signed Contributor License Agreement
|
||||
|
||||
Contact info@kdab.com for more information.
|
||||
|
||||
Please submit your contributions or issue reports from our GitHub space at
|
||||
https://github.com/KDAB/KDDockWidgets
|
||||
|
||||
About KDAB
|
||||
==========
|
||||
KDDockWidgets is supported and maintained by Klarälvdalens Datakonsult AB (KDAB).
|
||||
@@ -88,4 +96,4 @@ We continue to help develop parts of Qt and are one of the major contributors
|
||||
to the Qt Project. We can give advanced or standard trainings anywhere
|
||||
around the globe on Qt as well as C++, OpenGL, 3D and more.
|
||||
|
||||
Please visit http://www.kdab.com to meet the people who write code like this.
|
||||
Please visit https://www.kdab.com to meet the people who write code like this.
|
||||
|
||||
@@ -5,18 +5,21 @@ project(kddockwidgets_example)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
|
||||
|
||||
# This will look for Qt, do find_package yourself manually before
|
||||
# if you want to look for a specific version for instance.
|
||||
find_package(KDDockWidgets REQUIRED)
|
||||
|
||||
qt5_add_resources(RESOURCES_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
|
||||
if (NOT TARGET kddockwidgets)
|
||||
# This will look for Qt, do find_package yourself manually before
|
||||
# if you want to look for a specific Qt version for instance.
|
||||
find_package(KDDockWidgets REQUIRED)
|
||||
endif()
|
||||
|
||||
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
|
||||
|
||||
add_executable(kddockwidgets_example
|
||||
main.cpp
|
||||
MyFrameworkWidgetFactory.cpp
|
||||
MyMainWindow.cpp
|
||||
MyWidget.cpp
|
||||
${RESOURCES_SRC}
|
||||
${RESOURCES_EXAMPLE_SRC}
|
||||
)
|
||||
|
||||
target_link_libraries(kddockwidgets_example
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,6 +18,8 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kddockwidgets/FrameworkWidgetFactory.h>
|
||||
#include <kddockwidgets/private/TitleBar_p.h>
|
||||
#include <QPainter>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "MyWidget.h"
|
||||
|
||||
#include <kddockwidgets/LayoutSaver.h>
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
@@ -36,7 +35,7 @@
|
||||
|
||||
static MyWidget *newMyWidget()
|
||||
{
|
||||
const int randomNumber = rand() % 100 + 1;
|
||||
const int randomNumber = qrand() % 100 + 1;
|
||||
|
||||
if (randomNumber < 50) {
|
||||
if (randomNumber < 33) {
|
||||
@@ -49,12 +48,17 @@ static MyWidget *newMyWidget()
|
||||
}
|
||||
}
|
||||
|
||||
MyMainWindow::MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *parent)
|
||||
: MainWindow(QStringLiteral("MyMainWindow"), options, parent)
|
||||
MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
|
||||
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
|
||||
const QString &affinityName, QWidget *parent)
|
||||
: MainWindow(uniqueName, options, parent)
|
||||
, m_dockWidget0IsNonClosable(dockWidget0IsNonClosable)
|
||||
, m_dockWidget9IsNonDockable(nonDockableDockWidget9)
|
||||
, m_restoreIsRelative(restoreIsRelative)
|
||||
{
|
||||
// qApp->installEventFilter(this);
|
||||
|
||||
srand(time(nullptr));
|
||||
qsrand(time(nullptr));
|
||||
|
||||
auto menubar = menuBar();
|
||||
auto fileMenu = new QMenu(QStringLiteral("File"));
|
||||
@@ -78,58 +82,96 @@ MyMainWindow::MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *pa
|
||||
auto saveLayoutAction = fileMenu->addAction(QStringLiteral("Save Layout"));
|
||||
connect(saveLayoutAction, &QAction::triggered, this, [] {
|
||||
KDDockWidgets::LayoutSaver saver;
|
||||
const bool result = saver.saveToDisk();
|
||||
const bool result = saver.saveToFile(QStringLiteral("mylayout.json"));
|
||||
qDebug() << "Saving layout to disk. Result=" << result;
|
||||
});
|
||||
|
||||
auto restoreLayoutAction = fileMenu->addAction(QStringLiteral("Restore Layout"));
|
||||
connect(restoreLayoutAction, &QAction::triggered, this, [] {
|
||||
KDDockWidgets::LayoutSaver saver;
|
||||
saver.restoreFromDisk();
|
||||
connect(restoreLayoutAction, &QAction::triggered, this, [this] {
|
||||
KDDockWidgets::RestoreOptions options = KDDockWidgets::RestoreOption_None;
|
||||
if (m_restoreIsRelative)
|
||||
options |= KDDockWidgets::RestoreOption_RelativeToMainWindow;
|
||||
|
||||
KDDockWidgets::LayoutSaver saver(options);
|
||||
saver.restoreFromFile(QStringLiteral("mylayout.json"));
|
||||
});
|
||||
|
||||
auto closeAllAction = fileMenu->addAction(QStringLiteral("Close all"));
|
||||
connect(closeAllAction, &QAction::triggered, this, [this] {
|
||||
for (auto dw : m_dockwidgets)
|
||||
dw->close();
|
||||
});
|
||||
|
||||
auto quitAction = fileMenu->addAction(QStringLiteral("Quit"));
|
||||
connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
|
||||
|
||||
setAffinityName(affinityName);
|
||||
createDockWidgets();
|
||||
}
|
||||
|
||||
void MyMainWindow::createDockWidgets()
|
||||
{
|
||||
KDDockWidgets::DockWidget::List dockwidgets;
|
||||
Q_ASSERT(m_dockwidgets.isEmpty());
|
||||
|
||||
const int numDockWidgets = m_dockWidget9IsNonDockable ? 10 : 9;
|
||||
|
||||
|
||||
// Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget instances)
|
||||
for (int i = 0; i < 9; i++)
|
||||
dockwidgets << newDockWidget();
|
||||
for (int i = 0; i < numDockWidgets; i++)
|
||||
m_dockwidgets << newDockWidget();
|
||||
|
||||
|
||||
// MainWindow::addDockWidget() attaches a dock widget to the main window:
|
||||
addDockWidget(dockwidgets[0], KDDockWidgets::Location_OnTop);
|
||||
addDockWidget(m_dockwidgets[0], KDDockWidgets::Location_OnTop);
|
||||
|
||||
// Here, for finer granularity we specify right of dockwidgets[0]:
|
||||
addDockWidget(dockwidgets[1], KDDockWidgets::Location_OnRight, dockwidgets[0]);
|
||||
addDockWidget(m_dockwidgets[1], KDDockWidgets::Location_OnRight, m_dockwidgets[0]);
|
||||
|
||||
addDockWidget(dockwidgets[2], KDDockWidgets::Location_OnLeft);
|
||||
addDockWidget(dockwidgets[3], KDDockWidgets::Location_OnBottom);
|
||||
addDockWidget(dockwidgets[4], KDDockWidgets::Location_OnBottom);
|
||||
addDockWidget(m_dockwidgets[2], KDDockWidgets::Location_OnLeft);
|
||||
addDockWidget(m_dockwidgets[3], KDDockWidgets::Location_OnBottom);
|
||||
addDockWidget(m_dockwidgets[4], KDDockWidgets::Location_OnBottom);
|
||||
|
||||
// Tab two dock widgets toghether
|
||||
dockwidgets[3]->addDockWidgetAsTab(dockwidgets[5]);
|
||||
m_dockwidgets[3]->addDockWidgetAsTab(m_dockwidgets[5]);
|
||||
|
||||
// 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
|
||||
// and we tab 7 with it.
|
||||
dockwidgets[6]->addDockWidgetAsTab(dockwidgets[7]);
|
||||
m_dockwidgets[6]->addDockWidgetAsTab(m_dockwidgets[7]);
|
||||
|
||||
// Floating windows also support nesting, here we add 8 to the bottom of the group
|
||||
dockwidgets[6]->addDockWidgetToContainingWindow(dockwidgets[8], KDDockWidgets::Location_OnBottom);
|
||||
m_dockwidgets[6]->addDockWidgetToContainingWindow(m_dockwidgets[8], KDDockWidgets::Location_OnBottom);
|
||||
|
||||
auto floatingWindow = m_dockwidgets[6]->window();
|
||||
floatingWindow->move(100, 100);
|
||||
}
|
||||
|
||||
KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
|
||||
{
|
||||
static int count = 0;
|
||||
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count));
|
||||
|
||||
// Passing options is optional, we just want to illustrate Option_NotClosable here
|
||||
KDDockWidgets::DockWidget::Options options = KDDockWidgets::DockWidget::Option_None;
|
||||
if (count == 0 && m_dockWidget0IsNonClosable)
|
||||
options |= KDDockWidgets::DockWidget::Option_NotClosable;
|
||||
|
||||
if (count == 9 && m_dockWidget9IsNonDockable)
|
||||
options |= KDDockWidgets::DockWidget::Option_NotDockable;
|
||||
|
||||
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options);
|
||||
dock->setAffinityName(affinityName()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
|
||||
|
||||
if (count == 1)
|
||||
dock->setIcon(QIcon::fromTheme(QStringLiteral("mail-message")));
|
||||
auto myWidget = newMyWidget();
|
||||
dock->setWidget(myWidget);
|
||||
dock->setTitle(QStringLiteral("DockWidget #%1").arg(count));
|
||||
|
||||
|
||||
if (dock->options() & KDDockWidgets::DockWidget::Option_NotDockable) {
|
||||
dock->setTitle(QStringLiteral("DockWidget #%1 (%2)").arg(count).arg("non dockable"));
|
||||
} else {
|
||||
dock->setTitle(QStringLiteral("DockWidget #%1").arg(count));
|
||||
}
|
||||
|
||||
dock->resize(600, 600);
|
||||
dock->show();
|
||||
m_toggleMenu->addAction(dock->toggleAction());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,16 +18,26 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
#include <kddockwidgets/MainWindow.h>
|
||||
|
||||
class MyMainWindow : public KDDockWidgets::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *parent = nullptr);
|
||||
explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
|
||||
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
|
||||
const QString &affinityName = {}, // Usually not needed. Just here to show the feature.
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void createDockWidgets();
|
||||
KDDockWidgets::DockWidgetBase* newDockWidget();
|
||||
QMenu *m_toggleMenu = nullptr;
|
||||
const bool m_dockWidget0IsNonClosable;
|
||||
const bool m_dockWidget9IsNonDockable;
|
||||
const bool m_restoreIsRelative;
|
||||
KDDockWidgets::DockWidget::List m_dockwidgets;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,9 +21,13 @@
|
||||
#ifndef EXAMPLEDOCKABLEWIDGET_H
|
||||
#define EXAMPLEDOCKABLEWIDGET_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPainter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MyWidget : public QWidget
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -49,11 +49,41 @@ int main(int argc, char **argv)
|
||||
QCommandLineOption customStyle("p", QCoreApplication::translate("main", "Shows how to style framework internals via FrameworkWidgetFactory"));
|
||||
parser.addOption(customStyle);
|
||||
|
||||
QCommandLineOption reorderTabsOption("r", QCoreApplication::translate("main", "Support re-ordering tabs with mouse"));
|
||||
parser.addOption(reorderTabsOption);
|
||||
|
||||
QCommandLineOption noTitleBars("t", QCoreApplication::translate("main", "Never show titlebars"));
|
||||
parser.addOption(noTitleBars);
|
||||
|
||||
QCommandLineOption lazyResizeOption("l", QCoreApplication::translate("main", "Use lazy resize"));
|
||||
parser.addOption(lazyResizeOption);
|
||||
|
||||
QCommandLineOption multipleMainWindows("m", QCoreApplication::translate("main", "Shows two multiple main windows"));
|
||||
parser.addOption(multipleMainWindows);
|
||||
|
||||
QCommandLineOption incompatibleMainWindows("i", QCoreApplication::translate("main", "Only usable with -m. Make the two main windows incompatible with each other. (Illustrates (MainWindowBase::setAffinityName))"));
|
||||
parser.addOption(incompatibleMainWindows);
|
||||
|
||||
QCommandLineOption tabsHaveCloseButton("c", QCoreApplication::translate("main", "Tabs have a close button"));
|
||||
parser.addOption(tabsHaveCloseButton);
|
||||
|
||||
QCommandLineOption nonClosableDockWidget("n", QCoreApplication::translate("main", "DockWidget #0 will be non-closable"));
|
||||
parser.addOption(nonClosableDockWidget);
|
||||
|
||||
QCommandLineOption relativeRestore("s", QCoreApplication::translate("main", "Don't restore main window geometry, restore dock widgets in relative sizes"));
|
||||
parser.addOption(relativeRestore);
|
||||
|
||||
QCommandLineOption doubleClickMaximize("x", QCoreApplication::translate("main", "Double clicking a title bar will maximize a floating window"));
|
||||
parser.addOption(doubleClickMaximize);
|
||||
|
||||
QCommandLineOption nonDockable("d", QCoreApplication::translate("main", "DockWidget #9 will be non-dockable"));
|
||||
parser.addOption(nonDockable);
|
||||
|
||||
QCommandLineOption maximizeButton("b", QCoreApplication::translate("main", "DockWidgets have maximize/restore buttons instead of float/dock button"));
|
||||
parser.addOption(maximizeButton);
|
||||
|
||||
#if defined(DOCKS_DEVELOPER_MODE)
|
||||
QCommandLineOption noCentralFrame("c", QCoreApplication::translate("main", "No central frame"));
|
||||
QCommandLineOption noCentralFrame("f", QCoreApplication::translate("main", "No central frame"));
|
||||
parser.addOption(noCentralFrame);
|
||||
#endif
|
||||
|
||||
@@ -72,12 +102,58 @@ int main(int argc, char **argv)
|
||||
: MainWindowOption_HasCentralFrame;
|
||||
#endif
|
||||
|
||||
auto flags = KDDockWidgets::Config::self().flags();
|
||||
if (parser.isSet(noTitleBars))
|
||||
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flags() | KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | KDDockWidgets::Config::Flag_AlwaysShowTabs);
|
||||
flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | KDDockWidgets::Config::Flag_AlwaysShowTabs;
|
||||
|
||||
MyMainWindow mainWindow(options);
|
||||
if (parser.isSet(reorderTabsOption))
|
||||
flags |= KDDockWidgets::Config::Flag_AllowReorderTabs;
|
||||
|
||||
if (parser.isSet(maximizeButton))
|
||||
flags |= KDDockWidgets::Config::Flag_TitleBarHasMaximizeButton;
|
||||
|
||||
if (parser.isSet(lazyResizeOption))
|
||||
flags |= KDDockWidgets::Config::Flag_LazyResize;
|
||||
|
||||
if (parser.isSet(tabsHaveCloseButton))
|
||||
flags |= KDDockWidgets::Config::Flag_TabsHaveCloseButton;
|
||||
|
||||
|
||||
if (parser.isSet(doubleClickMaximize))
|
||||
flags |= KDDockWidgets::Config::Flag_DoubleClickMaximizes;
|
||||
|
||||
if (parser.isSet(incompatibleMainWindows) && !parser.isSet(multipleMainWindows)) {
|
||||
qWarning() << "Error: Argument -i requires -m";
|
||||
return 1;
|
||||
}
|
||||
|
||||
KDDockWidgets::Config::self().setFlags(flags);
|
||||
|
||||
const bool nonClosableDockWidget0 = parser.isSet(nonClosableDockWidget);
|
||||
const bool restoreIsRelative = parser.isSet(relativeRestore);
|
||||
const bool nonDockableDockWidget9 = parser.isSet(nonDockable);
|
||||
|
||||
MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative);
|
||||
mainWindow.setWindowTitle("Main Window 1");
|
||||
mainWindow.resize(1200, 1200);
|
||||
mainWindow.show();
|
||||
|
||||
if (parser.isSet(multipleMainWindows)) {
|
||||
// By default a dock widget can dock into any main window.
|
||||
// By setting an affinity name we can prevent that. Dock widgets of different affinities are incompatible.
|
||||
const QString affinity = parser.isSet(incompatibleMainWindows) ? QStringLiteral("affinity1")
|
||||
: QString();
|
||||
|
||||
auto mainWindow2 = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options,
|
||||
nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative, affinity);
|
||||
if (affinity.isEmpty())
|
||||
mainWindow2->setWindowTitle("Main Window 2");
|
||||
else
|
||||
mainWindow2->setWindowTitle("Main Window 2 (different affinity)");
|
||||
|
||||
mainWindow2->resize(1200, 1200);
|
||||
mainWindow2->show();
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -122,12 +122,14 @@ endif()
|
||||
qt5_add_resources(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
|
||||
|
||||
add_library(kddockwidgets SHARED ${DOCKSLIBS_SRCS} ${DOCKS_INSTALLABLE_INCLUDES} ${RESOURCES} ${RESOURCES_QUICK})
|
||||
add_library(KDAB::kddockwidgets ALIAS kddockwidgets)
|
||||
|
||||
target_include_directories(kddockwidgets
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<INSTALL_INTERFACE:include/kddockwidgets>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/fwd_headers>
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/private
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
11
src/Config.h
11
src/Config.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
#include "docks_export.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QQmlEngine;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets
|
||||
{
|
||||
@@ -62,7 +64,12 @@ public:
|
||||
Flag_NativeTitleBar = 1, ///> Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise. This is mutually exclusive with Flag_AeroSnap
|
||||
Flag_AeroSnapWithClientDecos = 2, ///> Enables AeroSnap even if we're not using the native title bar. Only supported on Windows 10.
|
||||
Flag_HideTitleBarWhenTabsVisible = 8, ///> Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
|
||||
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one
|
||||
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one,
|
||||
Flag_AllowReorderTabs = 32, /// Allows user to re-order tabs by dragging them
|
||||
Flag_LazyResize = 32, /// The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
|
||||
Flag_TabsHaveCloseButton = 64, /// Tabs will have a close button. Equivalent to QTabWidget::setTabsClosable(true).
|
||||
Flag_DoubleClickMaximizes = 128, /// Double clicking the titlebar will maximize a floating window instead of re-docking it
|
||||
Flag_TitleBarHasMaximizeButton = 256, /// The title bar will have a maximize/restore button when floating. This is mutually-exclusive with the floating button (since many apps behave that way).
|
||||
Flag_Default = Flag_AeroSnapWithClientDecos ///> The defaults
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
#include "DockWidgetBase.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCloseEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -57,16 +57,27 @@ public:
|
||||
, q(qq)
|
||||
, options(options_)
|
||||
, toggleAction(new QAction(q))
|
||||
, floatAction(new QAction(q))
|
||||
{
|
||||
q->connect(q, &DockWidgetBase::shown, q, [this] { onDockWidgetShown(); } );
|
||||
q->connect(q, &DockWidgetBase::hidden, q, [this] { onDockWidgetHidden(); } );
|
||||
|
||||
q->connect(toggleAction, &QAction::toggled, q, [this] (bool enabled) {
|
||||
if (!m_updatingToggleAction) // guard against recursiveness
|
||||
if (!m_updatingToggleAction) { // guard against recursiveness
|
||||
toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock widget is inserted into a tab widget it might get hide events, ignore those. The Dock Widget is open.
|
||||
toggle(enabled);
|
||||
toggleAction->blockSignals(false);
|
||||
}
|
||||
});
|
||||
|
||||
q->connect(floatAction, &QAction::toggled, q, [this] (bool enabled) {
|
||||
if (!m_updatingFloatAction) { // guard against recursiveness
|
||||
q->setFloating(enabled);
|
||||
}
|
||||
});
|
||||
|
||||
toggleAction->setCheckable(true);
|
||||
floatAction->setCheckable(true);
|
||||
}
|
||||
|
||||
void init()
|
||||
@@ -74,10 +85,13 @@ public:
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
QPoint defaultCenterPosForFloating();
|
||||
|
||||
void updateTitle();
|
||||
void updateIcon();
|
||||
void toggle(bool enabled);
|
||||
void updateToggleAction();
|
||||
void updateFloatAction();
|
||||
void onDockWidgetShown();
|
||||
void onDockWidgetHidden();
|
||||
TabWidget *parentTabWidget() const;
|
||||
@@ -94,14 +108,17 @@ public:
|
||||
void saveTabIndex();
|
||||
|
||||
const QString name;
|
||||
QString affinityName;
|
||||
QString title;
|
||||
QIcon icon;
|
||||
QWidget *widget = nullptr;
|
||||
DockWidgetBase *const q;
|
||||
const DockWidgetBase::Options options;
|
||||
DockWidgetBase::Options options;
|
||||
QAction *const toggleAction;
|
||||
QAction *const floatAction;
|
||||
LastPosition m_lastPosition;
|
||||
bool m_updatingToggleAction = false;
|
||||
bool m_updatingFloatAction = false;
|
||||
bool m_isForceClosing = false;
|
||||
};
|
||||
|
||||
@@ -125,7 +142,7 @@ DockWidgetBase::~DockWidgetBase()
|
||||
delete d;
|
||||
}
|
||||
|
||||
void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
|
||||
void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, AddingOption addingOption)
|
||||
{
|
||||
qCDebug(addwidget) << Q_FUNC_INFO << other;
|
||||
if (other == this) {
|
||||
@@ -138,6 +155,18 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->affinityName() != affinityName()) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
|
||||
<< other->affinityName() << affinityName();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((other->options() & DockWidgetBase::Option_NotDockable) ||
|
||||
(options() & DockWidgetBase::Option_NotDockable)) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other;
|
||||
return;
|
||||
}
|
||||
|
||||
Frame *frame = this->frame();
|
||||
|
||||
if (frame) {
|
||||
@@ -155,7 +184,7 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
|
||||
|
||||
Q_ASSERT(frame);
|
||||
other->setParent(nullptr);
|
||||
frame->addWidget(other);
|
||||
frame->addWidget(other, addingOption);
|
||||
}
|
||||
|
||||
void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Location location, DockWidgetBase *relativeTo)
|
||||
@@ -166,6 +195,18 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Loca
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->affinityName() != affinityName()) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
|
||||
<< other->affinityName() << affinityName();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((other->options() & DockWidgetBase::Option_NotDockable) ||
|
||||
(options() & DockWidgetBase::Option_NotDockable)) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWindow())
|
||||
morphIntoFloatingWindow();
|
||||
|
||||
@@ -239,6 +280,11 @@ QAction *DockWidgetBase::toggleAction() const
|
||||
return d->toggleAction;
|
||||
}
|
||||
|
||||
QAction *DockWidgetBase::floatAction() const
|
||||
{
|
||||
return d->floatAction;
|
||||
}
|
||||
|
||||
QString DockWidgetBase::uniqueName() const
|
||||
{
|
||||
return d->name;
|
||||
@@ -263,6 +309,21 @@ DockWidgetBase::Options DockWidgetBase::options() const
|
||||
return d->options;
|
||||
}
|
||||
|
||||
void DockWidgetBase::setOptions(Options options)
|
||||
{
|
||||
if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) {
|
||||
qWarning() << Q_FUNC_INFO << "Option_NotDockable not allowed to change. Pass via ctor only.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (options != d->options) {
|
||||
d->options = options;
|
||||
Q_EMIT optionsChanged(options);
|
||||
if (auto tb = titleBar())
|
||||
tb->updateCloseButton();
|
||||
}
|
||||
}
|
||||
|
||||
bool DockWidgetBase::isTabbed() const
|
||||
{
|
||||
if (TabWidget* tabWidget = d->parentTabWidget()) {
|
||||
@@ -315,6 +376,55 @@ TitleBar *DockWidgetBase::titleBar() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DockWidgetBase::isOpen() const
|
||||
{
|
||||
return d->toggleAction->isChecked();
|
||||
}
|
||||
|
||||
QString DockWidgetBase::affinityName() const
|
||||
{
|
||||
return d->affinityName;
|
||||
}
|
||||
|
||||
void DockWidgetBase::show()
|
||||
{
|
||||
if (isWindow() && (lastPosition()->m_wasFloating || !lastPosition()->isValid())) {
|
||||
// Create the FloatingWindow already, instead of waiting for the show event.
|
||||
// This reduces flickering on some platforms
|
||||
morphIntoFloatingWindow();
|
||||
} else {
|
||||
QWidget::show();
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::raise()
|
||||
{
|
||||
if (!isOpen())
|
||||
return;
|
||||
|
||||
setAsCurrentTab();
|
||||
|
||||
if (auto fw = qobject_cast<FloatingWindow*>(window())) {
|
||||
fw->raise();
|
||||
fw->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::setAffinityName(const QString &name)
|
||||
{
|
||||
if (d->affinityName == name)
|
||||
return;
|
||||
|
||||
if (!d->affinityName.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO
|
||||
<< "Affinity is already set, refusing to change."
|
||||
<< "Submit a feature request with a good justification.";
|
||||
return;
|
||||
}
|
||||
|
||||
d->affinityName = name;
|
||||
}
|
||||
|
||||
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
|
||||
{
|
||||
qCDebug(creation) << "DockWidget::morphIntoFloatingWindow() this=" << this
|
||||
@@ -325,8 +435,12 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
|
||||
|
||||
if (isWindow()) {
|
||||
QRect geo = lastPosition()->lastFloatingGeometry();
|
||||
if (geo.isNull())
|
||||
if (geo.isNull()) {
|
||||
geo = geometry();
|
||||
const QPoint center = d->defaultCenterPosForFloating();
|
||||
if (!center.isNull())
|
||||
geo.moveCenter(center);
|
||||
}
|
||||
|
||||
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
|
||||
frame->addWidget(this);
|
||||
@@ -374,6 +488,17 @@ LastPosition *DockWidgetBase::lastPosition() const
|
||||
return &d->m_lastPosition;
|
||||
}
|
||||
|
||||
QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
|
||||
{
|
||||
MainWindowBase::List mainWindows = DockRegistry::self()->mainwindows();
|
||||
// We don't care about multiple mainwindows yet. Or, let's just say that the first one is more main than the others
|
||||
MainWindowBase *mw = mainWindows.isEmpty() ? nullptr : mainWindows.constFirst();
|
||||
if (!mw || !q->isFloating())
|
||||
return {};
|
||||
|
||||
return mw->geometry().center();
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::updateTitle()
|
||||
{
|
||||
if (q->isFloating())
|
||||
@@ -408,15 +533,32 @@ void DockWidgetBase::Private::updateToggleAction()
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::updateFloatAction()
|
||||
{
|
||||
QScopedValueRollback<bool> recursionGuard(m_updatingFloatAction, true); // Guard against recursiveness
|
||||
|
||||
if (q->isFloating()) {
|
||||
floatAction->setEnabled(m_lastPosition.isValid());
|
||||
floatAction->setChecked(true);
|
||||
floatAction->setToolTip(tr("Dock"));
|
||||
} else {
|
||||
floatAction->setEnabled(true);
|
||||
floatAction->setChecked(false);
|
||||
floatAction->setToolTip(tr("Detach"));
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::onDockWidgetShown()
|
||||
{
|
||||
updateToggleAction();
|
||||
updateFloatAction();
|
||||
qCDebug(hiding) << Q_FUNC_INFO << "parent=" << q->parentWidget();
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::onDockWidgetHidden()
|
||||
{
|
||||
updateToggleAction();
|
||||
updateFloatAction();
|
||||
qCDebug(hiding) << Q_FUNC_INFO << "parent=" << q->parentWidget();
|
||||
}
|
||||
|
||||
@@ -430,7 +572,8 @@ TabWidget *DockWidgetBase::Private::parentTabWidget() const
|
||||
|
||||
void DockWidgetBase::Private::close()
|
||||
{
|
||||
if (!m_isForceClosing && q->isFloating()) { // only user-closing is interesting to save the geometry
|
||||
if (!m_isForceClosing && q->isFloating() && q->isVisible()) { // only user-closing is interesting to save the geometry
|
||||
// We check for isVisible so we don't save geometry if you call close() on an already closed dock widget
|
||||
m_lastPosition.setLastFloatingGeometry(q->window()->geometry());
|
||||
}
|
||||
|
||||
@@ -508,6 +651,7 @@ void DockWidgetBase::onParentChanged()
|
||||
{
|
||||
Q_EMIT parentChanged();
|
||||
d->updateToggleAction();
|
||||
d->updateFloatAction();
|
||||
}
|
||||
|
||||
void DockWidgetBase::onShown(bool spontaneous)
|
||||
@@ -561,6 +705,13 @@ DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &
|
||||
if (QWidget *w = dw->widget())
|
||||
w->setVisible(true);
|
||||
dw->setProperty("kddockwidget_was_restored", true);
|
||||
|
||||
if (dw->affinityName() != saved->affinityName) {
|
||||
qWarning() << Q_FUNC_INFO << "Affinity name changed from" << dw->affinityName()
|
||||
<< "; to" << saved->affinityName;
|
||||
dw->d->affinityName = saved->affinityName;
|
||||
}
|
||||
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << saved->uniqueName;
|
||||
}
|
||||
@@ -570,5 +721,8 @@ DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &
|
||||
|
||||
LayoutSaver::DockWidget::Ptr DockWidgetBase::serialize() const
|
||||
{
|
||||
return LayoutSaver::DockWidget::dockWidgetForName(uniqueName());
|
||||
auto ptr = LayoutSaver::DockWidget::dockWidgetForName(uniqueName());
|
||||
ptr->affinityName = affinityName();
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,7 +36,9 @@
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
@@ -49,6 +51,7 @@ class DockRegistry;
|
||||
class LayoutSaver;
|
||||
class TabWidget;
|
||||
class TitleBar;
|
||||
class MainWindowBase;
|
||||
|
||||
/**
|
||||
* @brief The DockWidget base-class. DockWidget and DockWidgetBase are only
|
||||
@@ -66,7 +69,8 @@ public:
|
||||
///@brief DockWidget options to pass at construction time
|
||||
enum Option {
|
||||
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
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Option)
|
||||
|
||||
@@ -97,9 +101,12 @@ public:
|
||||
/**
|
||||
* @brief docks @p other widget into this one. Tabs will be shown if not already.
|
||||
* @param other The other dock widget to dock into this one.
|
||||
* @param addingOption Allows to specify an AddingOption. Which is useful to add the dock widget
|
||||
* as hidden, recording only a placeholder in the tab. So it's restored to tabbed when eventually
|
||||
* shown.
|
||||
* @sa MainWindow::addDockWidget(), DockWidget::addDockWidgetToContainingWindow()
|
||||
*/
|
||||
void addDockWidgetAsTab(DockWidgetBase *other);
|
||||
void addDockWidgetAsTab(DockWidgetBase *other, AddingOption addingOption = AddingOption_None);
|
||||
|
||||
/**
|
||||
* @brief docks @p other widget into the window that contains this one.
|
||||
@@ -147,6 +154,12 @@ public:
|
||||
*/
|
||||
QAction *toggleAction() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the QAction that allows to dock/undock the dock widget
|
||||
* Useful to put in menus.
|
||||
*/
|
||||
QAction *floatAction() const;
|
||||
|
||||
/**
|
||||
* @brief the dock widget's unique name.
|
||||
* @internal
|
||||
@@ -168,11 +181,20 @@ public:
|
||||
void setTitle(const QString &title);
|
||||
|
||||
/**
|
||||
* @brief Returns the dock widget's options which control behaviour
|
||||
* These options were passed at construction time and are immutable.
|
||||
* @brief Returns the dock widget's options which control behaviour.
|
||||
* @sa setOptions(), optionsChanged()
|
||||
*/
|
||||
Options options() const;
|
||||
|
||||
/**
|
||||
* @brief Setter for the options.
|
||||
* Only Option_NotClosable is allowed to change after construction. For the other options use
|
||||
* the constructor only.
|
||||
*
|
||||
* @sa options(), optionsChanged()
|
||||
*/
|
||||
void setOptions(Options);
|
||||
|
||||
/**
|
||||
* @brief returns if this dock widget is tabbed into another
|
||||
*
|
||||
@@ -221,6 +243,48 @@ public:
|
||||
*/
|
||||
TitleBar *titleBar() const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this dock widget is open.
|
||||
* Equivalent to calling toggleAction().isChecked() or isVisible()
|
||||
*/
|
||||
bool isOpen() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the affinity name. Dock widgets can only dock into dock widgets of the same affinity.
|
||||
*
|
||||
* By default the affinity is empty and a dock widget can dock into any main window and into any
|
||||
* floating window. Usually you won't ever need to call
|
||||
* this function, unless you have requirements where certain dock widgets can only dock into
|
||||
* certain other dock widgets and main windows. @sa MainWindowBase::setAffinityName().
|
||||
*
|
||||
* Note: Call this function right after creating your dock widget, before adding to a main window and
|
||||
* before restoring any layout.
|
||||
*
|
||||
* Note: Currently you can only call this function once, to keep the code simple and avoid
|
||||
* edge cases. This will only be changed if a good use case comes up that requires changing
|
||||
* affinities multiple times.
|
||||
*
|
||||
* @p name The affinity name.
|
||||
*/
|
||||
void setAffinityName(const QString &name);
|
||||
|
||||
/**
|
||||
* @brief Returns the affinity name. Empty by default.
|
||||
*/
|
||||
QString affinityName() const;
|
||||
|
||||
/// @brief Equivalent to QWidget::show(), but it's optimized to reduce flickering on some platforms
|
||||
void show();
|
||||
|
||||
/// @brief Brings the dock widget to the front.
|
||||
///
|
||||
/// This means:
|
||||
/// - If the dock widget is tabbed with other dock widgets but its tab is not current, it's made current.
|
||||
/// - If the dock widget is floating, QWindow::raise() is called.
|
||||
///
|
||||
/// This only applies if the dock widget is already open. If closed, does nothing.
|
||||
void raise();
|
||||
|
||||
Q_SIGNALS:
|
||||
///@brief signal emitted when the parent changed
|
||||
void parentChanged();
|
||||
@@ -240,6 +304,10 @@ Q_SIGNALS:
|
||||
///@brief emitted when the hosted widget changed
|
||||
void widgetChanged(QWidget*);
|
||||
|
||||
///@brief emitted when the options change
|
||||
///@sa setOptions(), options()
|
||||
void optionsChanged(Options);
|
||||
|
||||
protected:
|
||||
void onParentChanged();
|
||||
void onShown(bool spontaneous);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -81,12 +81,12 @@ Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidgetAdapter
|
||||
return new SeparatorWidget(anchor, parent);
|
||||
}
|
||||
|
||||
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(QWidget *parent) const
|
||||
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(MainWindowBase *parent) const
|
||||
{
|
||||
return new FloatingWindowWidget(parent);
|
||||
}
|
||||
|
||||
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, QWidget *parent) const
|
||||
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWindowBase *parent) const
|
||||
{
|
||||
return new FloatingWindowWidget(frame, parent);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MainWindowBase;
|
||||
class DropIndicatorOverlayInterface;
|
||||
class Separator;
|
||||
class FloatingWindow;
|
||||
@@ -108,14 +109,14 @@ public:
|
||||
/// Override to provide your own FloatingWindow sub-class. If overridden then
|
||||
/// you also need to override the overloads below.
|
||||
///@param parent Just forward to FloatingWindow's constructor.
|
||||
virtual FloatingWindow *createFloatingWindow(QWidgetOrQuick *parent = nullptr) const = 0;
|
||||
virtual FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const = 0;
|
||||
|
||||
///@brief Called internally by the framework to create a FloatingWindow
|
||||
/// Override to provide your own FloatingWindow sub-class. If overridden then
|
||||
/// you also need to override the overloads above.
|
||||
///@param frame Just forward to FloatingWindow's constructor.
|
||||
///@param parent Just forward to FloatingWindow's constructor.
|
||||
virtual FloatingWindow *createFloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr) const = 0;
|
||||
virtual FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const = 0;
|
||||
|
||||
///@brief Called internally by the framework to create a DropIndicatorOverlayInterface
|
||||
/// Override to provide your own DropIndicatorOverlayInterface sub-class.
|
||||
@@ -135,8 +136,8 @@ public:
|
||||
TabBar *createTabBar(TabWidget *parent) const override;
|
||||
TabWidget *createTabWidget(Frame *parent) const override;
|
||||
Separator *createSeparator(Anchor *anchor, QWidgetAdapter *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(QWidgetOrQuick *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override;
|
||||
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
|
||||
DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -59,6 +59,13 @@ namespace KDDockWidgets
|
||||
};
|
||||
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
|
||||
|
||||
enum RestoreOption {
|
||||
RestoreOption_None = 0,
|
||||
RestoreOption_RelativeToMainWindow = 1, ///< Skips restoring the main window geometry and the restored dock widgets will use relative sizing.
|
||||
///< Loading layouts won't change the main window geometry and just use whatever the user has at the moment.
|
||||
};
|
||||
Q_DECLARE_FLAGS(RestoreOptions, RestoreOption)
|
||||
|
||||
///@internal
|
||||
inline Location oppositeLocation(Location loc)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -38,15 +38,52 @@
|
||||
#include "multisplitter/Item_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <qmath.h>
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
QHash<QString, LayoutSaver::DockWidget::Ptr> LayoutSaver::DockWidget::s_dockWidgets;
|
||||
LayoutSaver::Layout* LayoutSaver::Layout::s_currentLayoutBeingRestored = nullptr;
|
||||
|
||||
static QVariantMap sizeToMap(QSize sz)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("width"), sz.width());
|
||||
map.insert(QStringLiteral("height"), sz.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QVariantMap rectToMap(QRect rect)
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("x"), rect.x());
|
||||
map.insert(QStringLiteral("y"), rect.y());
|
||||
map.insert(QStringLiteral("width"), rect.width());
|
||||
map.insert(QStringLiteral("height"), rect.height());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static QSize mapToSize(const QVariantMap &map)
|
||||
{
|
||||
return { map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt() };
|
||||
}
|
||||
|
||||
static QRect mapToRect(const QVariantMap &map)
|
||||
{
|
||||
return QRect(map.value(QStringLiteral("x")).toInt(),
|
||||
map.value(QStringLiteral("y")).toInt(),
|
||||
map.value(QStringLiteral("width")).toInt(),
|
||||
map.value(QStringLiteral("height")).toInt());
|
||||
}
|
||||
|
||||
class KDDockWidgets::LayoutSaver::Private
|
||||
{
|
||||
@@ -66,12 +103,15 @@ public:
|
||||
Q_DISABLE_COPY(RAIIIsRestoring)
|
||||
};
|
||||
|
||||
Private()
|
||||
Private(RestoreOptions options)
|
||||
: m_dockRegistry(DockRegistry::self())
|
||||
, m_restoreOptions(options)
|
||||
{
|
||||
}
|
||||
|
||||
void serializeWindowGeometry(QDataStream &ds, QWidgetOrQuick *topLevel);
|
||||
bool matchesAffinity(const QString &affinityName) const {
|
||||
return m_affinityNames.isEmpty() || affinityName.isEmpty() || m_affinityNames.contains(affinityName);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel);
|
||||
@@ -80,13 +120,15 @@ public:
|
||||
|
||||
std::unique_ptr<QSettings> settings() const;
|
||||
DockRegistry *const m_dockRegistry;
|
||||
const RestoreOptions m_restoreOptions;
|
||||
QStringList m_affinityNames;
|
||||
static bool s_restoreInProgress;
|
||||
};
|
||||
|
||||
bool LayoutSaver::Private::s_restoreInProgress = false;
|
||||
|
||||
LayoutSaver::LayoutSaver()
|
||||
: d(new Private())
|
||||
LayoutSaver::LayoutSaver(RestoreOptions options)
|
||||
: d(new Private(options))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -95,30 +137,38 @@ LayoutSaver::~LayoutSaver()
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool LayoutSaver::saveToDisk()
|
||||
bool LayoutSaver::saveToFile(const QString &jsonFilename)
|
||||
{
|
||||
if (qApp->organizationName().isEmpty() || qApp->applicationName().isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO
|
||||
<< "Cannot save. Either organization name or application name is empty.";
|
||||
const QByteArray data = serializeLayout();
|
||||
|
||||
QFile f(jsonFilename);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray data = serializeLayout();
|
||||
d->settings()->setValue(QStringLiteral("data"), data);
|
||||
f.write(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayoutSaver::restoreFromDisk()
|
||||
bool LayoutSaver::restoreFromFile(const QString &jsonFilename)
|
||||
{
|
||||
const QByteArray data = d->settings()->value(QStringLiteral("data")).toByteArray();
|
||||
QFile f(jsonFilename);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray data = f.readAll();
|
||||
const bool result = restoreLayout(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray LayoutSaver::serializeLayout() const
|
||||
{
|
||||
if (!d->m_dockRegistry->isSane()) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to serialize insane layout";
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to serialize this layout. Check previous warnings.";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -128,37 +178,41 @@ QByteArray LayoutSaver::serializeLayout() const
|
||||
d->m_dockRegistry->ensureAllFloatingWidgetsAreMorphed();
|
||||
|
||||
const MainWindowBase::List mainWindows = d->m_dockRegistry->mainwindows();
|
||||
layout.mainWindows.reserve(mainWindows.size());
|
||||
for (MainWindowBase *mainWindow : mainWindows) {
|
||||
layout.mainWindows.push_back(mainWindow->serialize());
|
||||
if (d->matchesAffinity(mainWindow->affinityName()))
|
||||
layout.mainWindows.push_back(mainWindow->serialize());
|
||||
}
|
||||
|
||||
const QVector<KDDockWidgets::FloatingWindow*> floatingWindows = d->m_dockRegistry->nestedwindows();
|
||||
layout.floatingWindows.reserve(floatingWindows.size());
|
||||
for (KDDockWidgets::FloatingWindow *floatingWindow : floatingWindows) {
|
||||
layout.floatingWindows.push_back(floatingWindow->serialize());
|
||||
if (d->matchesAffinity(floatingWindow->affinityName()))
|
||||
layout.floatingWindows.push_back(floatingWindow->serialize());
|
||||
}
|
||||
|
||||
// Closed dock widgets also have interesting things to save, like geometry and placeholder info
|
||||
const DockWidgetBase::List closedDockWidgets = d->m_dockRegistry->closedDockwidgets();
|
||||
|
||||
layout.closedDockWidgets.reserve(closedDockWidgets.size());
|
||||
for (DockWidgetBase *dockWidget : closedDockWidgets) {
|
||||
layout.closedDockWidgets.push_back(dockWidget->serialize());
|
||||
if (d->matchesAffinity(dockWidget->affinityName()))
|
||||
layout.closedDockWidgets.push_back(dockWidget->serialize());
|
||||
}
|
||||
|
||||
// Save the placeholder info. We do it last, as we also restore it last, since we need all items to be created
|
||||
// before restoring the placeholders
|
||||
|
||||
const DockWidgetBase::List dockWidgets = d->m_dockRegistry->dockwidgets();
|
||||
layout.allDockWidgets.reserve(dockWidgets.size());
|
||||
for (DockWidgetBase *dockWidget : dockWidgets) {
|
||||
auto dw = dockWidget->serialize();
|
||||
dw->lastPosition = dockWidget->lastPosition()->serialize();
|
||||
layout.allDockWidgets.push_back(dw);
|
||||
if (d->matchesAffinity(dockWidget->affinityName())) {
|
||||
auto dw = dockWidget->serialize();
|
||||
dw->lastPosition = dockWidget->lastPosition()->serialize();
|
||||
layout.allDockWidgets.push_back(dw);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray result;
|
||||
QDataStream ds(&result, QIODevice::WriteOnly);
|
||||
ds << &layout;
|
||||
|
||||
return result;
|
||||
return layout.toJson();
|
||||
}
|
||||
|
||||
bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
@@ -167,10 +221,36 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
if (data.isEmpty())
|
||||
return true;
|
||||
|
||||
struct EnsureItemsAtCorrectPlace {
|
||||
|
||||
EnsureItemsAtCorrectPlace(LayoutSaver *ls)
|
||||
: layoutSaver(ls)
|
||||
{
|
||||
}
|
||||
|
||||
~EnsureItemsAtCorrectPlace()
|
||||
{
|
||||
// When using RestoreOption_RelativeToMainWindow we'll have many rounding errors so the layout won't be exact.
|
||||
// Make sure to run a relayout at the end
|
||||
// (Using RAII to make sure it runs after Private::RAIIIsRestoring went out of scope, since "isRestoring= true" inhibits relayout
|
||||
if (ensure) {
|
||||
for (auto layout : DockRegistry::self()->layouts()) {
|
||||
if (layoutSaver->d->matchesAffinity(layout->affinityName()))
|
||||
layout->redistributeSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ensure = false;
|
||||
LayoutSaver *const layoutSaver;
|
||||
};
|
||||
|
||||
EnsureItemsAtCorrectPlace ensureItemsAtCorrectPlace(this);
|
||||
Private::RAIIIsRestoring isRestoring;
|
||||
|
||||
struct FrameCleanup {
|
||||
FrameCleanup(LayoutSaver *saver) : m_saver(saver)
|
||||
FrameCleanup(LayoutSaver *saver)
|
||||
: m_saver(saver)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -180,17 +260,20 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
}
|
||||
|
||||
LayoutSaver *const m_saver;
|
||||
|
||||
};
|
||||
|
||||
FrameCleanup cleanup(this);
|
||||
|
||||
LayoutSaver::Layout layout;
|
||||
if (!layout.fillFrom(data))
|
||||
if (!layout.fromJson(data)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to parse json data";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->m_restoreOptions & RestoreOption_RelativeToMainWindow)
|
||||
layout.scaleSizes();
|
||||
|
||||
// Hide all dockwidgets and unparent them from any layout before starting restore
|
||||
d->m_dockRegistry->clear(/*deleteStaticAnchors=*/true);
|
||||
d->m_dockRegistry->clear(d->m_affinityNames, /*deleteStaticAnchors=*/true);
|
||||
|
||||
// 1. Restore main windows
|
||||
for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) {
|
||||
@@ -200,7 +283,11 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
d->deserializeWindowGeometry(mw, mainWindow->window()); // window() as the MainWindow can be embedded
|
||||
if (!d->matchesAffinity(mainWindow->affinityName()))
|
||||
continue;
|
||||
|
||||
if (!(d->m_restoreOptions & RestoreOption_RelativeToMainWindow))
|
||||
d->deserializeWindowGeometry(mw, mainWindow->window()); // window(), as the MainWindow can be embedded
|
||||
|
||||
if (!mainWindow->deserialize(mw))
|
||||
return false;
|
||||
@@ -208,8 +295,11 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
|
||||
// 2. Restore FloatingWindows
|
||||
for (const LayoutSaver::FloatingWindow &fw : qAsConst(layout.floatingWindows)) {
|
||||
QWidget *parent = fw.parentIndex == -1 ? nullptr
|
||||
: DockRegistry::self()->mainwindows().at(fw.parentIndex);
|
||||
if (!d->matchesAffinity(fw.affinityName))
|
||||
continue;
|
||||
|
||||
MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
|
||||
: DockRegistry::self()->mainwindows().at(fw.parentIndex);
|
||||
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
|
||||
d->deserializeWindowGeometry(fw, floatingWindow);
|
||||
@@ -220,11 +310,16 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
|
||||
// 3. Restore closed dock widgets. They remain closed but acquire geometry and placeholder properties
|
||||
for (const auto &dw : qAsConst(layout.closedDockWidgets)) {
|
||||
DockWidgetBase::deserialize(dw);
|
||||
if (d->matchesAffinity(dw->affinityName)) {
|
||||
DockWidgetBase::deserialize(dw);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Restore the placeholder info, now that the Items have been created
|
||||
for (const auto &dw : qAsConst(layout.allDockWidgets)) {
|
||||
if (!d->matchesAffinity(dw->affinityName))
|
||||
continue;
|
||||
|
||||
if (DockWidgetBase *dockWidget = d->m_dockRegistry->dockByName(dw->uniqueName)) {
|
||||
dockWidget->lastPosition()->deserialize(dw->lastPosition);
|
||||
} else {
|
||||
@@ -232,9 +327,21 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
|
||||
}
|
||||
}
|
||||
|
||||
// our raii class will run when
|
||||
ensureItemsAtCorrectPlace.ensure = d->m_restoreOptions & RestoreOption_RelativeToMainWindow;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::setAffinityNames(const QStringList &affinityNames)
|
||||
{
|
||||
d->m_affinityNames = affinityNames;
|
||||
if (affinityNames.contains(QString())) {
|
||||
// Any window with empty affinity will also be subject to save/restore
|
||||
d->m_affinityNames << QString();
|
||||
}
|
||||
}
|
||||
|
||||
DockWidgetBase::List LayoutSaver::restoredDockWidgets() const
|
||||
{
|
||||
const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
|
||||
@@ -314,15 +421,103 @@ bool LayoutSaver::Layout::fillFrom(const QByteArray &serialized)
|
||||
QDataStream ds(serialized);
|
||||
ds >> this;
|
||||
|
||||
if (serializationVersion != KDDOCKWIDGETS_SERIALIZATION_VERSION) {
|
||||
if (serializationVersion > KDDOCKWIDGETS_SERIALIZATION_VERSION) {
|
||||
qWarning() << "Unsupported serialization version. Got=" << serializationVersion
|
||||
<< "; expected=" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
|
||||
<< "; expected equal or less than" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray LayoutSaver::Layout::toJson() const
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromVariant(toVariantMap());
|
||||
return doc.toJson();
|
||||
}
|
||||
|
||||
bool LayoutSaver::Layout::fromJson(const QByteArray &jsonData)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
||||
if (error.error == QJsonParseError::NoError) {
|
||||
fromVariantMap(doc.toVariant().toMap());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Layout::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("serializationVersion"), serializationVersion);
|
||||
map.insert(QStringLiteral("mainWindows"), toVariantList<LayoutSaver::MainWindow>(mainWindows));
|
||||
map.insert(QStringLiteral("floatingWindows"), toVariantList<LayoutSaver::FloatingWindow>(floatingWindows));
|
||||
map.insert(QStringLiteral("closedDockWidgets"), dockWidgetNames(closedDockWidgets));
|
||||
map.insert(QStringLiteral("allDockWidgets"), toVariantList(allDockWidgets));
|
||||
map.insert(QStringLiteral("screenInfo"), toVariantList<LayoutSaver::ScreenInfo>(screenInfo));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Layout::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
allDockWidgets.clear();
|
||||
const QVariantList dockWidgetsV = map.value(QStringLiteral("allDockWidgets")).toList();
|
||||
for (const QVariant &v : dockWidgetsV) {
|
||||
const QVariantMap dwV = v.toMap();
|
||||
const QString name = dwV.value(QStringLiteral("uniqueName")).toString();
|
||||
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
|
||||
dw->fromVariantMap(dwV);
|
||||
allDockWidgets.push_back(dw);
|
||||
}
|
||||
|
||||
closedDockWidgets.clear();
|
||||
const QVariantList closedDockWidgetsV = map.value(QStringLiteral("closedDockWidgets")).toList();
|
||||
closedDockWidgets.reserve(closedDockWidgetsV.size());
|
||||
for (const QVariant &v : closedDockWidgetsV) {
|
||||
closedDockWidgets.push_back(LayoutSaver::DockWidget::dockWidgetForName(v.toString()));
|
||||
}
|
||||
|
||||
serializationVersion = map.value(QStringLiteral("serializationVersion")).toInt();
|
||||
mainWindows = fromVariantList<LayoutSaver::MainWindow>(map.value(QStringLiteral("mainWindows")).toList());
|
||||
floatingWindows = fromVariantList<LayoutSaver::FloatingWindow>(map.value(QStringLiteral("floatingWindows")).toList());
|
||||
screenInfo = fromVariantList<LayoutSaver::ScreenInfo>(map.value(QStringLiteral("screenInfo")).toList());
|
||||
}
|
||||
|
||||
void LayoutSaver::Layout::scaleSizes()
|
||||
{
|
||||
if (mainWindows.isEmpty())
|
||||
return;
|
||||
|
||||
for (auto &mw : mainWindows)
|
||||
mw.scaleSizes();
|
||||
|
||||
for (auto &fw : floatingWindows) {
|
||||
LayoutSaver::MainWindow mw = mainWindowForIndex(fw.parentIndex);
|
||||
if (mw.scalingInfo.isValid())
|
||||
fw.scaleSizes(mw.scalingInfo);
|
||||
}
|
||||
|
||||
const ScalingInfo firstScalingInfo = mainWindows.constFirst().scalingInfo;
|
||||
if (firstScalingInfo.isValid()) {
|
||||
for (auto &dw : allDockWidgets) {
|
||||
// TODO: Determine the best main window. This only interesting for closed dock widget geometry
|
||||
// which was previously floating. But they still have some other main window as parent.
|
||||
dw->scaleSizes(firstScalingInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
|
||||
{
|
||||
if (index < 0 || index >= mainWindows.size())
|
||||
return {};
|
||||
|
||||
return mainWindows.at(index);
|
||||
}
|
||||
|
||||
bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
|
||||
{
|
||||
if (!frame.isValid())
|
||||
@@ -331,9 +526,9 @@ bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout)
|
||||
const int numAnchors = layout.anchors.size();
|
||||
|
||||
if (indexOfLeftAnchor < 0 || indexOfTopAnchor < 0 ||
|
||||
indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 ||
|
||||
indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors ||
|
||||
indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) {
|
||||
indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 ||
|
||||
indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors ||
|
||||
indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid anchor indexes"
|
||||
<< indexOfLeftAnchor << indexOfTopAnchor
|
||||
<< indexOfBottomAnchor << indexOfRightAnchor;
|
||||
@@ -343,6 +538,43 @@ bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout)
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::Item::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(geometry);
|
||||
if (!frame.isNull)
|
||||
frame.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Item::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("isPlaceholder"), isPlaceholder);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
|
||||
map.insert(QStringLiteral("indexOfLeftAnchor"), indexOfLeftAnchor);
|
||||
map.insert(QStringLiteral("indexOfTopAnchor"), indexOfTopAnchor);
|
||||
map.insert(QStringLiteral("indexOfRightAnchor"), indexOfRightAnchor);
|
||||
map.insert(QStringLiteral("indexOfBottomAnchor"), indexOfBottomAnchor);
|
||||
if (!frame.isNull)
|
||||
map.insert(QStringLiteral("frame"), frame.toVariantMap());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Item::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
isPlaceholder = map.value(QStringLiteral("isPlaceholder")).toBool();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
|
||||
indexOfLeftAnchor = map.value(QStringLiteral("indexOfLeftAnchor")).toInt();
|
||||
indexOfTopAnchor = map.value(QStringLiteral("indexOfTopAnchor")).toInt();
|
||||
indexOfRightAnchor = map.value(QStringLiteral("indexOfRightAnchor")).toInt();
|
||||
indexOfBottomAnchor = map.value(QStringLiteral("indexOfBottomAnchor")).toInt();
|
||||
frame.fromVariantMap(map.value(QStringLiteral("frame"), QVariantMap()).toMap());
|
||||
}
|
||||
|
||||
bool LayoutSaver::Frame::isValid() const
|
||||
{
|
||||
if (!isNull)
|
||||
@@ -353,7 +585,7 @@ bool LayoutSaver::Frame::isValid() const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options < 0 || options > 3) {
|
||||
if (options > 3) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid options" << options;
|
||||
return false;
|
||||
}
|
||||
@@ -371,11 +603,77 @@ bool LayoutSaver::Frame::isValid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(geometry);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Frame::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("isNull"), isNull);
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("options"), options);
|
||||
map.insert(QStringLiteral("currentTabIndex"), currentTabIndex);
|
||||
|
||||
map.insert(QStringLiteral("dockWidgets"), dockWidgetNames(dockWidgets));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
if (map.isEmpty()) {
|
||||
isNull = true;
|
||||
dockWidgets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
isNull = map.value(QStringLiteral("isNull")).toBool();
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
options = map.value(QStringLiteral("options")).toUInt();
|
||||
currentTabIndex = map.value(QStringLiteral("currentTabIndex")).toInt();
|
||||
|
||||
QVariantList dockWidgetsV = map.value(QStringLiteral("dockWidgets")).toList();
|
||||
|
||||
dockWidgets.clear();
|
||||
dockWidgets.reserve(dockWidgetsV.size());
|
||||
for (const auto &variant : dockWidgetsV) {
|
||||
DockWidget::Ptr dw = DockWidget::dockWidgetForName(variant.toString());
|
||||
dockWidgets.push_back(dw);
|
||||
}
|
||||
}
|
||||
|
||||
bool LayoutSaver::DockWidget::isValid() const
|
||||
{
|
||||
return !uniqueName.isEmpty();
|
||||
}
|
||||
|
||||
void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
lastPosition.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::DockWidget::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
if (!affinityName.isEmpty())
|
||||
map.insert(QStringLiteral("affinityName"), affinityName);
|
||||
map.insert(QStringLiteral("uniqueName"), uniqueName);
|
||||
map.insert(QStringLiteral("lastPosition"), lastPosition.toVariantMap());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
affinityName = map.value(QStringLiteral("affinityName")).toString();
|
||||
uniqueName = map.value(QStringLiteral("uniqueName")).toString();
|
||||
lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap());
|
||||
}
|
||||
|
||||
bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
|
||||
{
|
||||
const bool isStatic = type != KDDockWidgets::Anchor::Type_None;
|
||||
@@ -423,6 +721,73 @@ bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Anchor::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("objectName"), objectName);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("orientation"), orientation);
|
||||
map.insert(QStringLiteral("type"), type);
|
||||
map.insert(QStringLiteral("indexOfFrom"), indexOfFrom);
|
||||
map.insert(QStringLiteral("indexOfTo"), indexOfTo);
|
||||
map.insert(QStringLiteral("indexOfFollowee"), indexOfFollowee);
|
||||
map.insert(QStringLiteral("positionPercentage"), positionPercentage);
|
||||
|
||||
QVariantList side1ItemsV;
|
||||
QVariantList side2ItemsV;
|
||||
side1ItemsV.reserve(side1Items.size());
|
||||
side2ItemsV.reserve(side2Items.size());
|
||||
for (int index : qAsConst(side1Items))
|
||||
side1ItemsV.push_back(index);
|
||||
for (int index : qAsConst(side2Items))
|
||||
side2ItemsV.push_back(index);
|
||||
|
||||
map.insert(QStringLiteral("side1Items"), side1ItemsV);
|
||||
map.insert(QStringLiteral("side2Items"), side2ItemsV);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Anchor::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
objectName = map.value(QStringLiteral("objectName")).toString();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
|
||||
orientation = map.value(QStringLiteral("orientation")).toInt();
|
||||
type = map.value(QStringLiteral("type")).toInt();
|
||||
indexOfFrom = map.value(QStringLiteral("indexOfFrom")).toInt();
|
||||
indexOfTo = map.value(QStringLiteral("indexOfTo")).toInt();
|
||||
indexOfFollowee = map.value(QStringLiteral("indexOfFollowee")).toInt();
|
||||
positionPercentage = map.value(QStringLiteral("positionPercentage")).toDouble();
|
||||
|
||||
side1Items.clear();
|
||||
side2Items.clear();
|
||||
const QVariantList side1ItemsV = map.value(QStringLiteral("side1Items")).toList();
|
||||
const QVariantList side2ItemsV = map.value(QStringLiteral("side2Items")).toList();
|
||||
side1Items.reserve(side1ItemsV.size());
|
||||
side2Items.reserve(side2ItemsV.size());
|
||||
for (const QVariant &v : side1ItemsV)
|
||||
side1Items.push_back(v.toInt());
|
||||
for (const QVariant &v : side2ItemsV)
|
||||
side2Items.push_back(v.toInt());
|
||||
}
|
||||
|
||||
void LayoutSaver::Anchor::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
const QPoint pos = geometry.topLeft();
|
||||
|
||||
if (isVertical()) {
|
||||
geometry.moveLeft(int(pos.x() * scalingInfo.widthFactor));
|
||||
} else {
|
||||
geometry.moveTop(int(pos.y() * scalingInfo.heightFactor));
|
||||
}
|
||||
}
|
||||
|
||||
bool LayoutSaver::Anchor::isVertical() const
|
||||
{
|
||||
return orientation == Qt::Vertical;
|
||||
}
|
||||
|
||||
bool LayoutSaver::FloatingWindow::isValid() const
|
||||
{
|
||||
if (!multiSplitterLayout.isValid())
|
||||
@@ -436,6 +801,39 @@ bool LayoutSaver::FloatingWindow::isValid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(/*by-ref*/geometry);
|
||||
multiSplitterLayout.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
|
||||
map.insert(QStringLiteral("parentIndex"), parentIndex);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("screenIndex"), screenIndex);
|
||||
map.insert(QStringLiteral("screenSize"), sizeToMap(screenSize));
|
||||
map.insert(QStringLiteral("isVisible"), isVisible);
|
||||
|
||||
if (!affinityName.isEmpty())
|
||||
map.insert(QStringLiteral("affinityName"), affinityName);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::FloatingWindow::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
|
||||
parentIndex = map.value(QStringLiteral("parentIndex")).toInt();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
|
||||
screenSize = mapToSize(map.value(QStringLiteral("screenSize")).toMap());
|
||||
isVisible = map.value(QStringLiteral("isVisible")).toBool();
|
||||
affinityName = map.value(QStringLiteral("affinityName")).toString();
|
||||
}
|
||||
|
||||
bool LayoutSaver::MainWindow::isValid() const
|
||||
{
|
||||
if (!multiSplitterLayout.isValid())
|
||||
@@ -449,6 +847,50 @@ bool LayoutSaver::MainWindow::isValid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::MainWindow::scaleSizes()
|
||||
{
|
||||
if (scalingInfo.isValid()) {
|
||||
// Doesn't happen, it's called only once
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
scalingInfo = ScalingInfo(uniqueName, geometry);
|
||||
|
||||
if (scalingInfo.isValid())
|
||||
multiSplitterLayout.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::MainWindow::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("options"), int(options));
|
||||
map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
|
||||
map.insert(QStringLiteral("uniqueName"), uniqueName);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("screenIndex"), screenIndex);
|
||||
map.insert(QStringLiteral("screenSize"), sizeToMap(screenSize));
|
||||
map.insert(QStringLiteral("isVisible"), isVisible);
|
||||
|
||||
if (!affinityName.isEmpty())
|
||||
map.insert(QStringLiteral("affinityName"), affinityName);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
options = KDDockWidgets::MainWindowOptions(map.value(QStringLiteral("options")).toInt());
|
||||
multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
|
||||
uniqueName = map.value(QStringLiteral("uniqueName")).toString();
|
||||
affinityName = map.value(QStringLiteral("affinityName")).toString();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
|
||||
screenSize = mapToSize(map.value(QStringLiteral("screenSize")).toMap());
|
||||
|
||||
isVisible = map.value(QStringLiteral("isVisible")).toBool();
|
||||
}
|
||||
|
||||
bool LayoutSaver::MultiSplitterLayout::isValid() const
|
||||
{
|
||||
for (auto &item : items) {
|
||||
@@ -468,3 +910,161 @@ bool LayoutSaver::MultiSplitterLayout::isValid() const
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayoutSaver::MultiSplitterLayout::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(/*by-ref*/size);
|
||||
for (LayoutSaver::Anchor &anchor : anchors)
|
||||
anchor.scaleSizes(scalingInfo);
|
||||
for (LayoutSaver::Item &item : items)
|
||||
item.scaleSizes(scalingInfo);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::MultiSplitterLayout::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
|
||||
map.insert(QStringLiteral("anchors"), toVariantList<LayoutSaver::Anchor>(anchors));
|
||||
map.insert(QStringLiteral("items"), toVariantList<LayoutSaver::Item>(items));
|
||||
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
|
||||
map.insert(QStringLiteral("size"), sizeToMap(size));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::MultiSplitterLayout::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
anchors = fromVariantList<LayoutSaver::Anchor>(map.value(QStringLiteral("anchors")).toList());
|
||||
items = fromVariantList<LayoutSaver::Item>(map.value(QStringLiteral("items")).toList());
|
||||
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
|
||||
size = mapToSize(map.value(QStringLiteral("size")).toMap());
|
||||
}
|
||||
|
||||
void LayoutSaver::LastPosition::scaleSizes(const ScalingInfo &scalingInfo)
|
||||
{
|
||||
scalingInfo.applyFactorsTo(/*by-ref*/lastFloatingGeometry);
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::LastPosition::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("lastFloatingGeometry"), rectToMap(lastFloatingGeometry));
|
||||
map.insert(QStringLiteral("tabIndex"), tabIndex);
|
||||
map.insert(QStringLiteral("wasFloating"), wasFloating);
|
||||
map.insert(QStringLiteral("placeholders"), toVariantList<LayoutSaver::Placeholder>(placeholders));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::LastPosition::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
lastFloatingGeometry = mapToRect(map.value(QStringLiteral("lastFloatingGeometry")).toMap());
|
||||
tabIndex = map.value(QStringLiteral("tabIndex")).toInt();
|
||||
wasFloating = map.value(QStringLiteral("wasFloating")).toBool();
|
||||
placeholders = fromVariantList<LayoutSaver::Placeholder>(map.value(QStringLiteral("placeholders")).toList());
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::ScreenInfo::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("index"), index);
|
||||
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
|
||||
map.insert(QStringLiteral("name"), name);
|
||||
map.insert(QStringLiteral("devicePixelRatio"), devicePixelRatio);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::ScreenInfo::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
index = map.value(QStringLiteral("index")).toInt();
|
||||
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
|
||||
name = map.value(QStringLiteral("name")).toString();
|
||||
devicePixelRatio = map.value(QStringLiteral("devicePixelRatio")).toDouble();
|
||||
}
|
||||
|
||||
QVariantMap LayoutSaver::Placeholder::toVariantMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(QStringLiteral("isFloatingWindow"), isFloatingWindow);
|
||||
map.insert(QStringLiteral("itemIndex"), itemIndex);
|
||||
|
||||
if (isFloatingWindow)
|
||||
map.insert(QStringLiteral("indexOfFloatingWindow"), indexOfFloatingWindow);
|
||||
else
|
||||
map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void LayoutSaver::Placeholder::fromVariantMap(const QVariantMap &map)
|
||||
{
|
||||
isFloatingWindow = map.value(QStringLiteral("isFloatingWindow")).toBool();
|
||||
indexOfFloatingWindow = map.value(QStringLiteral("indexOfFloatingWindow"), -1).toInt();
|
||||
itemIndex = map.value(QStringLiteral("itemIndex")).toInt();
|
||||
mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
|
||||
}
|
||||
|
||||
LayoutSaver::ScalingInfo::ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo)
|
||||
{
|
||||
auto mainWindow = DockRegistry::self()->mainWindowByName(mainWindowId);
|
||||
if (!mainWindow) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to find main window with name" << mainWindowName;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!savedMainWindowGeo.isValid() || savedMainWindowGeo.isNull()) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid saved main window geometry" << savedMainWindowGeo;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mainWindow->geometry().isValid() || mainWindow->geometry().isNull()) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid main window geometry" << mainWindow->geometry();
|
||||
return;
|
||||
}
|
||||
|
||||
this->mainWindowName = mainWindowId;
|
||||
this->savedMainWindowGeometry = savedMainWindowGeo;
|
||||
realMainWindowGeometry = mainWindow->window()->geometry(); // window() as our main window might be embedded
|
||||
widthFactor = double(realMainWindowGeometry.width()) / savedMainWindowGeo.width();
|
||||
heightFactor = double(realMainWindowGeometry.height()) / savedMainWindowGeo.height();
|
||||
}
|
||||
|
||||
void LayoutSaver::ScalingInfo::translatePos(QPoint &pt) const
|
||||
{
|
||||
const int deltaX = pt.x() - savedMainWindowGeometry.x();
|
||||
const int deltaY = pt.y() - savedMainWindowGeometry.y();
|
||||
|
||||
const double newDeltaX = deltaX * widthFactor;
|
||||
const double newDeltaY = deltaY * heightFactor;
|
||||
|
||||
pt.setX(qCeil(savedMainWindowGeometry.x() + newDeltaX));
|
||||
pt.setY(qCeil(savedMainWindowGeometry.y() + newDeltaY));
|
||||
}
|
||||
|
||||
void LayoutSaver::ScalingInfo::applyFactorsTo(QPoint &pt) const
|
||||
{
|
||||
pt.setX(qCeil(pt.x() * widthFactor));
|
||||
pt.setY(qCeil(pt.y() * heightFactor));
|
||||
}
|
||||
|
||||
void LayoutSaver::ScalingInfo::applyFactorsTo(QSize &sz) const
|
||||
{
|
||||
sz.setWidth(int(widthFactor * sz.width()));
|
||||
sz.setHeight(int(heightFactor * sz.height()));
|
||||
}
|
||||
|
||||
void LayoutSaver::ScalingInfo::applyFactorsTo(QRect &rect) const
|
||||
{
|
||||
if (rect.isEmpty())
|
||||
return;
|
||||
|
||||
QPoint pos = rect.topLeft();
|
||||
QSize size = rect.size();
|
||||
|
||||
applyFactorsTo(/*by-ref*/size);
|
||||
applyFactorsTo(/*by-ref*/pos);
|
||||
|
||||
rect.moveTopLeft(pos);
|
||||
rect.setSize(size);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,11 @@
|
||||
|
||||
#include "docks_export.h"
|
||||
|
||||
#include "KDDockWidgets.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QByteArray;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
@@ -40,7 +44,7 @@ class DOCKS_EXPORT LayoutSaver
|
||||
{
|
||||
public:
|
||||
///@brief Constructor. Construction on the stack is suggested.
|
||||
LayoutSaver();
|
||||
explicit LayoutSaver(RestoreOptions options = RestoreOption_None);
|
||||
|
||||
///@brief Destructor.
|
||||
~LayoutSaver();
|
||||
@@ -49,15 +53,18 @@ public:
|
||||
static bool restoreInProgress();
|
||||
|
||||
/**
|
||||
* @brief saves the layout to disk using QSettings
|
||||
*/
|
||||
bool saveToDisk();
|
||||
|
||||
/**
|
||||
* @brief restores the layout from disk using QSettings.
|
||||
* @brief saves the layout to JSON file
|
||||
* @brief jsonFilename the filename where the layout will be saved to
|
||||
* @return true on success
|
||||
*/
|
||||
bool restoreFromDisk();
|
||||
bool saveToFile(const QString &jsonFilename);
|
||||
|
||||
/**
|
||||
* @brief restores the layout from a JSON file
|
||||
* @brief jsonFilename the filename containing a saved layout
|
||||
* @return true on success
|
||||
*/
|
||||
bool restoreFromFile(const QString &jsonFilename);
|
||||
|
||||
/**
|
||||
* @brief saves the layout into a byte array
|
||||
@@ -86,6 +93,15 @@ public:
|
||||
*/
|
||||
QVector<DockWidgetBase *> restoredDockWidgets() const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets the list of affinity names for which restore and save will be applied on.
|
||||
* Allows to save/restore only a subset of the windows.
|
||||
* Empty by default, all windows are subject to save/restore.
|
||||
* Any window with empty affinity will also be subject to save/restore, regardless of @p affinityNames.
|
||||
*/
|
||||
void setAffinityNames(const QStringList &affinityNames);
|
||||
|
||||
struct Layout;
|
||||
struct MainWindow;
|
||||
struct FloatingWindow;
|
||||
@@ -96,6 +112,8 @@ public:
|
||||
struct Anchor;
|
||||
struct Frame;
|
||||
struct Placeholder;
|
||||
struct ScalingInfo;
|
||||
struct ScreenInfo;
|
||||
|
||||
private:
|
||||
friend class TestDocks;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,25 +27,86 @@
|
||||
#include <QRect>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QScreen>
|
||||
#include <QApplication>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define ANCHOR_MAGIC_MARKER "e520c60e-cf5d-4a30-b1a7-588d2c569851"
|
||||
#define MULTISPLITTER_LAYOUT_MAGIC_MARKER "bac9948e-5f1b-4271-acc5-07f1708e2611"
|
||||
|
||||
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 1
|
||||
/**
|
||||
* Bump whenever the format changes, so we can still load old layouts.
|
||||
* version 1: Initial version
|
||||
* version 2: Introduced MainWindow::screenSize and FloatingWindow::screenSize
|
||||
*/
|
||||
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 2
|
||||
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
template <typename T>
|
||||
typename T::List fromVariantList(const QVariantList &listV)
|
||||
{
|
||||
typename T::List result;
|
||||
|
||||
result.reserve(listV.size());
|
||||
for (const QVariant &v : listV) {
|
||||
T t;
|
||||
t.fromVariantMap(v.toMap());
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVariantList toVariantList(const typename T::List &list)
|
||||
{
|
||||
QVariantList result;
|
||||
result.reserve(list.size());
|
||||
for (const T &v : list)
|
||||
result.push_back(v.toVariantMap());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct LayoutSaver::Placeholder
|
||||
{
|
||||
typedef QVector<LayoutSaver::Placeholder> List;
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
bool isFloatingWindow;
|
||||
int indexOfFloatingWindow;
|
||||
int itemIndex;
|
||||
QString mainWindowUniqueName;
|
||||
};
|
||||
|
||||
///@brief contains info about how a main window is scaled.
|
||||
///Used for RestoreOption_RelativeToMainWindow
|
||||
struct LayoutSaver::ScalingInfo
|
||||
{
|
||||
ScalingInfo() = default;
|
||||
explicit ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo);
|
||||
|
||||
bool isValid() const {
|
||||
return heightFactor > 0 && widthFactor > 0 && !((qFuzzyCompare(widthFactor, 1) && qFuzzyCompare(heightFactor, 1)));
|
||||
}
|
||||
|
||||
void translatePos(QPoint &) const;
|
||||
void applyFactorsTo(QPoint &) const;
|
||||
void applyFactorsTo(QSize &) const;
|
||||
void applyFactorsTo(QRect &) const;
|
||||
|
||||
QString mainWindowName;
|
||||
QRect savedMainWindowGeometry;
|
||||
QRect realMainWindowGeometry;
|
||||
double heightFactor = -1;
|
||||
double widthFactor = -1;
|
||||
};
|
||||
|
||||
struct LayoutSaver::LastPosition
|
||||
{
|
||||
@@ -53,6 +114,12 @@ struct LayoutSaver::LastPosition
|
||||
int tabIndex;
|
||||
bool wasFloating;
|
||||
LayoutSaver::Placeholder::List placeholders;
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
};
|
||||
|
||||
struct DOCKS_EXPORT LayoutSaver::DockWidget
|
||||
@@ -64,6 +131,9 @@ struct DOCKS_EXPORT LayoutSaver::DockWidget
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
static Ptr dockWidgetForName(const QString &name)
|
||||
{
|
||||
auto dw = s_dockWidgets.value(name);
|
||||
@@ -71,26 +141,58 @@ struct DOCKS_EXPORT LayoutSaver::DockWidget
|
||||
return dw;
|
||||
|
||||
dw = Ptr(new LayoutSaver::DockWidget);
|
||||
s_dockWidgets.insert(name, dw);
|
||||
dw->uniqueName = name;
|
||||
|
||||
return dw;
|
||||
}
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
QString uniqueName;
|
||||
QString affinityName;
|
||||
LayoutSaver::LastPosition lastPosition;
|
||||
|
||||
private:
|
||||
DockWidget() {}
|
||||
};
|
||||
|
||||
|
||||
inline QVariantList toVariantList(const LayoutSaver::DockWidget::List &list)
|
||||
{
|
||||
QVariantList result;
|
||||
result.reserve(list.size());
|
||||
for (const auto &dw : list)
|
||||
result.push_back(dw->toVariantMap());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline QVariantList dockWidgetNames(const LayoutSaver::DockWidget::List &list)
|
||||
{
|
||||
QVariantList result;
|
||||
result.reserve(list.size());
|
||||
for (auto &dw : list)
|
||||
result.push_back(dw->uniqueName);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct LayoutSaver::Frame
|
||||
{
|
||||
bool isValid() const;
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
bool isNull = true;
|
||||
QString objectName;
|
||||
QRect geometry;
|
||||
int options;
|
||||
unsigned int options;
|
||||
int currentTabIndex;
|
||||
|
||||
LayoutSaver::DockWidget::List dockWidgets;
|
||||
@@ -101,17 +203,20 @@ struct LayoutSaver::Item
|
||||
typedef QVector<LayoutSaver::Item> List;
|
||||
|
||||
bool isValid(const MultiSplitterLayout &) const;
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
QString objectName;
|
||||
bool isPlaceholder;
|
||||
QRect geometry;
|
||||
QSize minSize;
|
||||
|
||||
int indexOfLeftAnchor;
|
||||
int indexOfTopAnchor;
|
||||
int indexOfRightAnchor;
|
||||
int indexOfBottomAnchor;
|
||||
|
||||
LayoutSaver::Frame frame;
|
||||
};
|
||||
|
||||
@@ -121,8 +226,15 @@ struct LayoutSaver::Anchor
|
||||
|
||||
bool isValid(const LayoutSaver::MultiSplitterLayout &layout) const;
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
void scaleSizes(const ScalingInfo &);
|
||||
|
||||
bool isVertical() const;
|
||||
|
||||
QString objectName;
|
||||
QRect geometry;
|
||||
double positionPercentage;
|
||||
int orientation;
|
||||
int type;
|
||||
int indexOfFrom;
|
||||
@@ -135,6 +247,11 @@ struct LayoutSaver::Anchor
|
||||
struct LayoutSaver::MultiSplitterLayout
|
||||
{
|
||||
bool isValid() const;
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &scalingInfo);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
LayoutSaver::Anchor::List anchors;
|
||||
LayoutSaver::Item::List items;
|
||||
@@ -148,9 +265,18 @@ struct LayoutSaver::FloatingWindow
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes(const ScalingInfo &);
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
|
||||
QString affinityName;
|
||||
int parentIndex = -1;
|
||||
QRect geometry;
|
||||
int screenIndex;
|
||||
QSize screenSize; // for relative-size restoring
|
||||
bool isVisible = true;
|
||||
};
|
||||
|
||||
@@ -161,36 +287,91 @@ public:
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes();
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
KDDockWidgets::MainWindowOptions options;
|
||||
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
|
||||
QString uniqueName;
|
||||
QString affinityName;
|
||||
QRect geometry;
|
||||
int screenIndex;
|
||||
QSize screenSize; // for relative-size restoring
|
||||
bool isVisible;
|
||||
|
||||
ScalingInfo scalingInfo;
|
||||
};
|
||||
|
||||
///@brief we serialize some info about screens, so eventually we can make restore smarter when switching screens
|
||||
///Not used currently, but nice to have in the json already
|
||||
struct LayoutSaver::ScreenInfo
|
||||
{
|
||||
typedef QVector<LayoutSaver::ScreenInfo> List;
|
||||
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
int index;
|
||||
QRect geometry;
|
||||
QString name;
|
||||
double devicePixelRatio;
|
||||
};
|
||||
|
||||
struct LayoutSaver::Layout
|
||||
{
|
||||
public:
|
||||
|
||||
Layout() {
|
||||
s_currentLayoutBeingRestored = this;
|
||||
|
||||
const QList<QScreen*> screens = qApp->screens();
|
||||
for (int i = 0; i < screens.size(); ++i) {
|
||||
ScreenInfo info;
|
||||
info.index = i;
|
||||
info.geometry = screens[i]->geometry();
|
||||
info.name = screens[i]->name();
|
||||
info.devicePixelRatio = screens[i]->devicePixelRatio();
|
||||
screenInfo.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
~Layout() {
|
||||
s_currentLayoutBeingRestored = nullptr;
|
||||
}
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool fillFrom(const QByteArray &serialized);
|
||||
QByteArray toJson() const;
|
||||
bool fromJson(const QByteArray &jsonData);
|
||||
QVariantMap toVariantMap() const;
|
||||
void fromVariantMap(const QVariantMap &map);
|
||||
|
||||
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
|
||||
void scaleSizes();
|
||||
|
||||
friend QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame);
|
||||
static LayoutSaver::Layout* s_currentLayoutBeingRestored;
|
||||
|
||||
LayoutSaver::MainWindow mainWindowForIndex(int index) const;
|
||||
|
||||
int serializationVersion = KDDOCKWIDGETS_SERIALIZATION_VERSION;
|
||||
LayoutSaver::MainWindow::List mainWindows;
|
||||
LayoutSaver::FloatingWindow::List floatingWindows;
|
||||
LayoutSaver::DockWidget::List closedDockWidgets;
|
||||
LayoutSaver::DockWidget::List allDockWidgets;
|
||||
ScreenInfo::List screenInfo;
|
||||
};
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Placeholder *p)
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::ScreenInfo *info)
|
||||
{
|
||||
ds << p->isFloatingWindow;
|
||||
if (p->isFloatingWindow)
|
||||
ds << p->indexOfFloatingWindow;
|
||||
else
|
||||
ds << p->mainWindowUniqueName;
|
||||
|
||||
ds << p->itemIndex;
|
||||
ds >> info->index;
|
||||
ds >> info->geometry;
|
||||
ds >> info->name;
|
||||
ds >> info->devicePixelRatio;
|
||||
|
||||
return ds;
|
||||
}
|
||||
@@ -208,22 +389,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Placeholder *p)
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Anchor *a)
|
||||
{
|
||||
ds << QStringLiteral(ANCHOR_MAGIC_MARKER);
|
||||
ds << a->objectName;
|
||||
ds << a->geometry;
|
||||
ds << a->orientation;
|
||||
ds << a->type;
|
||||
ds << a->indexOfFrom;
|
||||
ds << a->indexOfTo;
|
||||
ds << a->indexOfFollowee;
|
||||
ds << a->side1Items;
|
||||
ds << a->side2Items;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a)
|
||||
{
|
||||
QString marker;
|
||||
@@ -245,21 +410,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a)
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Frame *frame)
|
||||
{
|
||||
ds << frame->objectName;
|
||||
ds << frame->geometry;
|
||||
ds << frame->options;
|
||||
ds << frame->currentTabIndex;
|
||||
ds << frame->dockWidgets.size();
|
||||
|
||||
for (auto &dock : frame->dockWidgets) {
|
||||
ds << dock->uniqueName;
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
|
||||
{
|
||||
int numDockWidgets;
|
||||
@@ -268,6 +418,12 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
|
||||
|
||||
ds >> frame->objectName;
|
||||
ds >> frame->geometry;
|
||||
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
QSize sz;
|
||||
ds >> sz; // deprecated field, just discard
|
||||
}
|
||||
|
||||
ds >> frame->options;
|
||||
ds >> frame->currentTabIndex;
|
||||
ds >> numDockWidgets;
|
||||
@@ -282,26 +438,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Item *item)
|
||||
{
|
||||
ds << item->objectName;
|
||||
ds << item->isPlaceholder;
|
||||
ds << item->geometry;
|
||||
ds << item->minSize;
|
||||
|
||||
ds << item->indexOfLeftAnchor;
|
||||
ds << item->indexOfTopAnchor;
|
||||
ds << item->indexOfRightAnchor;
|
||||
ds << item->indexOfBottomAnchor;
|
||||
|
||||
const bool hasFrame = !item->frame.isNull;
|
||||
ds << hasFrame;
|
||||
if (hasFrame)
|
||||
ds << &item->frame;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
|
||||
{
|
||||
ds >> item->objectName;
|
||||
@@ -326,24 +462,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
|
||||
{
|
||||
ds << QStringLiteral(MULTISPLITTER_LAYOUT_MAGIC_MARKER);
|
||||
|
||||
ds << l->size;
|
||||
ds << l->minSize;
|
||||
ds << l->items.size();
|
||||
ds << l->anchors.size();
|
||||
|
||||
for (auto &item : l->items)
|
||||
ds << &item;
|
||||
|
||||
for (auto &anchor : l->anchors)
|
||||
ds << &anchor;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
|
||||
{
|
||||
int numItems;
|
||||
@@ -377,21 +495,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::LastPosition *lp)
|
||||
{
|
||||
ds << lp->placeholders.size();
|
||||
|
||||
for (auto &p : lp->placeholders) {
|
||||
ds << &p;
|
||||
}
|
||||
|
||||
ds << lp->lastFloatingGeometry;
|
||||
ds << lp->tabIndex;
|
||||
ds << lp->wasFloating;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
|
||||
{
|
||||
int numPlaceholders;
|
||||
@@ -411,71 +514,34 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
|
||||
{
|
||||
ds << fw->parentIndex;
|
||||
ds << fw->geometry;
|
||||
ds << fw->isVisible;
|
||||
ds << &fw->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
|
||||
{
|
||||
ds >> fw->parentIndex;
|
||||
ds >> fw->geometry;
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> fw->screenIndex;
|
||||
ds >> fw->screenSize;
|
||||
}
|
||||
|
||||
ds >> fw->isVisible;
|
||||
ds >> &fw->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::MainWindow *m)
|
||||
{
|
||||
ds << m->uniqueName;
|
||||
ds << m->geometry;
|
||||
ds << m->isVisible;
|
||||
ds << m->options;
|
||||
ds << &m->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MainWindow *m)
|
||||
{
|
||||
ds >> m->uniqueName;
|
||||
ds >> m->geometry;
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> m->screenIndex;
|
||||
ds >> m->screenSize;
|
||||
}
|
||||
ds >> m->isVisible;
|
||||
ds >> m->options;
|
||||
ds >> &m->multiSplitterLayout;
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Layout *l)
|
||||
{
|
||||
ds << l->serializationVersion;
|
||||
ds << l->mainWindows.size();
|
||||
for (auto &m: l->mainWindows) {
|
||||
ds << &m;
|
||||
}
|
||||
|
||||
ds << l->floatingWindows.size();
|
||||
for (auto &fw: l->floatingWindows) {
|
||||
ds << &fw;
|
||||
}
|
||||
|
||||
ds << l->closedDockWidgets.size();
|
||||
for (auto &dw: l->closedDockWidgets) {
|
||||
ds << dw->uniqueName;
|
||||
}
|
||||
|
||||
ds << l->allDockWidgets.size();
|
||||
for (auto &dw: l->allDockWidgets) {
|
||||
ds << dw->uniqueName;
|
||||
ds << &dw->lastPosition;
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
|
||||
{
|
||||
LayoutSaver::DockWidget::s_dockWidgets.clear();
|
||||
@@ -483,6 +549,7 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
|
||||
int numFloatingWindows;
|
||||
int numClosedDockWidgets;
|
||||
int numAllDockWidgets;
|
||||
int numScreenInfo;
|
||||
|
||||
ds >> l->serializationVersion;
|
||||
|
||||
@@ -522,6 +589,18 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
|
||||
l->allDockWidgets.push_back(dw);
|
||||
}
|
||||
|
||||
|
||||
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
|
||||
ds >> numScreenInfo;
|
||||
l->screenInfo.clear();
|
||||
l->screenInfo.reserve(numScreenInfo);
|
||||
for (int i = 0; i < numScreenInfo; ++i) {
|
||||
LayoutSaver::ScreenInfo info;
|
||||
ds >> &info;
|
||||
l->screenInfo.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "DockRegistry_p.h"
|
||||
#include "DropArea_p.h"
|
||||
#include "Frame_p.h"
|
||||
#include "Utils_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "DropAreaWithCentralFrame_p.h"
|
||||
#include "multisplitter/MultiSplitterLayout_p.h"
|
||||
@@ -51,6 +52,7 @@ public:
|
||||
}
|
||||
|
||||
QString name;
|
||||
QString affinityName;
|
||||
const MainWindowOptions m_options;
|
||||
};
|
||||
|
||||
@@ -73,6 +75,17 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
|
||||
Q_ASSERT(widget);
|
||||
qCDebug(addwidget) << Q_FUNC_INFO << widget;
|
||||
|
||||
if (widget->affinityName() != affinityName()) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
|
||||
<< widget->affinityName() << affinityName();
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget->options() & DockWidgetBase::Option_NotDockable) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << widget;
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->supportsCentralFrame()) {
|
||||
dropArea()->m_centralFrame->addWidget(widget);
|
||||
} else {
|
||||
@@ -82,7 +95,12 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
|
||||
|
||||
void MainWindowBase::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBase *relativeTo, AddingOption option)
|
||||
{
|
||||
dropArea()->addDockWidget(dw, location, relativeTo, option);
|
||||
if (dw->options() & DockWidgetBase::Option_NotDockable) {
|
||||
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << dw;
|
||||
return;
|
||||
}
|
||||
|
||||
dropArea()->addDockWidget(dw, location, relativeTo, option);
|
||||
}
|
||||
|
||||
QString MainWindowBase::uniqueName() const
|
||||
@@ -100,6 +118,26 @@ MultiSplitterLayout *MainWindowBase::multiSplitterLayout() const
|
||||
return dropArea()->multiSplitterLayout();
|
||||
}
|
||||
|
||||
void MainWindowBase::setAffinityName(const QString &name)
|
||||
{
|
||||
if (d->affinityName == name)
|
||||
return;
|
||||
|
||||
if (!d->affinityName.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO
|
||||
<< "Affinity is already set, refusing to change."
|
||||
<< "Submit a feature request with a good justification.";
|
||||
return;
|
||||
}
|
||||
|
||||
d->affinityName = name;
|
||||
}
|
||||
|
||||
QString MainWindowBase::affinityName() const
|
||||
{
|
||||
return d->affinityName;
|
||||
}
|
||||
|
||||
void MainWindowBase::setUniqueName(const QString &uniqueName)
|
||||
{
|
||||
if (uniqueName.isEmpty())
|
||||
@@ -122,6 +160,13 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->affinityName != mw.affinityName) {
|
||||
qWarning() << Q_FUNC_INFO << "Affinty name changed from" << d->affinityName
|
||||
<< "; to" << mw.affinityName;
|
||||
|
||||
d->affinityName = mw.affinityName;
|
||||
}
|
||||
|
||||
return dropArea()->multiSplitterLayout()->deserialize(mw.multiSplitterLayout);
|
||||
}
|
||||
|
||||
@@ -133,7 +178,10 @@ LayoutSaver::MainWindow MainWindowBase::serialize() const
|
||||
m.geometry = window()->geometry(); // window() as the MainWindow can be embedded
|
||||
m.isVisible = isVisible();
|
||||
m.uniqueName = uniqueName();
|
||||
m.screenIndex = screenNumberForWidget(this);
|
||||
m.screenSize = screenSizeForWidget(this);
|
||||
m.multiSplitterLayout = dropArea()->multiSplitterLayout()->serialize();
|
||||
m.affinityName = d->affinityName;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -100,6 +100,29 @@ public:
|
||||
///@brief returns the MultiSplitterLayout.
|
||||
MultiSplitterLayout* multiSplitterLayout() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the affinity name. Dock widgets can only dock into main windows of the same affinity.
|
||||
*
|
||||
* By default the affinity is empty and a dock widget can dock into any main window. Usually you
|
||||
* won't ever need to call this function, unless you have requirements where certain dock widgets
|
||||
* can only dock into certain main windows. @sa DockWidgetBase::setAffinityName().
|
||||
*
|
||||
* Note: Call this function right after creating your main window, before docking any dock widgets
|
||||
* into a main window and before restoring any layout.
|
||||
*
|
||||
* Note: Currently you can only call this function once, to keep the code simple and avoid
|
||||
* edge cases. This will only be changed if a good use case comes up that requires changing
|
||||
* affinities multiple times.
|
||||
*
|
||||
* @p name The affinity name.
|
||||
*/
|
||||
void setAffinityName(const QString &name);
|
||||
|
||||
/**
|
||||
* @brief Returns the affinity name. Empty by default.
|
||||
*/
|
||||
QString affinityName() const;
|
||||
|
||||
protected:
|
||||
void setUniqueName(const QString &uniqueName);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
1
src/fwd_headers/kddockwidgets/Config.h
Normal file
1
src/fwd_headers/kddockwidgets/Config.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../Config.h"
|
||||
1
src/fwd_headers/kddockwidgets/DockWidget.h
Normal file
1
src/fwd_headers/kddockwidgets/DockWidget.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../DockWidget.h"
|
||||
1
src/fwd_headers/kddockwidgets/FrameworkWidgetFactory.h
Normal file
1
src/fwd_headers/kddockwidgets/FrameworkWidgetFactory.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../FrameworkWidgetFactory.h"
|
||||
1
src/fwd_headers/kddockwidgets/LayoutSaver.h
Normal file
1
src/fwd_headers/kddockwidgets/LayoutSaver.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../LayoutSaver.h"
|
||||
1
src/fwd_headers/kddockwidgets/MainWindow.h
Normal file
1
src/fwd_headers/kddockwidgets/MainWindow.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../MainWindow.h"
|
||||
1
src/fwd_headers/kddockwidgets/private/TitleBar_p.h
Normal file
1
src/fwd_headers/kddockwidgets/private/TitleBar_p.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../../private/TitleBar_p.h"
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,12 +36,15 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QSpinBox>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QWindow>
|
||||
#include <QFileDialog>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# include <Windows.h>
|
||||
@@ -113,6 +116,25 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
}
|
||||
});
|
||||
|
||||
hlay = new QHBoxLayout();
|
||||
layout->addLayout(hlay);
|
||||
button = new QPushButton(this);
|
||||
auto lineedit = new QLineEdit(this);
|
||||
lineedit->setPlaceholderText(tr("DockWidget unique name"));
|
||||
button->setText(QStringLiteral("Show"));
|
||||
hlay->addWidget(button);
|
||||
hlay->addWidget(lineedit);
|
||||
|
||||
connect(button, &QPushButton::clicked, this, [lineedit] {
|
||||
auto dw = DockRegistry::self()->dockByName(lineedit->text());
|
||||
if (dw) {
|
||||
dw->show();
|
||||
} else {
|
||||
QMessageBox::warning(nullptr, QStringLiteral("Could not find"),
|
||||
QStringLiteral("Could not find DockWidget with name %1").arg(lineedit->text()));
|
||||
}
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Float all visible docks"));
|
||||
layout->addWidget(button);
|
||||
@@ -129,8 +151,8 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
LayoutSaver saver;
|
||||
QString message = saver.saveToDisk() ? QStringLiteral("Saved!")
|
||||
: QStringLiteral("Error!");
|
||||
QString message = saver.saveToFile(QStringLiteral("layout.json")) ? QStringLiteral("Saved!")
|
||||
: QStringLiteral("Error!");
|
||||
qDebug() << message;
|
||||
});
|
||||
|
||||
@@ -139,8 +161,8 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
LayoutSaver saver;
|
||||
QString message = saver.restoreFromDisk() ? QStringLiteral("Restored!")
|
||||
: QStringLiteral("Error!");
|
||||
QString message = saver.restoreFromFile(QStringLiteral("layout.json")) ? QStringLiteral("Restored!")
|
||||
: QStringLiteral("Error!");
|
||||
qDebug() << message;
|
||||
});
|
||||
|
||||
@@ -186,7 +208,6 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Detach central widget"));
|
||||
layout->addWidget(button);
|
||||
@@ -214,30 +235,85 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
button->setText(QStringLiteral("EnsureAnchorsBounded"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto mainWindows = DockRegistry::self()->mainwindows();
|
||||
if (mainWindows.isEmpty())
|
||||
return;
|
||||
mainWindows.at(0)->multiSplitterLayout()->ensureAnchorsBounded();
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->ensureAnchorsBounded();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("RedistributeSpace"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->redistributeSpace();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("resize by 1x1"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts) {
|
||||
QWidget *tlw = l->multiSplitter()->window();
|
||||
tlw->resize(tlw->size() + QSize(1, 1));
|
||||
}
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("PositionStaticAnchors()"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto mainWindows = DockRegistry::self()->mainwindows();
|
||||
if (mainWindows.isEmpty())
|
||||
return;
|
||||
mainWindows.at(0)->multiSplitterLayout()->positionStaticAnchors();
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->positionStaticAnchors();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("UpdateAnchorFollowing"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [] {
|
||||
const auto mainWindows = DockRegistry::self()->mainwindows();
|
||||
if (mainWindows.isEmpty())
|
||||
const auto layouts = DockRegistry::self()->layouts();
|
||||
for (auto l : layouts)
|
||||
l->updateAnchorFollowing();
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Raise #0 (after 3s timeout)"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [this] {
|
||||
QTimer::singleShot(3000, this, [] {
|
||||
const auto docks = DockRegistry::self()->dockwidgets();
|
||||
if (!docks.isEmpty())
|
||||
docks.constFirst()->raise();
|
||||
});
|
||||
});
|
||||
|
||||
button = new QPushButton(this);
|
||||
button->setText(QStringLiteral("Convert old layout to JSON"));
|
||||
layout->addWidget(button);
|
||||
connect(button, &QPushButton::clicked, this, [this] {
|
||||
const QString filename = QFileDialog::getOpenFileName(this);
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
mainWindows.at(0)->multiSplitterLayout()->updateAnchorFollowing();
|
||||
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Failed to open file" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray oldData = f.readAll();
|
||||
LayoutSaver::Layout savedLayout;
|
||||
savedLayout.fillFrom(oldData);
|
||||
const QByteArray jsonData = savedLayout.toJson();
|
||||
QFile f2(QStringLiteral("%1.json").arg(filename));
|
||||
if (!f2.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Failed to open file for writing" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
f2.write(jsonData);
|
||||
});
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -319,6 +395,6 @@ void DebugWindow::mousePressEvent(QMouseEvent *event)
|
||||
<< (w ? w->parentWidget() : nullptr) << "; geometry="
|
||||
<< (w ? w->geometry() : QRect());
|
||||
|
||||
|
||||
m_isPickingWidget->quit();
|
||||
if (m_isPickingWidget)
|
||||
m_isPickingWidget->quit();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,9 @@
|
||||
#include "ObjectViewer_p.h"
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QEventLoop;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
namespace Debug {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -72,6 +72,11 @@ void DockRegistry::checkSanityAll()
|
||||
layout->checkSanity();
|
||||
}
|
||||
|
||||
bool DockRegistry::isProcessingAppQuitEvent() const
|
||||
{
|
||||
return m_isProcessingAppQuitEvent;
|
||||
}
|
||||
|
||||
DockRegistry *DockRegistry::self()
|
||||
{
|
||||
static QPointer<DockRegistry> s_dockRegistry;
|
||||
@@ -168,6 +173,19 @@ MainWindowBase *DockRegistry::mainWindowByName(const QString &name) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DockWidgetBase *DockRegistry::dockWidgetForGuest(QWidget *guest) const
|
||||
{
|
||||
if (!guest)
|
||||
return nullptr;
|
||||
|
||||
for (DockWidgetBase *dw : m_dockWidgets) {
|
||||
if (dw->widget() == guest)
|
||||
return dw;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DockRegistry::isSane() const
|
||||
{
|
||||
QSet<QString> names;
|
||||
@@ -257,6 +275,26 @@ FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) con
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QVector<QWidget *> DockRegistry::topLevels(bool excludeFloatingDocks) const
|
||||
{
|
||||
QVector<QWidget *> windows;
|
||||
windows.reserve(m_nestedWindows.size() + m_mainWindows.size());
|
||||
|
||||
if (!excludeFloatingDocks) {
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
if (fw->isVisible())
|
||||
windows << fw;
|
||||
}
|
||||
}
|
||||
|
||||
for (MainWindowBase *m : m_mainWindows) {
|
||||
if (m->isVisible())
|
||||
windows << m->topLevelWidget();
|
||||
}
|
||||
|
||||
return windows;
|
||||
}
|
||||
|
||||
void DockRegistry::clear(bool deleteStaticAnchors)
|
||||
{
|
||||
for (auto dw : qAsConst(m_dockWidgets)) {
|
||||
@@ -271,6 +309,31 @@ void DockRegistry::clear(bool deleteStaticAnchors)
|
||||
<< "; nestedwindows=" << m_nestedWindows.size();
|
||||
}
|
||||
|
||||
void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors)
|
||||
{
|
||||
if (affinities.isEmpty()) {
|
||||
// Just clear everything
|
||||
clear(deleteStaticAnchors);
|
||||
return;
|
||||
}
|
||||
|
||||
// empty affinity also matches and will be closed
|
||||
affinities << QString();
|
||||
|
||||
for (auto dw : qAsConst(m_dockWidgets)) {
|
||||
if (affinities.contains(dw->affinityName())) {
|
||||
dw->forceClose();
|
||||
dw->lastPosition()->removePlaceholders();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto mw : qAsConst(m_mainWindows)) {
|
||||
if (affinities.contains(mw->affinityName())) {
|
||||
mw->multiSplitterLayout()->clear(deleteStaticAnchors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockRegistry::ensureAllFloatingWidgetsAreMorphed()
|
||||
{
|
||||
for (DockWidgetBase *dw : qAsConst(m_dockWidgets)) {
|
||||
@@ -281,17 +344,18 @@ void DockRegistry::ensureAllFloatingWidgetsAreMorphed()
|
||||
|
||||
bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
// This event filter is needed so we sort the floating windows by z-order
|
||||
// When a FloatingWindow is exposed we put it at the end of the list
|
||||
|
||||
if (event->type() != QEvent::Expose)
|
||||
return false;
|
||||
|
||||
if (auto windowHandle = qobject_cast<QWindow*>(watched)) {
|
||||
if (FloatingWindow *fw = floatingWindowForHandle(windowHandle)) {
|
||||
// This floating window was exposed
|
||||
m_nestedWindows.removeOne(fw);
|
||||
m_nestedWindows.append(fw);
|
||||
if (event->type() == QEvent::Quit && !m_isProcessingAppQuitEvent) {
|
||||
m_isProcessingAppQuitEvent = true;
|
||||
qApp->sendEvent(qApp, event);
|
||||
m_isProcessingAppQuitEvent = false;
|
||||
return true;
|
||||
} else if (event->type() == QEvent::Expose) {
|
||||
if (auto windowHandle = qobject_cast<QWindow*>(watched)) {
|
||||
if (FloatingWindow *fw = floatingWindowForHandle(windowHandle)) {
|
||||
// This floating window was exposed
|
||||
m_nestedWindows.removeOne(fw);
|
||||
m_nestedWindows.append(fw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -59,6 +59,10 @@ public:
|
||||
|
||||
DockWidgetBase *dockByName(const QString &) const;
|
||||
MainWindowBase *mainWindowByName(const QString &) const;
|
||||
|
||||
/// @brief returns the dock widget that hosts @p guest widget. Nullptr if there's none.
|
||||
DockWidgetBase *dockWidgetForGuest(QWidget *guest) const;
|
||||
|
||||
bool isSane() const;
|
||||
|
||||
///@brief returns all DockWidget instances
|
||||
@@ -83,12 +87,28 @@ public:
|
||||
///@brief returns the FloatingWindow with handle @p windowHandle
|
||||
FloatingWindow *floatingWindowForHandle(QWindow *windowHandle) const;
|
||||
|
||||
///@brief Returns the list with all visiblye top-level parents of our FloatingWindow and MainWindow instances.
|
||||
///
|
||||
/// Typically these are the FloatingWindows and MainWindows themselves. However, since a
|
||||
/// MainWindow can be embedded into another widget (for whatever reason, like a QWinWidget),
|
||||
/// it means that a top-level can be something else.
|
||||
///
|
||||
/// Every returned widget is either a FloatingWindow, MainWindow, or something that contains a MainWindow.
|
||||
///
|
||||
/// If @p excludeFloatingDocks is true then FloatingWindow won't be returned
|
||||
QVector<QWidget*> topLevels(bool excludeFloatingDocks = false) const;
|
||||
|
||||
/**
|
||||
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors.
|
||||
* This is called before restoring a layout.
|
||||
*/
|
||||
void clear(bool deleteStaticAnchors = false);
|
||||
|
||||
/**
|
||||
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors with the specified affinities.
|
||||
*/
|
||||
void clear(QStringList affinities, bool deleteStaticAnchors = false);
|
||||
|
||||
/**
|
||||
* @brief Ensures that all floating DockWidgets have a FloatingWindow as a window.
|
||||
*
|
||||
@@ -110,11 +130,19 @@ public:
|
||||
*/
|
||||
void checkSanityAll();
|
||||
|
||||
/**
|
||||
* @brief Returns whether we're processing a QEvent::Quit
|
||||
*
|
||||
* Used internally to know if we should let Qt close a NonClosable dock widget at shutdown time.
|
||||
*/
|
||||
bool isProcessingAppQuitEvent() const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
private:
|
||||
explicit DockRegistry(QObject *parent = nullptr);
|
||||
void maybeDelete();
|
||||
bool m_isProcessingAppQuitEvent = false;
|
||||
DockWidgetBase::List m_dockWidgets;
|
||||
MainWindowBase::List m_mainWindows;
|
||||
Frame::List m_frames;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -153,19 +153,19 @@ void StatePreDrag::onEntry(QEvent *)
|
||||
|
||||
bool StatePreDrag::handleMouseMove(QPoint globalPos)
|
||||
{
|
||||
if ((globalPos - q->m_pressPos).manhattanLength() > QApplication::startDragDistance()) {
|
||||
if (q->m_draggable->dragCanStart(q->m_pressPos, globalPos)) {
|
||||
Q_EMIT q->manhattanLengthMove();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StatePreDrag::handleMouseButtonRelease(QPoint)
|
||||
{
|
||||
Q_EMIT q->dragCanceled();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
StateDragging::StateDragging(DragController *parent)
|
||||
: StateBase(parent)
|
||||
{
|
||||
@@ -197,6 +197,12 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (floatingWindow->anyNonDockable()) {
|
||||
qCDebug(state) << "StateDragging: Ignoring floating window with non dockable widgets";
|
||||
Q_EMIT q->dragCanceled();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (q->m_currentDropArea) {
|
||||
if (q->m_currentDropArea->drop(floatingWindow, globalPos)) {
|
||||
Q_EMIT q->dropped();
|
||||
@@ -213,21 +219,36 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
|
||||
|
||||
bool StateDragging::handleMouseMove(QPoint globalPos)
|
||||
{
|
||||
if (!q->m_windowBeingDragged->floatingWindow()) {
|
||||
FloatingWindow *fw = q->m_windowBeingDragged->floatingWindow();
|
||||
if (!fw) {
|
||||
qCDebug(state) << "Canceling drag, window was deleted";
|
||||
Q_EMIT q->dragCanceled();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!q->m_nonClientDrag)
|
||||
q->m_windowBeingDragged->floatingWindow()->windowHandle()->setPosition(globalPos - q->m_offset);
|
||||
fw->windowHandle()->setPosition(globalPos - q->m_offset);
|
||||
|
||||
|
||||
if (fw->anyNonDockable()) {
|
||||
qCDebug(state) << "StateDragging: Ignoring non dockable floating window";
|
||||
return true;
|
||||
}
|
||||
|
||||
DropArea *dropArea = q->dropAreaUnderCursor();
|
||||
if (q->m_currentDropArea && dropArea != q->m_currentDropArea)
|
||||
q->m_currentDropArea->removeHover();
|
||||
|
||||
if (dropArea)
|
||||
dropArea->hover(q->m_windowBeingDragged->floatingWindow(), globalPos);
|
||||
if (dropArea) {
|
||||
if (FloatingWindow *targetFw = dropArea->floatingWindow()) {
|
||||
if (targetFw->anyNonDockable()) {
|
||||
qCDebug(state) << "StateDragging: Ignoring non dockable target floating window";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dropArea->hover(fw, globalPos);
|
||||
}
|
||||
|
||||
q->m_currentDropArea = dropArea;
|
||||
|
||||
@@ -469,7 +490,7 @@ QWidgetOrQuick *DragController::qtTopLevelUnderCursor() const
|
||||
if (auto tl = qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->nestedwindows(), m_windowBeingDragged->floatingWindow()))
|
||||
return tl;
|
||||
|
||||
return qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->mainwindows(), static_cast<MainWindowBase*>(nullptr));
|
||||
return qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->topLevels(/*excludeFloating=*/true), static_cast<QWidget*>(nullptr));
|
||||
}
|
||||
#else
|
||||
// QtQuick:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "FloatingWindow_p.h"
|
||||
#include "WidgetResizeHandler_p.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
class Draggable::Private
|
||||
@@ -59,6 +61,11 @@ QWidgetOrQuick *Draggable::asWidget() const
|
||||
return d->thisWidget;
|
||||
}
|
||||
|
||||
bool Draggable::dragCanStart(QPoint pressPos, QPoint globalPos) const
|
||||
{
|
||||
return (globalPos - pressPos).manhattanLength() > QApplication::startDragDistance();
|
||||
}
|
||||
|
||||
WidgetResizeHandler *Draggable::widgetResizeHandler() const
|
||||
{
|
||||
return d->widgetResizeHandler;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -59,9 +59,17 @@ public:
|
||||
* @brief Returns whether point @p p is draggable.
|
||||
*
|
||||
* Because simply inheriting from Draggable doesn't mean you can click anywhere to drag.
|
||||
* @param p is the point where the mouse press occurred
|
||||
*/
|
||||
virtual bool isPositionDraggable(QPoint p) const { Q_UNUSED(p) return true; }
|
||||
|
||||
/**
|
||||
* @brief Returns whether a mouse move can start a drag or not.
|
||||
* The default implementation just checks if the delta is bigger than
|
||||
* QApplication::startDragDistance().
|
||||
*/
|
||||
virtual bool dragCanStart(QPoint pressPos, QPoint globalPos) const;
|
||||
|
||||
WidgetResizeHandler *widgetResizeHandler() const;
|
||||
void setWidgetResizeHandler(WidgetResizeHandler *w);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "Config.h"
|
||||
#include "DropIndicatorOverlayInterface_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "MainWindowBase.h"
|
||||
|
||||
// #include "indicators/AnimatedIndicators_p.h"
|
||||
#include "WindowBeingDragged_p.h"
|
||||
@@ -109,6 +110,9 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateAffinity(dw))
|
||||
return;
|
||||
|
||||
Frame *frame = nullptr;
|
||||
Frame *relativeToFrame = relativeTo ? relativeTo->frame() : nullptr;
|
||||
|
||||
@@ -158,8 +162,22 @@ bool DropArea::contains(DockWidgetBase *dw) const
|
||||
return dw->frame() && m_layout->contains(dw->frame());
|
||||
}
|
||||
|
||||
QString DropArea::affinityName() const
|
||||
{
|
||||
if (auto mw = mainWindow()) {
|
||||
return mw->affinityName();
|
||||
} else if (auto fw = floatingWindow()) {
|
||||
return fw->affinityName();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void DropArea::hover(FloatingWindow *floatingWindow, QPoint globalPos)
|
||||
{
|
||||
if (!validateAffinity(floatingWindow))
|
||||
return;
|
||||
|
||||
Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow);
|
||||
m_dropIndicatorOverlay->setHoveredFrame(frame);
|
||||
@@ -220,6 +238,8 @@ bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
|
||||
break;
|
||||
case DropIndicatorOverlayInterface::DropLocation_Center:
|
||||
qCDebug(hovering) << "Tabbing" << droppedWindow << "into" << acceptingFrame;
|
||||
if (!validateAffinity(droppedWindow))
|
||||
return false;
|
||||
acceptingFrame->addWidget(droppedWindow);
|
||||
break;
|
||||
|
||||
@@ -241,10 +261,16 @@ bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location locat
|
||||
qCDebug(docking) << "DropArea::addFrame";
|
||||
|
||||
if (auto dock = qobject_cast<DockWidgetBase *>(droppedWindow)) {
|
||||
if (!validateAffinity(dock))
|
||||
return false;
|
||||
|
||||
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
|
||||
frame->addWidget(dock);
|
||||
m_layout->addWidget(frame, location, relativeTo);
|
||||
} else if (auto floatingWindow = qobject_cast<FloatingWindow *>(droppedWindow)) {
|
||||
if (!validateAffinity(floatingWindow))
|
||||
return false;
|
||||
|
||||
m_layout->addMultiSplitter(floatingWindow->dropArea(), location, relativeTo);
|
||||
floatingWindow->scheduleDeleteLater();
|
||||
return true;
|
||||
@@ -261,3 +287,16 @@ void DropArea::removeHover()
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(nullptr);
|
||||
m_dropIndicatorOverlay->setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool DropArea::validateAffinity(T *window) const
|
||||
{
|
||||
if (window->affinityName() != affinityName()) {
|
||||
// Commented the warning, so we don't warn when hovering over
|
||||
//qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
|
||||
//<< window->affinityName() << affinityName();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -68,13 +68,18 @@ public:
|
||||
|
||||
bool checkSanity(MultiSplitterLayout::AnchorSanityOption o = MultiSplitterLayout::AnchorSanity_All);
|
||||
bool contains(DockWidgetBase *) const;
|
||||
|
||||
QString affinityName() const;
|
||||
private:
|
||||
Q_DISABLE_COPY(DropArea)
|
||||
friend class Frame;
|
||||
friend class TestDocks;
|
||||
friend class DropIndicatorOverlayInterface;
|
||||
friend class AnimatedIndicators;
|
||||
template <typename T>
|
||||
bool validateAffinity(T *) const;
|
||||
bool m_inDestructor = false;
|
||||
QString m_affinityName;
|
||||
DropIndicatorOverlayInterface *m_dropIndicatorOverlay = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
|
||||
FloatingWindow::FloatingWindow(MainWindowBase *parent)
|
||||
: QWidgetAdapter(parent, KDDockWidgets::usesNativeDraggingAndResizing() ? Qt::Window : Qt::Tool)
|
||||
, Draggable(this, KDDockWidgets::usesNativeDraggingAndResizing()) // FloatingWindow is only draggable when using a native title bar. Otherwise the KDDockWidgets::TitleBar is the draggable
|
||||
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
|
||||
@@ -105,6 +105,13 @@ FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
|
||||
DockRegistry::self()->registerNestedWindow(this);
|
||||
qCDebug(creation) << "FloatingWindow()" << this;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
# if QT_VERSION < 0x051000
|
||||
// On Windows with Qt 5.9 (and maybe later but we don't care), the WM_NCALCSIZE isn't being processed unless we explicitly create the window.
|
||||
// So create it now, otherwise floating dock widgets will show a native title bar until resized.
|
||||
create();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
maybeCreateResizeHandler();
|
||||
|
||||
@@ -115,25 +122,45 @@ FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
|
||||
m_layoutDestroyedConnection = connect(ms, &MultiSplitterLayout::destroyed, this, &FloatingWindow::scheduleDeleteLater);
|
||||
}
|
||||
|
||||
static QWidgetOrQuick* hackFindParentHarder(QWidgetOrQuick *p)
|
||||
static MainWindowBase* hackFindParentHarder(Frame *frame, MainWindowBase *candidateParent)
|
||||
{
|
||||
// TODO: Using a parent helps the floating windows stay in front of the main window always.
|
||||
// We're not receiving the parent via ctor argument as the app can have multiple-main windows,
|
||||
// so use a hack here.
|
||||
// Not quite clear what to do if the app supports multiple main windows though.
|
||||
|
||||
if (p)
|
||||
return p;
|
||||
if (candidateParent)
|
||||
return candidateParent;
|
||||
|
||||
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
||||
const MainWindowBase::List windows = DockRegistry::self()->mainwindows();
|
||||
|
||||
if (windows.isEmpty())
|
||||
return nullptr;
|
||||
if (windows.size() == 1)
|
||||
|
||||
if (windows.size() == 1) {
|
||||
return windows.first();
|
||||
else {
|
||||
qWarning() << Q_FUNC_INFO << "There's multiple MainWindows, not sure what to do about parenting";
|
||||
} else {
|
||||
const QString affinityName = frame ? frame->affinityName() : QString();
|
||||
|
||||
if (affinityName.isEmpty()) {
|
||||
|
||||
for (MainWindowBase *window : windows) {
|
||||
if (window->affinityName().isEmpty())
|
||||
return window;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "No window with empty affinity found";
|
||||
|
||||
} else {
|
||||
for (MainWindowBase *window : windows) {
|
||||
if (window->affinityName() == affinityName)
|
||||
return window;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "No window with affinity" << affinityName << "found";
|
||||
}
|
||||
|
||||
return windows.first();
|
||||
}
|
||||
#else
|
||||
@@ -142,8 +169,8 @@ static QWidgetOrQuick* hackFindParentHarder(QWidgetOrQuick *p)
|
||||
#endif
|
||||
}
|
||||
|
||||
FloatingWindow::FloatingWindow(Frame *frame, QWidgetOrQuick *parent)
|
||||
: FloatingWindow(hackFindParentHarder(parent))
|
||||
FloatingWindow::FloatingWindow(Frame *frame, MainWindowBase *parent)
|
||||
: FloatingWindow(hackFindParentHarder(frame, parent))
|
||||
{
|
||||
m_disableSetVisible = true;
|
||||
// Adding a widget will trigger onFrameCountChanged, which triggers a setVisible(true).
|
||||
@@ -233,6 +260,15 @@ bool FloatingWindow::anyNonClosable() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FloatingWindow::anyNonDockable() const
|
||||
{
|
||||
for (Frame *frame : frames()) {
|
||||
if (frame->anyNonDockable())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FloatingWindow::hasSingleFrame() const
|
||||
{
|
||||
return frames().size() == 1;
|
||||
@@ -303,6 +339,12 @@ void FloatingWindow::updateTitleBarVisibility()
|
||||
m_titleBar->setVisible(visible);
|
||||
}
|
||||
|
||||
QString FloatingWindow::affinityName() const
|
||||
{
|
||||
auto frames = this->frames();
|
||||
return frames.isEmpty() ? QString() : frames.constFirst()->affinityName();
|
||||
}
|
||||
|
||||
void FloatingWindow::updateTitleAndIcon()
|
||||
{
|
||||
QString title;
|
||||
@@ -326,6 +368,13 @@ void FloatingWindow::updateTitleAndIcon()
|
||||
void FloatingWindow::onCloseEvent(QCloseEvent *e)
|
||||
{
|
||||
qCDebug(closing) << "Frame::closeEvent";
|
||||
|
||||
if (e->spontaneous() && anyNonClosable()) {
|
||||
// Event from the window system won't close us
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
e->accept(); // Accepted by default (will close unless ignored)
|
||||
|
||||
Frame::List frames = this->frames();
|
||||
@@ -353,6 +402,9 @@ LayoutSaver::FloatingWindow FloatingWindow::serialize() const
|
||||
fw.geometry = geometry();
|
||||
fw.isVisible = isVisible();
|
||||
fw.multiSplitterLayout = dropArea()->multiSplitterLayout()->serialize();
|
||||
fw.screenIndex = screenNumberForWidget(this);
|
||||
fw.screenSize = screenSizeForWidget(this);
|
||||
fw.affinityName = affinityName();
|
||||
|
||||
auto mainWindow = qobject_cast<MainWindowBase*>(parentWidget());
|
||||
fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,10 +27,14 @@
|
||||
#include "QWidgetAdapter.h"
|
||||
#include "LayoutSaver_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractNativeEventFilter;
|
||||
class QWindowStateChangeEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class MainWindowBase;
|
||||
class DropArea;
|
||||
class Frame;
|
||||
class MultiSplitterLayout;
|
||||
@@ -40,8 +44,8 @@ class DOCKS_EXPORT FloatingWindow : public QWidgetAdapter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FloatingWindow(QWidgetOrQuick *parent = nullptr);
|
||||
explicit FloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr);
|
||||
explicit FloatingWindow(MainWindowBase *parent = nullptr);
|
||||
explicit FloatingWindow(Frame *frame, MainWindowBase *parent = nullptr);
|
||||
~FloatingWindow() override;
|
||||
|
||||
bool deserialize(const LayoutSaver::FloatingWindow &);
|
||||
@@ -60,6 +64,7 @@ public:
|
||||
TitleBar *titleBar() const { return m_titleBar; }
|
||||
|
||||
bool anyNonClosable() const;
|
||||
bool anyNonDockable() const;
|
||||
|
||||
/**
|
||||
* @brief checks if this FloatingWindow only has one frame.
|
||||
@@ -106,8 +111,11 @@ public:
|
||||
void updateTitleAndIcon();
|
||||
void updateTitleBarVisibility();
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void numFramesChanged();
|
||||
void windowStateChanged(QWindowStateChangeEvent *);
|
||||
protected:
|
||||
#ifdef Q_OS_WIN
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include <QTabBar>
|
||||
#include <QCloseEvent>
|
||||
#include <QTimer>
|
||||
|
||||
#define MARGIN_THRESHOLD 100
|
||||
|
||||
@@ -96,18 +97,19 @@ void Frame::updateTitleAndIcon()
|
||||
}
|
||||
}
|
||||
|
||||
setObjectName(dw->uniqueName());
|
||||
|
||||
} else if (currentTabIndex() != -1) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid dock widget for frame." << currentTabIndex();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Frame::addWidget(DockWidgetBase *dockWidget)
|
||||
void Frame::addWidget(DockWidgetBase *dockWidget, AddingOption addingOption)
|
||||
{
|
||||
insertWidget(dockWidget, m_tabWidget->numDockWidgets()); // append
|
||||
insertWidget(dockWidget, m_tabWidget->numDockWidgets(), addingOption); // append
|
||||
}
|
||||
|
||||
void Frame::addWidget(Frame *frame)
|
||||
void Frame::addWidget(Frame *frame, AddingOption addingOption)
|
||||
{
|
||||
if (frame->isEmpty()) {
|
||||
qWarning() << "Frame::addWidget: frame is empty." << frame;
|
||||
@@ -116,34 +118,46 @@ void Frame::addWidget(Frame *frame)
|
||||
|
||||
const auto &docks = frame->dockWidgets();
|
||||
for (DockWidgetBase *dockWidget : docks)
|
||||
addWidget(dockWidget);
|
||||
addWidget(dockWidget, addingOption);
|
||||
}
|
||||
|
||||
void Frame::addWidget(FloatingWindow *floatingWindow)
|
||||
void Frame::addWidget(FloatingWindow *floatingWindow, AddingOption addingOption)
|
||||
{
|
||||
Q_ASSERT(floatingWindow);
|
||||
for (Frame *f : floatingWindow->frames())
|
||||
addWidget(f);
|
||||
addWidget(f, addingOption);
|
||||
}
|
||||
|
||||
void Frame::insertWidget(DockWidgetBase *dockWidget, int index)
|
||||
void Frame::insertWidget(DockWidgetBase *dockWidget, int index, AddingOption addingOption)
|
||||
{
|
||||
qCDebug(addwidget()) << Q_FUNC_INFO << ((void*)this) << "; dockWidget=" << dockWidget << "; oldFrame=" << dockWidget->frame();
|
||||
qCDebug(addwidget()) << Q_FUNC_INFO << ((void*)this) << "; dockWidget="
|
||||
<< dockWidget << "; oldFrame=" << dockWidget->frame()
|
||||
<< "; addingOption=" << addingOption;
|
||||
|
||||
Q_ASSERT(dockWidget);
|
||||
if (contains(dockWidget)) {
|
||||
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_layoutItem)
|
||||
dockWidget->addPlaceholderItem(m_layoutItem);
|
||||
|
||||
m_tabWidget->insertDockWidget(dockWidget, index);
|
||||
|
||||
if (hasSingleDockWidget()) {
|
||||
Q_EMIT currentDockWidgetChanged(dockWidget);
|
||||
setObjectName(dockWidget->uniqueName());
|
||||
if (addingOption == AddingOption_StartHidden) {
|
||||
dockWidget->close(); // Ensure closed
|
||||
} else {
|
||||
if (hasSingleDockWidget()) {
|
||||
Q_EMIT currentDockWidgetChanged(dockWidget);
|
||||
setObjectName(dockWidget->uniqueName());
|
||||
|
||||
if (!m_layoutItem) {
|
||||
// When adding the 1st dock widget of a fresh frame, let's give the frame the size
|
||||
// of the dock widget, so that when adding it to the main window, the main window can
|
||||
// use that size as the initial suggested size.
|
||||
resize(dockWidget->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect(dockWidget, &DockWidgetBase::titleChanged, this, &Frame::updateTitleAndIcon);
|
||||
@@ -312,7 +326,17 @@ DockWidgetBase *Frame::currentDockWidget() const
|
||||
bool Frame::anyNonClosable() const
|
||||
{
|
||||
for (auto dw : dockWidgets()) {
|
||||
if (dw->options() & DockWidgetBase::Option_NotClosable)
|
||||
if ((dw->options() & DockWidgetBase::Option_NotClosable) && !DockRegistry::self()->isProcessingAppQuitEvent())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Frame::anyNonDockable() const
|
||||
{
|
||||
for (auto dw : dockWidgets()) {
|
||||
if (dw->options() & DockWidgetBase::Option_NotDockable)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -397,6 +421,15 @@ bool Frame::hasTabsVisible() const
|
||||
return alwaysShowsTabs() || dockWidgetCount() > 1;
|
||||
}
|
||||
|
||||
QString Frame::affinityName() const
|
||||
{
|
||||
if (isEmpty()) {
|
||||
return {};
|
||||
} else {
|
||||
return dockWidgetAt(0)->affinityName();
|
||||
}
|
||||
}
|
||||
|
||||
DockWidgetBase *Frame::dockWidgetAt(int index) const
|
||||
{
|
||||
return qobject_cast<DockWidgetBase *>(m_tabWidget->dockwidgetAt(index));
|
||||
@@ -406,16 +439,19 @@ void Frame::setDropArea(DropArea *dt)
|
||||
{
|
||||
if (dt != m_dropArea) {
|
||||
qCDebug(docking) << "Frame::setDropArea dt=" << dt;
|
||||
const bool wasInMainWindow = dt && isInMainWindow();
|
||||
if (m_dropArea)
|
||||
disconnect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
|
||||
this, &Frame::updateTitleBarVisibility);
|
||||
disconnect(m_visibleWidgetCountChangedConnection);
|
||||
|
||||
m_dropArea = dt;
|
||||
|
||||
if (m_dropArea) {
|
||||
connect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
|
||||
this, &Frame::updateTitleBarVisibility);
|
||||
// We keep the connect result so we don't dereference m_dropArea at shutdown
|
||||
m_visibleWidgetCountChangedConnection = connect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
|
||||
this, &Frame::updateTitleBarVisibility);
|
||||
updateTitleBarVisibility();
|
||||
if (wasInMainWindow != isInMainWindow())
|
||||
Q_EMIT isInMainWindowChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,6 +540,9 @@ void Frame::scheduleDeleteLater()
|
||||
{
|
||||
qCDebug(creation) << Q_FUNC_INFO << this;
|
||||
m_beingDeleted = true;
|
||||
deleteLater();
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
// Can't use deleteLater() here due to QTBUG-83030 (deleteLater() never delivered if triggered by a sendEvent() before event loop starts)
|
||||
delete this;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -71,14 +71,14 @@ public:
|
||||
LayoutSaver::Frame serialize() const;
|
||||
|
||||
///@brief Adds a widget into the Frame's TabWidget
|
||||
void addWidget(DockWidgetBase *);
|
||||
void addWidget(DockWidgetBase *, AddingOption = AddingOption_None);
|
||||
///@overload
|
||||
void addWidget(Frame *);
|
||||
void addWidget(Frame *, AddingOption = AddingOption_None);
|
||||
///@overload
|
||||
void addWidget(FloatingWindow *floatingWindow);
|
||||
void addWidget(FloatingWindow *floatingWindow, AddingOption addingOption = AddingOption_None);
|
||||
|
||||
///@brief Inserts a widget into the Frame's TabWidget at @p index
|
||||
void insertWidget(DockWidgetBase *, int index);
|
||||
void insertWidget(DockWidgetBase *, int index, AddingOption addingOption = AddingOption_None);
|
||||
|
||||
///@brief removes a dockwidget from the frame
|
||||
void removeWidget(DockWidgetBase *);
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
|
||||
FrameOptions options() const { return m_options; }
|
||||
bool anyNonClosable() const;
|
||||
|
||||
bool anyNonDockable() const;
|
||||
|
||||
///@brief returns whether there's 0 dock widgets. If not persistent then the Frame will delete itself.
|
||||
bool isEmpty() const { return dockWidgetCount() == 0; }
|
||||
@@ -202,11 +202,14 @@ public:
|
||||
**/
|
||||
bool hasTabsVisible() const;
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *);
|
||||
void numDockWidgetsChanged();
|
||||
void hasTabsVisibleChanged();
|
||||
void layoutInvalidated();
|
||||
void isInMainWindowChanged();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Frame)
|
||||
@@ -222,6 +225,7 @@ private:
|
||||
const FrameOptions m_options;
|
||||
QPointer<Item> m_layoutItem;
|
||||
bool m_beingDeleted = false;
|
||||
QMetaObject::Connection m_visibleWidgetCountChangedConnection;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -35,7 +35,9 @@
|
||||
#include <QObject>
|
||||
#include <QMenu>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStandardItem;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
namespace Debug {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -51,6 +51,8 @@ TitleBar::TitleBar(FloatingWindow *parent)
|
||||
{
|
||||
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateCloseButton);
|
||||
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateFloatButton);
|
||||
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMaximizeButton);
|
||||
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -66,7 +68,11 @@ TitleBar::~TitleBar()
|
||||
|
||||
bool TitleBar::onDoubleClicked()
|
||||
{
|
||||
if (supportsFloatingButton()) {
|
||||
if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes) && m_floatingWindow) {
|
||||
// Not using isFloating(), as that can be a dock widget nested in a floating window. By convention it's floating, but it's not the title bar of the top-level window.
|
||||
toggleMaximized();
|
||||
return true;
|
||||
} else if (supportsFloatingButton()) {
|
||||
onFloatClicked();
|
||||
return true;
|
||||
}
|
||||
@@ -74,6 +80,17 @@ bool TitleBar::onDoubleClicked()
|
||||
return false;
|
||||
}
|
||||
|
||||
void TitleBar::toggleMaximized()
|
||||
{
|
||||
if (!m_floatingWindow)
|
||||
return;
|
||||
|
||||
if (m_floatingWindow->isMaximized())
|
||||
m_floatingWindow->showNormal();
|
||||
else
|
||||
m_floatingWindow->showMaximized();
|
||||
}
|
||||
|
||||
void TitleBar::setTitle(const QString &title)
|
||||
{
|
||||
if (title != m_title) {
|
||||
@@ -138,11 +155,26 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
|
||||
|
||||
bool TitleBar::supportsFloatingButton() const
|
||||
{
|
||||
if (Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton) {
|
||||
// Apps having a maximize/restore button traditionally don't have a floating one,
|
||||
// QDockWidget style only has floating and no maximize/restore.
|
||||
// We can add an option later if we need them to co-exist
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have a floating window with nested dock widgets we can't re-attach, because we don't
|
||||
// know where to
|
||||
return !m_floatingWindow || m_floatingWindow->hasSingleFrame();
|
||||
}
|
||||
|
||||
bool TitleBar::supportsMaximizeButton() const
|
||||
{
|
||||
if (!(Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton))
|
||||
return false;
|
||||
|
||||
return m_floatingWindow != nullptr;
|
||||
}
|
||||
|
||||
bool TitleBar::hasIcon() const
|
||||
{
|
||||
return !m_icon.isNull();
|
||||
@@ -222,3 +254,8 @@ void TitleBar::onFloatClicked()
|
||||
makeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void TitleBar::onMaximizeClicked()
|
||||
{
|
||||
toggleMaximized();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -31,8 +31,10 @@
|
||||
#include <QVector>
|
||||
#include <QIcon>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QHBoxLayout;
|
||||
class QLabel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
@@ -65,9 +67,12 @@ public:
|
||||
/// There should always be at least 1. If more than 1 then they are tabbed.
|
||||
DockWidgetBase::List dockWidgets() const;
|
||||
|
||||
///@brief returns whether this title bar supports a floating/unfloating button
|
||||
///@brief returns whether this title bar supports a floating/docking button
|
||||
bool supportsFloatingButton() const;
|
||||
|
||||
///@brief returns whether this title bar supports a maximize/restore button
|
||||
bool supportsMaximizeButton() const;
|
||||
|
||||
///@brief returns whether this title bar has an icon
|
||||
bool hasIcon() const;
|
||||
|
||||
@@ -83,6 +88,8 @@ public:
|
||||
///@brief getter for m_floatingWindow
|
||||
const FloatingWindow *floatingWindow() const { return m_floatingWindow; }
|
||||
|
||||
virtual void updateCloseButton() {}
|
||||
|
||||
Q_SIGNALS:
|
||||
void titleChanged();
|
||||
void iconChanged();
|
||||
@@ -90,8 +97,11 @@ Q_SIGNALS:
|
||||
protected:
|
||||
void onCloseClicked();
|
||||
void onFloatClicked();
|
||||
void onMaximizeClicked();
|
||||
void toggleMaximized();
|
||||
|
||||
virtual void updateFloatButton() {}
|
||||
virtual void updateCloseButton() {}
|
||||
virtual void updateMaximizeButton() {}
|
||||
|
||||
// The following are needed for the unit-tests
|
||||
virtual bool isCloseButtonVisible() const { return true; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "Config.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
|
||||
#ifdef QT_X11EXTRAS_LIB
|
||||
# include <QtX11Extras/QX11Info>
|
||||
@@ -65,6 +68,30 @@ inline bool windowManagerHasTranslucency()
|
||||
return true;
|
||||
}
|
||||
|
||||
inline QSize screenSizeForWidget(const QWidget *w)
|
||||
{
|
||||
QWidget *topLevel = w->window();
|
||||
if (QWindow *window = topLevel->windowHandle()) {
|
||||
if (QScreen *screen = window->screen()) {
|
||||
return screen->size();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline int screenNumberForWidget(const QWidget *w)
|
||||
{
|
||||
QWidget *topLevel = w->window();
|
||||
if (QWindow *window = topLevel->windowHandle()) {
|
||||
if (QScreen *screen = window->screen()) {
|
||||
return qApp->screens().indexOf(screen);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "FloatingWindow_p.h"
|
||||
#include "TitleBar_p.h"
|
||||
#include "DragController_p.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
@@ -266,15 +267,23 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
|
||||
|
||||
return *result != 0;
|
||||
} else if (msg->message == WM_NCLBUTTONDBLCLK) {
|
||||
// We don't want double click to maximize the window
|
||||
if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes)) {
|
||||
// By returning false we accept Windows native action, a maximize.
|
||||
// We could also call titleBar->onDoubleClicked(); here which will maximize if Flag_DoubleClickMaximizes is set,
|
||||
// but there's a bug in QWidget::showMaximized() on Windows when we're covering the native title bar, the window is maximized with an offset.
|
||||
// So instead, use a native maximize which works well
|
||||
return false;
|
||||
} else {
|
||||
// Let the title bar handle it. It will re-dock the window.
|
||||
|
||||
if (TitleBar *titleBar = w->titleBar()) {
|
||||
if (titleBar->isVisible()) { // can't be invisible afaik
|
||||
titleBar->onDoubleClicked();
|
||||
if (TitleBar *titleBar = w->titleBar()) {
|
||||
if (titleBar->isVisible()) { // can't be invisible afaik
|
||||
titleBar->onDoubleClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
} else if (msg->message == WM_GETMINMAXINFO) {
|
||||
// Qt doesn't work well with windows that don't have title bar but have native frames.
|
||||
// When maximized they go out of bounds and the title bar is clipped, so catch WM_GETMINMAXINFO
|
||||
@@ -282,8 +291,9 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
|
||||
|
||||
// According to microsoft docs it only works for the primary screen, but extrapolates for the others
|
||||
QScreen *screen = QApplication::primaryScreen();
|
||||
if (w->isMaximized() || !screen || w->windowHandle()->screen() != screen)
|
||||
if (!screen || w->windowHandle()->screen() != screen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,7 +25,9 @@
|
||||
#include <QPoint>
|
||||
#include <QDebug>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMouseEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "DropArea_p.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QState>
|
||||
#include <QStateMachine>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,7 +23,9 @@
|
||||
|
||||
#include "DropIndicatorOverlayInterface_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QRubberBand;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Separator_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <QRubberBand>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
@@ -45,6 +46,8 @@ Anchor::Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter,
|
||||
, m_type(type)
|
||||
, m_layout(multiSplitter)
|
||||
, m_separatorWidget(Config::self().frameworkWidgetFactory()->createSeparator(this, multiSplitter->multiSplitter()))
|
||||
, m_lazyResize(Config::self().flags() & Config::Flag_LazyResize)
|
||||
, m_lazyResizeRubberBand(m_lazyResize ? new QRubberBand(QRubberBand::Line, multiSplitter->multiSplitter()) : nullptr)
|
||||
{
|
||||
multiSplitter->insertAnchor(this);
|
||||
connect(this, &QObject::objectNameChanged, m_separatorWidget, &QObject::setObjectName);
|
||||
@@ -252,7 +255,15 @@ void Anchor::setPosition(int p, SetPositionOptions options)
|
||||
|
||||
void Anchor::updatePositionPercentage()
|
||||
{
|
||||
m_positionPercentage = (position() * 1.0) / m_layout->width();
|
||||
const int layoutLength = m_layout->length(m_orientation);
|
||||
m_positionPercentage = (position() * 1.0) / layoutLength;
|
||||
|
||||
if (position() > layoutLength) {
|
||||
// This warning makes the unit-tests fail if some invalid m_positionPercentage ever appears.
|
||||
// Bug fixed now though.
|
||||
qWarning() << Q_FUNC_INFO << "Weird position percentage" << m_positionPercentage
|
||||
<< position() << layoutLength;
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::position() const
|
||||
@@ -500,21 +511,45 @@ int Anchor::cumulativeMinLength(Anchor::Side side) const
|
||||
(side == Side1 && (m_type & (Type_RightStatic | Type_BottomStatic))))
|
||||
return 2 * staticAnchorThickness;
|
||||
}
|
||||
const CumulativeMin result = cumulativeMinLength_recursive(side);
|
||||
|
||||
const int numNonStaticAnchors = result.numItems >= 2 ? result.numItems - 1
|
||||
: 0;
|
||||
|
||||
int r = Anchor::thickness(isStatic()) + Anchor::thickness(true)
|
||||
+ numNonStaticAnchors*Anchor::thickness(false)
|
||||
+ result.minLength;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Anchor::CumulativeMin Anchor::cumulativeMinLength_recursive(Anchor::Side side) const
|
||||
{
|
||||
const auto items = this->items(side);
|
||||
int minLength = 0;
|
||||
CumulativeMin result = { 0, 0 };
|
||||
|
||||
for (auto item : items) {
|
||||
const int itemMin = item->cumulativeMinLength(side, orientation());
|
||||
minLength = qMax(itemMin, minLength);
|
||||
Anchor *oppositeAnchor = item->anchorAtSide(side, orientation());
|
||||
if (!oppositeAnchor) {
|
||||
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
|
||||
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
CumulativeMin candidateMin = { 0, 0 };
|
||||
if (!item->isPlaceholder()) {
|
||||
candidateMin.numItems++;
|
||||
candidateMin.minLength = item->minLength(orientation());
|
||||
}
|
||||
|
||||
candidateMin += oppositeAnchor->cumulativeMinLength_recursive(side);
|
||||
|
||||
if (candidateMin.minLength >= result.minLength) {
|
||||
result = candidateMin;
|
||||
}
|
||||
}
|
||||
|
||||
auto map = m_layout->anchorsShouldFollow();
|
||||
|
||||
// Dont' use isFollowing() here, because when restoring a placeholder we clear the followers first
|
||||
const bool willFollow = map.contains(const_cast<Anchor*>(this));
|
||||
const int thickness = willFollow ? 0 : Anchor::thickness(isStatic());
|
||||
|
||||
return thickness + minLength;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Anchor::setFollowee(Anchor *followee)
|
||||
@@ -668,6 +703,22 @@ void Anchor::setThickness()
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::setLazyPosition(int pos)
|
||||
{
|
||||
if (m_lazyPosition != pos) {
|
||||
m_lazyPosition = pos;
|
||||
|
||||
QRect geo = m_separatorWidget->geometry();
|
||||
if (isVertical()) {
|
||||
geo.moveLeft(pos);
|
||||
} else {
|
||||
geo.moveTop(pos);
|
||||
}
|
||||
|
||||
m_lazyResizeRubberBand->setGeometry(geo);
|
||||
}
|
||||
}
|
||||
|
||||
int Anchor::position(QPoint p) const
|
||||
{
|
||||
return isVertical() ? p.x() : p.y();
|
||||
@@ -729,10 +780,20 @@ void Anchor::onMousePress()
|
||||
s_isResizing = true;
|
||||
m_layout->setAnchorBeingDragged(this);
|
||||
qCDebug(anchors) << "Drag started";
|
||||
|
||||
if (m_lazyResize) {
|
||||
setLazyPosition(position());
|
||||
m_lazyResizeRubberBand->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Anchor::onMouseReleased()
|
||||
{
|
||||
if (m_lazyResize) {
|
||||
m_lazyResizeRubberBand->hide();
|
||||
setPosition(m_lazyPosition);
|
||||
}
|
||||
|
||||
s_isResizing = false;
|
||||
m_layout->setAnchorBeingDragged(nullptr);
|
||||
}
|
||||
@@ -769,7 +830,11 @@ void Anchor::onMouseMoved(QPoint pt)
|
||||
m_lastMoveDirection = positionToGoTo < position() ? Side1
|
||||
: (positionToGoTo > position() ? Side2
|
||||
: Side_None); // Side_None shouldn't happen though.
|
||||
setPosition(positionToGoTo);
|
||||
|
||||
if (m_lazyResize)
|
||||
setLazyPosition(positionToGoTo);
|
||||
else
|
||||
setPosition(positionToGoTo);
|
||||
}
|
||||
|
||||
void Anchor::onWidgetMoved(int p)
|
||||
@@ -792,7 +857,8 @@ Anchor *Anchor::deserialize(const LayoutSaver::Anchor &a, MultiSplitterLayout *l
|
||||
auto anchor = new Anchor(Qt::Orientation(a.orientation), layout, Anchor::Type(a.type));
|
||||
anchor->setObjectName(a.objectName);
|
||||
anchor->setGeometry(a.geometry);
|
||||
anchor->updatePositionPercentage();
|
||||
anchor->m_positionPercentage = a.positionPercentage;
|
||||
|
||||
anchor->setProperty("indexFrom", a.indexOfFrom);
|
||||
anchor->setProperty("indexTo", a.indexOfTo);
|
||||
anchor->setProperty("indexFolowee", a.indexOfFollowee);
|
||||
@@ -829,6 +895,7 @@ LayoutSaver::Anchor Anchor::serialize() const
|
||||
a.indexOfFrom = allAnchors.indexOf(from());
|
||||
a.indexOfTo = allAnchors.indexOf(to());
|
||||
a.indexOfFollowee = followee() ? allAnchors.indexOf(followee()) : -1;
|
||||
a.positionPercentage = m_positionPercentage;
|
||||
|
||||
a.side1Items.clear();
|
||||
a.side1Items.reserve(this->side1Items().size());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -29,6 +29,10 @@
|
||||
#include <QRect>
|
||||
#include <QVector>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QRubberBand;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class Item;
|
||||
@@ -281,7 +285,19 @@ public:
|
||||
static bool isResizing();
|
||||
|
||||
private:
|
||||
struct CumulativeMin {
|
||||
int minLength;
|
||||
int numItems;
|
||||
CumulativeMin& operator+=(CumulativeMin other) {
|
||||
minLength += other.minLength;
|
||||
numItems += other.numItems;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
CumulativeMin cumulativeMinLength_recursive(Anchor::Side side) const;
|
||||
|
||||
void setThickness();
|
||||
void setLazyPosition(int);
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(int pos);
|
||||
@@ -308,7 +324,7 @@ public:
|
||||
QPointer<Anchor> m_from;// QPointer just so we can assert. They should never be null.
|
||||
QPointer<Anchor> m_to;
|
||||
const Type m_type;
|
||||
qreal m_positionPercentage = 0.0;
|
||||
qreal m_positionPercentage = 0.0; // Should be between 0 and 1
|
||||
|
||||
// Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise.
|
||||
Side m_lastMoveDirection = Side_None;
|
||||
@@ -329,6 +345,9 @@ public:
|
||||
QRect m_geometry;
|
||||
Anchor *m_followee = nullptr;
|
||||
QMetaObject::Connection m_followeeDestroyedConnection;
|
||||
const bool m_lazyResize;
|
||||
int m_lazyPosition = 0;
|
||||
QRubberBand *const m_lazyResizeRubberBand;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -233,6 +233,9 @@ void Item::ensureMinSize(Qt::Orientation orientation)
|
||||
Anchor *anchor1 = anchorGroup().anchorAtSide(Anchor::Side1, orientation);
|
||||
Anchor *anchor2 = anchorGroup().anchorAtSide(Anchor::Side2, orientation);
|
||||
|
||||
anchor1 = anchor1->isFollowing() ? anchor1->endFollowee() : anchor1;
|
||||
anchor2 = anchor2->isFollowing() ? anchor2->endFollowee() : anchor2;
|
||||
|
||||
const int bound1 = d->m_layout->boundPositionForAnchor(anchor1, Anchor::Side1);
|
||||
const int bound2 = d->m_layout->boundPositionForAnchor(anchor2, Anchor::Side2);
|
||||
|
||||
@@ -241,7 +244,7 @@ void Item::ensureMinSize(Qt::Orientation orientation)
|
||||
|
||||
const int suggestedDelta1 = qMin(delta, qCeil(delta / 2) + anchor1->thickness() + 1);
|
||||
const int maxPos1 = bound2 - newLength - anchor1->thickness();
|
||||
const int newPosition1 = qMax(qMin(maxPos1, anchor1->position() - suggestedDelta1), bound1); // Honour the bound
|
||||
const int newPosition1 = qMin(anchor1->position(), qMax(qMin(maxPos1, anchor1->position() - suggestedDelta1), bound1)); // Honour the bound
|
||||
const int newPosition2 = newPosition1 + anchor1->thickness() + newLength; // No need to check bound2, we have enough space afterall
|
||||
|
||||
if (!anchor1->isStatic())
|
||||
@@ -365,17 +368,6 @@ const AnchorGroup &Item::anchorGroup() const
|
||||
return d->m_anchorGroup;
|
||||
}
|
||||
|
||||
int Item::cumulativeMinLength(Anchor::Side side, Qt::Orientation orientation) const
|
||||
{
|
||||
Anchor *oppositeAnchor = anchorAtSide(side, orientation);
|
||||
if (!oppositeAnchor) {
|
||||
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
|
||||
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
|
||||
return 0;
|
||||
}
|
||||
return minLength(orientation) + oppositeAnchor->cumulativeMinLength(side);
|
||||
}
|
||||
|
||||
QSize Item::minimumSize() const
|
||||
{
|
||||
return isPlaceholder() ? QSize(0, 0)
|
||||
@@ -448,6 +440,9 @@ void Item::onLayoutRequest() const
|
||||
if (!d->m_frame || d->m_isPlaceholder)
|
||||
return; // It's a placeholder, nothing to do.
|
||||
|
||||
if (LayoutSaver::restoreInProgress())
|
||||
return; // we don't even have the anchors yet, nothing to do
|
||||
|
||||
if (d->m_frame->geometry() != geometry()) {
|
||||
// The frame is controlled by the layout, it can't change its geometry on its own.
|
||||
// Put it back.
|
||||
@@ -595,14 +590,15 @@ void Item::Private::turnIntoPlaceholder()
|
||||
m_layout->clearAnchorsFollowing();
|
||||
|
||||
AnchorGroup anchorGroup = q->anchorGroup();
|
||||
auto layout = m_layout; // copy it, since we're deleting 'q', which deletes 'this'
|
||||
if (anchorGroup.isValid()) {
|
||||
m_layout->emitVisibleWidgetCountChanged();
|
||||
layout->emitVisibleWidgetCountChanged();
|
||||
} else {
|
||||
// Auto-destruction, which removes it from the layout
|
||||
delete q;
|
||||
}
|
||||
|
||||
m_layout->updateAnchorFollowing(anchorGroup);
|
||||
layout->updateAnchorFollowing(anchorGroup);
|
||||
}
|
||||
|
||||
void Item::Private::setIsPlaceholder(bool is)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -150,8 +150,6 @@ public:
|
||||
AnchorGroup& anchorGroup();
|
||||
const AnchorGroup& anchorGroup() const;
|
||||
|
||||
int cumulativeMinLength(Anchor::Side, Qt::Orientation orientation) const;
|
||||
|
||||
QSize minimumSize() const;
|
||||
|
||||
bool isPlaceholder() const;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -171,6 +171,57 @@ bool MultiSplitterLayout::validateInputs(QWidgetOrQuick *widget,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<int,int> MultiSplitterLayout::boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const
|
||||
{
|
||||
const int bound1 = boundPositionForAnchor(anchor1, Anchor::Side1);
|
||||
const int bound2 = boundPositionForAnchor(anchor2, Anchor::Side2);
|
||||
|
||||
if (newPos1 >= bound1 && newPos2 <= bound2) {
|
||||
// Simplest case, it's bounded.
|
||||
return { newPos1, newPos2 };
|
||||
}
|
||||
|
||||
if (newPos1 < bound1) {
|
||||
// the anchor1 is out of bounds
|
||||
|
||||
const int bythismuch = bound1 - newPos1;
|
||||
newPos1 = bound1;
|
||||
newPos2 = newPos2 + bythismuch;
|
||||
|
||||
if (newPos2 > bound2) {
|
||||
qWarning() << "Adjusted interval still out of bounds. Not enough space. #1"
|
||||
<< "; newPos1=" << newPos1
|
||||
<< "; newPos2=" << newPos2
|
||||
<< "; bounds=" << bound1 << bound2
|
||||
<< "; anchor1=" << anchor1
|
||||
<< "; anchor2=" << anchor2
|
||||
<< "; size=" << size();
|
||||
}
|
||||
|
||||
return { newPos1, newPos2 };
|
||||
} else if (newPos2 > bound2) {
|
||||
// the anchor2 is out of bounds
|
||||
|
||||
const int bythismuch = newPos2 - bound2;
|
||||
newPos2 = bound2;
|
||||
newPos1 = newPos1 - bythismuch;
|
||||
|
||||
if (newPos1 < bound1) {
|
||||
qWarning() << "Adjusted interval still out of bounds. Not enough space. #2"
|
||||
<< "; newPos1=" << newPos1
|
||||
<< "; newPos2=" << newPos2
|
||||
<< "; bounds=" << bound1 << bound2
|
||||
<< "; anchor1=" << anchor1
|
||||
<< "; anchor2=" << anchor2
|
||||
<< "; size=" << size();
|
||||
}
|
||||
|
||||
return { newPos1, newPos2 };
|
||||
}
|
||||
|
||||
return { newPos1, newPos2 };
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame *relativeToWidget, AddingOption option)
|
||||
{
|
||||
auto frame = qobject_cast<Frame*>(w);
|
||||
@@ -267,6 +318,7 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
|
||||
case Location_OnTop:
|
||||
direction1Anchor = existingAnchor;
|
||||
direction2Anchor = newAnchor;
|
||||
std::tie(posForExistingAnchor, posForNewAnchor) = boundInterval(posForExistingAnchor, existingAnchor, posForNewAnchor, newAnchor);
|
||||
delta1 = originalExistingAnchorPos - posForExistingAnchor;
|
||||
delta2 = posForNewAnchor - posForExistingAnchor;
|
||||
break;
|
||||
@@ -274,6 +326,7 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
|
||||
case Location_OnBottom:
|
||||
direction1Anchor = newAnchor;
|
||||
direction2Anchor = existingAnchor;
|
||||
std::tie(posForNewAnchor, posForExistingAnchor) = boundInterval(posForNewAnchor, newAnchor, posForExistingAnchor, existingAnchor);
|
||||
delta1 = posForExistingAnchor - posForNewAnchor;
|
||||
delta2 = posForExistingAnchor - originalExistingAnchorPos;
|
||||
break;
|
||||
@@ -297,16 +350,13 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
|
||||
existingAnchor->setPosition(posForExistingAnchor);
|
||||
}
|
||||
|
||||
// If you drop a 100px in the middle of a layout, it will steal some space from the left widgets
|
||||
// and still some space from the right ones. delta1 is the space stolen at the left
|
||||
// delta2 is the space stolen at the right. The sum of delta1+delta2 is the size of the widget
|
||||
// (plus the splitter). Then we propagate the resize, so that all widgets chip in and get smaller
|
||||
// to make room for ours.
|
||||
propagateResize(delta1, direction1Anchor, /*direction*/ Anchor::Side1);
|
||||
propagateResize(delta2, direction2Anchor, /*direction*/ Anchor::Side2);
|
||||
// Make sure not just the side1/side2 adjacent widgets are contributing space for our new widget
|
||||
// the ones adjacents to the adjacents (recursive) must also give.
|
||||
// The code would work fine without this, it's just that it wouldn't look fair.
|
||||
propagateResize(delta1, direction1Anchor, Anchor::Side1);
|
||||
propagateResize(delta2, direction2Anchor, Anchor::Side2);
|
||||
}
|
||||
|
||||
|
||||
if (newAnchor) {
|
||||
// Also ensure the widget has a minimum size in the other direction. So, when adding to
|
||||
// left/right, it will still have its minimum height honoured, and vice-versa.
|
||||
@@ -343,8 +393,9 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
|
||||
addItems_internal(ItemList{ item });
|
||||
}
|
||||
|
||||
m_addingItem = false;
|
||||
updateAnchorFollowing();
|
||||
m_addingItem = false;
|
||||
|
||||
maybeCheckSanity();
|
||||
}
|
||||
|
||||
@@ -451,8 +502,10 @@ static Anchor::List removeSmallestPath(QVector<Anchor::List> &paths)
|
||||
|
||||
void MultiSplitterLayout::propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction)
|
||||
{
|
||||
Q_ASSERT(delta >= 0);
|
||||
if (delta == 0 || fromAnchor->isStatic())
|
||||
if (delta < 0)
|
||||
qWarning() << Q_FUNC_INFO << "Invalid delta" << delta << fromAnchor << direction;
|
||||
|
||||
if (delta <= 0 || fromAnchor->isStatic())
|
||||
return;
|
||||
|
||||
QVector<Anchor::List> paths;
|
||||
@@ -551,6 +604,19 @@ void MultiSplitterLayout::ensureItemsMinSize()
|
||||
}
|
||||
}
|
||||
|
||||
QString MultiSplitterLayout::affinityName() const
|
||||
{
|
||||
if (auto ms = multiSplitter()) {
|
||||
if (auto mainWindow = ms->mainWindow()) {
|
||||
return mainWindow->affinityName();
|
||||
} else if (auto fw = ms->floatingWindow()) {
|
||||
return fw->affinityName();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::addMultiSplitter(MultiSplitter *sourceMultiSplitter,
|
||||
Location location,
|
||||
Frame *relativeTo)
|
||||
@@ -668,6 +734,9 @@ QPair<int, int> MultiSplitterLayout::boundPositionsForAnchor(Anchor *anchor) con
|
||||
}
|
||||
}
|
||||
|
||||
if (anchor->isFollowing())
|
||||
anchor = anchor->endFollowee();
|
||||
|
||||
const int minSide1Length = anchor->cumulativeMinLength(Anchor::Side1);
|
||||
const int minSide2Length = anchor->cumulativeMinLength(Anchor::Side2);
|
||||
const int length = anchor->isVertical() ? width() : height();
|
||||
@@ -745,6 +814,8 @@ MultiSplitterLayout::Length MultiSplitterLayout::availableLengthForDrop(Location
|
||||
break;
|
||||
}
|
||||
|
||||
anchor = anchor->isFollowing() ? anchor->endFollowee() : anchor;
|
||||
|
||||
const int minForAlreadyOccupied1 = anchor->cumulativeMinLength(Anchor::Side1) - anchor->thickness(); // TODO: Check if this is correct, we're discounting the anchor twice
|
||||
const int minForAlreadyOccupied2 = anchor->cumulativeMinLength(Anchor::Side2) - anchor->thickness();
|
||||
|
||||
@@ -966,7 +1037,10 @@ void MultiSplitterLayout::dumpDebug() const
|
||||
{
|
||||
Q_EMIT aboutToDumpDebug();
|
||||
qDebug() << Q_FUNC_INFO << "m_size=" << m_size
|
||||
<< "; minimumSize=" << minimumSize();
|
||||
<< "; minimumSize=" << minimumSize()
|
||||
<< "; parentWidget.size=" << multiSplitter()->size()
|
||||
<< "; window=" << multiSplitter()->window()
|
||||
<< "; window.size=" << multiSplitter()->window()->size();
|
||||
|
||||
qDebug() << "Items:";
|
||||
for (auto item : items()) {
|
||||
@@ -1000,7 +1074,8 @@ void MultiSplitterLayout::dumpDebug() const
|
||||
<< "; isFollowing=" << anchor->isFollowing()
|
||||
<< "; followee=" << anchor->followee()
|
||||
<< "; from=" << ((void*)anchor->from())
|
||||
<< "; to=" << ((void*)anchor->to());
|
||||
<< "; to=" << ((void*)anchor->to())
|
||||
<< "; positionPercentage=" << anchor->positionPercentage();
|
||||
}
|
||||
|
||||
qDebug() << "Num Frame:" << Frame::dbg_numFrames();
|
||||
@@ -1016,6 +1091,13 @@ void MultiSplitterLayout::positionStaticAnchors()
|
||||
m_rightAnchor->setPosition(width() - m_rightAnchor->thickness());
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::redistributeSpace()
|
||||
{
|
||||
positionStaticAnchors();
|
||||
redistributeSpace_recursive(m_leftAnchor, 0);
|
||||
redistributeSpace_recursive(m_topAnchor, 0);
|
||||
}
|
||||
|
||||
void MultiSplitterLayout::redistributeSpace(QSize oldSize, QSize newSize)
|
||||
{
|
||||
positionStaticAnchors();
|
||||
@@ -1283,7 +1365,8 @@ bool MultiSplitterLayout::checkSanity(AnchorSanityOption options) const
|
||||
if ((options & AnchorSanity_WidgetInvalidSizes) && !item->isPlaceholder()) {
|
||||
if (item->width() <= 0 || item->height() <= 0) {
|
||||
dumpDebug();
|
||||
qWarning() << "Invalid size for widget" << item << item->size() << "; isPlaceholder=" << item->isPlaceholder();
|
||||
qWarning() << "Invalid size for widget" << item << item->size() << "; isPlaceholder=" << item->isPlaceholder()
|
||||
<< "; minSize=" << item->minimumSize();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1512,6 +1595,7 @@ void MultiSplitterLayout::restorePlaceholder(Item *item)
|
||||
Q_ASSERT(anchorGroup.isStaticOrFollowsStatic());
|
||||
anchorGroup.updateItemSizes();
|
||||
maybeCheckSanity();
|
||||
item->endBlockPropagateGeo();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1553,27 +1637,6 @@ void MultiSplitterLayout::restorePlaceholder(Item *item)
|
||||
const int boundPosition2 = side2Anchor->isStatic() ? side2Anchor->position()
|
||||
: boundPositionForAnchor(side2Anchor, Anchor::Side2);
|
||||
|
||||
// Double check the available space again, for sanity
|
||||
if (!anchorGroup.hasAvailableSizeFor(newSize, orientation)) {
|
||||
qWarning() << "There's not enough space: bound2=" << boundPosition2
|
||||
<< "; bound1=" << boundPosition1
|
||||
<< "; newSize=" << newSize
|
||||
<< "; anchorGroup.available" << anchorGroup.availableSize()
|
||||
<< "; widgetMinSize=" << widgetMinSize
|
||||
<< "; newspace=" << boundPosition2 - boundPosition1 - side1Anchor->thickness()
|
||||
<< "; available_old=" << availableSize
|
||||
<< "; available_new=" << this->availableSize()
|
||||
<< "; anchors=" << side1Anchor << side2Anchor
|
||||
<< "; oldPos1=" << oldPosition1
|
||||
<< "; oldPos2=" << oldPosition2
|
||||
<< "; thickness=" << side1Anchor->thickness() << side2Anchor->thickness()
|
||||
<< "; isFollowing=" << side1Anchor->isFollowing() << side2Anchor->isFollowing()
|
||||
<< "; static=" << side1Anchor->isStatic() << side2Anchor->isStatic()
|
||||
<< "; size=" << m_size
|
||||
<< "; m_minSize=" << m_minSize;
|
||||
return;
|
||||
}
|
||||
|
||||
const int newLength = anchorFollowingInwards->isVertical() ? newSize.width() : newSize.height();
|
||||
// Let's try that each anchor contributes 50%, so that the widget appears centered
|
||||
const int suggestedLength1 = qMin(newLength, qCeil(newLength / 2) + side1Anchor->thickness() + 1);
|
||||
@@ -1791,7 +1854,7 @@ void MultiSplitterLayout::updateAnchorFollowing(const AnchorGroup &groupBeingRem
|
||||
}
|
||||
}
|
||||
|
||||
if (doShift)
|
||||
if (doShift && !anchorToShift->isFollowing())
|
||||
anchorToShift->setPosition(newPosition);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -442,12 +442,14 @@ private:
|
||||
friend class Anchor;
|
||||
friend class TestDocks;
|
||||
friend class KDDockWidgets::Debug::DebugWindow;
|
||||
friend class LayoutSaver;
|
||||
|
||||
struct AnchorBounds {
|
||||
Anchor *side1;
|
||||
Anchor *side2;
|
||||
};
|
||||
|
||||
std::pair<int,int> boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const;
|
||||
void blockItemPropagateGeo(bool block);
|
||||
|
||||
/**
|
||||
@@ -549,6 +551,7 @@ private:
|
||||
* When this MultiSplitter is resized, it gives or steals the less/extra space evenly through
|
||||
* all widgets.
|
||||
**/
|
||||
void redistributeSpace();
|
||||
void redistributeSpace(QSize oldSize, QSize newSize);
|
||||
void redistributeSpace_recursive(Anchor *fromAnchor, int minAnchorPos);
|
||||
|
||||
@@ -561,11 +564,13 @@ private:
|
||||
|
||||
/**
|
||||
* Called by addWidget().
|
||||
* If you drop a 100px in the middle of a layout, it will steal some space from the left widgets
|
||||
* and still some space from the right ones. delta1 is the space stolen at the left
|
||||
* delta2 is the space stolen at the right. The sum of delta1+delta2 is the size of the widget
|
||||
* (plus the splitter). Then we propagate the resize, so that all widgets chip in and get smaller
|
||||
* to make room for ours.
|
||||
*
|
||||
* When adding a widget to a layout, it will steal space from the widgets on the left (or top) (@p direction being Anchor::Side1),
|
||||
* and from the widgets on the right (or bottom) (@p direction being Anchor::Side2).
|
||||
*
|
||||
* @param delta the amount of space we're stealing in the specified side
|
||||
* @param fromAnchor The anchor we're starting from
|
||||
* @param direction if we're going left/top (Side1) or right/bottom (Side2)
|
||||
*/
|
||||
void propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction);
|
||||
|
||||
@@ -583,6 +588,8 @@ private:
|
||||
bool isRestoringPlaceholder() const { return m_restoringPlaceholder; }
|
||||
bool isAddingItem() const { return m_addingItem; }
|
||||
|
||||
QString affinityName() const;
|
||||
|
||||
MultiSplitter *const m_multiSplitter;
|
||||
Anchor::List m_anchors;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
#include "DockWidgetBase.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCloseEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,7 +36,9 @@
|
||||
#include <QObject>
|
||||
#include <QCloseEvent>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QWindow;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user