Compare commits
201 Commits
minimize
...
fix-python
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fce8e6161 | ||
|
|
6479bcabfb | ||
|
|
a8c9735652 | ||
|
|
93b25b6a31 | ||
|
|
4eb5a0940e | ||
|
|
c2cc914b5c | ||
|
|
1219ba90af | ||
|
|
beef3c7fb5 | ||
|
|
52184ca72b | ||
|
|
78cd7c56f7 | ||
|
|
7805a1dc25 | ||
|
|
92f426e4c0 | ||
|
|
90f10042fd | ||
|
|
697c140883 | ||
|
|
3369816d31 | ||
|
|
819725351a | ||
|
|
860cbd29bc | ||
|
|
07db9f9a7a | ||
|
|
2fd4f9ce97 | ||
|
|
77d7cd7d58 | ||
|
|
e5cb42c4fe | ||
|
|
1fe1d619a9 | ||
|
|
697cc34c23 | ||
|
|
b00d1d80b0 | ||
|
|
67bb42c5b4 | ||
|
|
d50fcb80c5 | ||
|
|
9571ffc30e | ||
|
|
f4a88276b8 | ||
|
|
4ea254029a | ||
|
|
f5e85c2196 | ||
|
|
926103325c | ||
|
|
20b2988165 | ||
|
|
39aefd312b | ||
|
|
b5478bcb0d | ||
|
|
d403557b9e | ||
|
|
db9884ea64 | ||
|
|
74148aabc3 | ||
|
|
7bfac091ae | ||
|
|
60a68817b6 | ||
|
|
a56e6d7fe8 | ||
|
|
1f0b208922 | ||
|
|
9da147f50e | ||
|
|
c175451284 | ||
|
|
9a88cf3a72 | ||
|
|
d0255e5310 | ||
|
|
2a2fb0a4f2 | ||
|
|
b0ad939db8 | ||
|
|
5abf118b8c | ||
|
|
f5099167b6 | ||
|
|
30949bd4c3 | ||
|
|
63279c187e | ||
|
|
fad03f8e0b | ||
|
|
6edfcc4f02 | ||
|
|
17df7b5b4e | ||
|
|
62cbd823c9 | ||
|
|
bdcc211308 | ||
|
|
ddeb4611a1 | ||
|
|
ad639c8001 | ||
|
|
15dc64d550 | ||
|
|
03b575d16e | ||
|
|
4cacee45e8 | ||
|
|
873b860203 | ||
|
|
128ae2d889 | ||
|
|
b0ec0e22e4 | ||
|
|
6aa4e979dc | ||
|
|
36f225b859 | ||
|
|
48a75b2450 | ||
|
|
2af4f939dd | ||
|
|
b592d21064 | ||
|
|
3cdd5afe7a | ||
|
|
0ef56c1302 | ||
|
|
96f7f38a0e | ||
|
|
689ba7da8f | ||
|
|
951aeece6e | ||
|
|
2ea0d54e6c | ||
|
|
1c98fe04ca | ||
|
|
59a9ab6642 | ||
|
|
7ee5949ae2 | ||
|
|
721f08e29b | ||
|
|
df07133c9d | ||
|
|
b9e3024720 | ||
|
|
d848d3e39a | ||
|
|
3f26f4c8f2 | ||
|
|
dc9d709dd7 | ||
|
|
543f734650 | ||
|
|
3fb723a82f | ||
|
|
71c2f34ed4 | ||
|
|
d4b3bf9de6 | ||
|
|
2b234f313c | ||
|
|
d1b2d17f7d | ||
|
|
f062ab7a64 | ||
|
|
f009f57581 | ||
|
|
f86a818e69 | ||
|
|
bb80c2158d | ||
|
|
86fa6258f6 | ||
|
|
545536fab5 | ||
|
|
ef23553b66 | ||
|
|
fcf56664cc | ||
|
|
d932166bcd | ||
|
|
0c5783774d | ||
|
|
aa42ea15f7 | ||
|
|
9dd778d55d | ||
|
|
ac35a919f4 | ||
|
|
a32c5017e6 | ||
|
|
7995a6d13b | ||
|
|
d4ec2eec88 | ||
|
|
12ad3b3484 | ||
|
|
9a53c36c14 | ||
|
|
8fb3802343 | ||
|
|
c035df7e85 | ||
|
|
92f81e28ca | ||
|
|
7a87db608b | ||
|
|
d42fb81790 | ||
|
|
73934e2f90 | ||
|
|
1c8642298b | ||
|
|
bfc45bb0e9 | ||
|
|
58af9e2516 | ||
|
|
4228c044ea | ||
|
|
ccc0aec968 | ||
|
|
3718d3fe79 | ||
|
|
26ed9b722b | ||
|
|
6751669249 | ||
|
|
c7955dce1e | ||
|
|
2e432f402f | ||
|
|
07791cd901 | ||
|
|
1458da92d9 | ||
|
|
c56f998292 | ||
|
|
991075d69e | ||
|
|
234ca75728 | ||
|
|
e34d62d3e5 | ||
|
|
423be28afd | ||
|
|
eb7bed9601 | ||
|
|
fa09521012 | ||
|
|
bb30f322a3 | ||
|
|
0e35c93ae5 | ||
|
|
603224e553 | ||
|
|
9b82063d52 | ||
|
|
c210a523e3 | ||
|
|
e33151d482 | ||
|
|
835f67a106 | ||
|
|
a50f6ec602 | ||
|
|
4a49dbc6b4 | ||
|
|
05c843397b | ||
|
|
e4871eb340 | ||
|
|
0e696ff2dd | ||
|
|
9f15773a9b | ||
|
|
a00984f95a | ||
|
|
c7e9632f18 | ||
|
|
7080712501 | ||
|
|
03b574434c | ||
|
|
6a46744073 | ||
|
|
7e52c510b3 | ||
|
|
9bf060af9a | ||
|
|
b5973fcf8c | ||
|
|
ac7d06d4fe | ||
|
|
75e9137e3a | ||
|
|
5168940422 | ||
|
|
cfd2dafa53 | ||
|
|
188b0a0edd | ||
|
|
206c418ebc | ||
|
|
b0acf8d1ae | ||
|
|
f279ea41e6 | ||
|
|
d2a929f35d | ||
|
|
6f5258b5dd | ||
|
|
4f223a9439 | ||
|
|
ea98eb3d44 | ||
|
|
cdb8f85620 | ||
|
|
f459dcb8b3 | ||
|
|
967a539e71 | ||
|
|
db615da0ee | ||
|
|
c4b420bd31 | ||
|
|
a6c4730c44 | ||
|
|
eb1f4c5a14 | ||
|
|
b303af738c | ||
|
|
af5e11a265 | ||
|
|
8d7627069c | ||
|
|
9e94c0d67d | ||
|
|
55ddacf978 | ||
|
|
005e0552b6 | ||
|
|
54b28c42e2 | ||
|
|
2659ddb76b | ||
|
|
c85873c6ce | ||
|
|
6e32f3f915 | ||
|
|
b8891a5727 | ||
|
|
fcb6f5a6af | ||
|
|
0365f3194d | ||
|
|
76e2ec6429 | ||
|
|
a5d0a202a5 | ||
|
|
def752632f | ||
|
|
f296048063 | ||
|
|
1c1b816292 | ||
|
|
fbd463e18b | ||
|
|
9e01363648 | ||
|
|
67bf7e9775 | ||
|
|
d8c2100b24 | ||
|
|
d1a3e39f17 | ||
|
|
3d5c081333 | ||
|
|
e1085fb770 | ||
|
|
c294c19be7 | ||
|
|
87726fb0a7 | ||
|
|
8bd429db4c |
4
.vscode/settings.json
vendored
@@ -4,7 +4,7 @@
|
||||
"qevent": "cpp"
|
||||
},
|
||||
"cmake.configureSettings": {
|
||||
"OPTION_DEVELOPER_MODE" : "ON",
|
||||
"OPTION_QTQUICK" : true
|
||||
"KDDockWidgets_DEVELOPER_MODE" : "ON",
|
||||
"KDDockWidgets_QTQUICK" : true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,19 +27,24 @@
|
||||
# Build the API documentation.
|
||||
# Default=false
|
||||
#
|
||||
# -DOPTION_DEVELOPER_MODE=[true|false]
|
||||
# -DKDDockWidgets_DEVELOPER_MODE=[true|false]
|
||||
# Configure the build for a developer setup.
|
||||
# Enables some features that are not geared towards end-users.
|
||||
# Forces the test harness to be built.
|
||||
# Default=false
|
||||
#
|
||||
# -DOPTION_BUILD_PYTHON_BINDINGS=[true|false]
|
||||
# -DKDDockWidgets_QTQUICK == IN DEVELOPMENT. DO NOT USE! ==
|
||||
# Build for QtQuick instead of QtWidgets
|
||||
# Default=false
|
||||
#
|
||||
# -DKDDockWidgets_PYTHON_BINDINGS=[true|false]
|
||||
# Build/Generate python bindings. Always false for Debug builds
|
||||
# Default=false
|
||||
#
|
||||
# -DPYTHON_BINDINGS_INSTALL_PREFIX=[path]
|
||||
# -DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=[path]
|
||||
# alternative install path for python bindings
|
||||
# Default=CMAKE_INSTALL_PREFIX
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
if(POLICY CMP0020)
|
||||
@@ -49,10 +54,12 @@ if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "")
|
||||
set(USE_DEFAULT_INSTALL_LOCATION True)
|
||||
else()
|
||||
set(USE_DEFAULT_INSTALL_LOCATION False)
|
||||
set(KDAB_INSTALL True)
|
||||
if((NOT DEFINED ${USE_DEFAULT_INSTALL_LOCATION}) OR (NOT ${USE_DEFAULT_INSTALL_LOCATION}))
|
||||
if(NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "")
|
||||
set(KDAB_INSTALL False)
|
||||
endif()
|
||||
set(USE_DEFAULT_INSTALL_LOCATION ${KDAB_INSTALL} CACHE INTERNAL "Install to default KDAB Location" FORCE)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
|
||||
@@ -65,16 +72,17 @@ else()
|
||||
endif()
|
||||
|
||||
set(${PROJECT_NAME}_VERSION_MAJOR 1)
|
||||
set(${PROJECT_NAME}_VERSION_MINOR 0)
|
||||
set(${PROJECT_NAME}_VERSION_MINOR 1)
|
||||
set(${PROJECT_NAME}_VERSION_PATCH 95)
|
||||
|
||||
set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
|
||||
set(${PROJECT_NAME}_SOVERSION "1.1")
|
||||
set(${PROJECT_NAME}_SOVERSION "1.2")
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
option(OPTION_DEVELOPER_MODE "Developer Mode" OFF)
|
||||
option(OPTION_BUILD_PYTHON_BINDINGS "Build python bindings" OFF)
|
||||
if(OPTION_BUILD_PYTHON_BINDINGS AND (CMAKE_BUILD_TYPE MATCHES "^[Dd]eb" OR ${PROJECT_NAME}_STATIC))
|
||||
option(${PROJECT_NAME}_DEVELOPER_MODE "Developer Mode" OFF)
|
||||
option(${PROJECT_NAME}_PYTHON_BINDINGS "Build python bindings" OFF)
|
||||
if(${PROJECT_NAME}_PYTHON_BINDINGS AND (CMAKE_BUILD_TYPE MATCHES "^[Dd]eb" OR ${PROJECT_NAME}_STATIC))
|
||||
message(FATAL_ERROR "** Python Bindings are disabled in debug or static builds.")
|
||||
endif()
|
||||
option(${PROJECT_NAME}_TESTS "Build the tests" OFF)
|
||||
@@ -82,11 +90,11 @@ option(${PROJECT_NAME}_EXAMPLES "Build the examples" ON)
|
||||
option(${PROJECT_NAME}_DOCS "Build the API documentation" OFF)
|
||||
|
||||
#Always build the test harness in developer-mode
|
||||
if(OPTION_DEVELOPER_MODE)
|
||||
if(${PROJECT_NAME}_DEVELOPER_MODE)
|
||||
set(${PROJECT_NAME}_TESTS ON)
|
||||
endif()
|
||||
|
||||
# option(OPTION_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
|
||||
#option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
|
||||
|
||||
find_package(Qt5Widgets 5.9 REQUIRED)
|
||||
|
||||
@@ -102,15 +110,18 @@ include(InstallLocation)
|
||||
include(QtInstallPaths) #to set QT_INSTALL_FOO variables
|
||||
|
||||
macro(set_compiler_flags targetName)
|
||||
if(OPTION_DEVELOPER_MODE)
|
||||
if(${PROJECT_NAME}_DEVELOPER_MODE)
|
||||
target_compile_definitions(${targetName} PRIVATE DOCKS_DEVELOPER_MODE QT_FORCE_ASSERTS)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(${targetName} PRIVATE -Wall -Wextra -Werror -Wno-error=deprecated-declarations)
|
||||
if (APPLE)
|
||||
target_compile_options(${targetName} PRIVATE -Wweak-vtables)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
if(OPTION_QTQUICK)
|
||||
if(${PROJECT_NAME}_QTQUICK)
|
||||
find_package(Qt5Quick)
|
||||
find_package(Qt5QuickControls2)
|
||||
add_definitions(-DKDDOCKWIDGETS_QTQUICK)
|
||||
@@ -145,12 +156,12 @@ install(FILES LICENSE.txt README.md DESTINATION ${INSTALL_DOC_DIR})
|
||||
install(DIRECTORY LICENSES DESTINATION ${INSTALL_DOC_DIR})
|
||||
|
||||
add_subdirectory(src)
|
||||
if(OPTION_BUILD_PYTHON_BINDINGS)
|
||||
if(${PROJECT_NAME}_PYTHON_BINDINGS)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
|
||||
if(${PROJECT_NAME}_EXAMPLES)
|
||||
if (OPTION_QTQUICK)
|
||||
if (${PROJECT_NAME}_QTQUICK)
|
||||
add_subdirectory(examples/qtquick)
|
||||
set_compiler_flags(kddockwidgets_example_quick)
|
||||
else()
|
||||
@@ -161,13 +172,13 @@ if(${PROJECT_NAME}_EXAMPLES)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(OPTION_DEVELOPER_MODE)
|
||||
if(${PROJECT_NAME}_DEVELOPER_MODE)
|
||||
include(ECMEnableSanitizers)
|
||||
if(${PROJECT_NAME}_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
|
||||
if (NOT OPTION_QTQUICK)
|
||||
if (NOT ${PROJECT_NAME}_QTQUICK)
|
||||
#Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
|
||||
if(NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
|
||||
# tst_docks.exe is pretty big (140 tests), so split it in 6 runs so we can use threads.
|
||||
|
||||
11
Changelog
@@ -1,19 +1,24 @@
|
||||
* v1.2.0 (unreleased)
|
||||
- QtQuick support
|
||||
|
||||
* v1.1.0 (unreleased)
|
||||
- New drop indicator style type: Segmented Indicators
|
||||
- Windows: Drop Shadow for floating windows
|
||||
- Added AutoHide / SideBar support
|
||||
- Added FocusScope support
|
||||
- Added DockWidget::isFocused() and DockWidgetBase::isFocusedChanged()
|
||||
- Added Config::Flag_AlwaysTitleBarWhenFloating, which complements Flag_HideTitleBarWhenTabsVisible
|
||||
- Added Config::Flag_DontUseUtilityWindowsForFloating
|
||||
- Added Config::Flag_TitleBarHasMinimizeButton
|
||||
- Added Config::Flag_TitleBarNoFloatButton
|
||||
- Added Config::Flag_AutoHideSupport
|
||||
- Bugfixes:
|
||||
- Windows: Fixed windows not having proper minimum size.
|
||||
- Windows: Fixed moving windows across screens with different DPI (#72)
|
||||
- Don't center floating windows if the user set a custom position (#75)
|
||||
- Fixed floating window's title not being correct (#74)
|
||||
- Fixed focus scope not reacting when clicking on current tab (#71)
|
||||
|
||||
* Roadmap
|
||||
- QtQuick support
|
||||
- Fixed floating window borders not being rendered correctly on HDPI due to rounding errors.
|
||||
|
||||
* v1.0.1 (unreleased)
|
||||
- cmake/Python - don't require pkg-config, only use if available (#68)
|
||||
|
||||
15
README.md
@@ -64,8 +64,7 @@ Features
|
||||
|
||||
Roadmap
|
||||
========
|
||||
- QtQuick support for v1.1
|
||||
- "Minimization bar" for v1.2
|
||||
- QtQuick support
|
||||
|
||||
Trying out the examples
|
||||
=======================
|
||||
@@ -82,15 +81,15 @@ Build and install the KDDockWidgets framework:
|
||||
|
||||
```
|
||||
$ cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/path/where/to/install ../path/to/kddockwidgets
|
||||
$ make
|
||||
$ make install
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
```
|
||||
|
||||
Now build and run the example:
|
||||
```
|
||||
$ cd path/to/kddockwidgets/examples/dockwidgets/
|
||||
$ cmake -DCMAKE_PREFIX_PATH=/path/where/to/install
|
||||
$ make
|
||||
$ cmake -G Ninja -DCMAKE_PREFIX_PATH=/path/where/to/install
|
||||
$ cmake --build .
|
||||
$ ./kddockwidgets_example
|
||||
|
||||
```
|
||||
@@ -115,13 +114,13 @@ For more info visit https://doc.qt.io/qtforpython/shiboken2/gettingstarted.html
|
||||
Once QtForPython is installed you are ready to generate the PySide bindings
|
||||
for KDDockwWidgets.
|
||||
|
||||
Next pass `-DOPTION_BUILD_PYTHON_BINDINGS=ON` to CMake, followed by the
|
||||
Next pass `-DKDDockWidgets_PYTHON_BINDINGS=ON` to CMake, followed by the
|
||||
make command.
|
||||
|
||||
The bindings will be installed to the passed `-DCMAKE_INSTALL_PREFIX`, which
|
||||
might require setting the `PYTHONPATH` env variable to point to that path when
|
||||
running applications. Alternatively, configure the bindings install location
|
||||
by passing `-DPYTHON_BINDINGS_INSTALL_PREFIX=/usr/lib/python3.8/site-packages`
|
||||
by passing `-DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=/usr/lib/python3.8/site-packages`
|
||||
to CMake (adjust to the python path on your system).
|
||||
|
||||
To run the KDDW python example
|
||||
|
||||
@@ -115,7 +115,6 @@ else()
|
||||
set(SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_CUSTOM_PATH})
|
||||
list(APPEND SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_BASEDIR})
|
||||
list(APPEND SHIBOKEN_SEARCH_PATHS ${SHIBOKEN_GENERATOR_BASEDIR})
|
||||
message(STATUS "BOO: ${SHIBOKEN_SEARCH_PATHS}")
|
||||
find_file(SHIBOKEN_LIBRARY
|
||||
${SHIBOKEN_LIBRARY_BASENAMES}
|
||||
PATHS ${SHIBOKEN_SEARCH_PATHS}
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
# Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
#
|
||||
|
||||
if (NOT PYTHON_BINDINGS_INSTALL_PREFIX)
|
||||
SET(PYTHON_BINDINGS_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH "Custom path to install python bindings.")
|
||||
if (NOT ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX)
|
||||
SET(${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH "Custom path to install python bindings.")
|
||||
endif()
|
||||
|
||||
message(STATUS "PYTHON INSTALL PREFIX ${PYTHON_BINDINGS_INSTALL_PREFIX}")
|
||||
message(STATUS "PYTHON INSTALL PREFIX ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}")
|
||||
|
||||
if (WIN32)
|
||||
set(PATH_SEP "\;")
|
||||
@@ -153,5 +153,5 @@ macro(CREATE_PYTHON_BINDINGS
|
||||
LINK_FLAGS "-undefined dynamic_lookup")
|
||||
endif()
|
||||
install(TARGETS ${TARGET_NAME}
|
||||
LIBRARY DESTINATION ${PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
|
||||
LIBRARY DESTINATION ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
|
||||
endmacro()
|
||||
|
||||
@@ -11,7 +11,7 @@ from conans import ConanFile, CMake, tools
|
||||
|
||||
class KDDockWidgetsConan(ConanFile):
|
||||
name = "kddockwidgets"
|
||||
version = "1.0.0"
|
||||
version = "1.1.95"
|
||||
default_user = "kdab"
|
||||
default_channel = "stable"
|
||||
license = ("https://raw.githubusercontent.com/KDAB/KDDockWidgets/master/LICENSES/GPL-2.0-only.txt",
|
||||
@@ -51,7 +51,7 @@ class KDDockWidgetsConan(ConanFile):
|
||||
self.cmake.definitions["KDDockWidgets_STATIC"] = self.options.build_static
|
||||
self.cmake.definitions["KDDockWidgets_EXAMPLES"] = self.options.build_examples
|
||||
self.cmake.definitions["KDDockWidgets_TESTS"] = self.options.build_tests
|
||||
self.cmake.definitions["OPTION_BUILD_PYTHON_BINDINGS"] = self.options.build_python_bindings
|
||||
self.cmake.definitions["KDDockWidgets_PYTHON_BINDINGS"] = self.options.build_python_bindings
|
||||
self.cmake.configure()
|
||||
self.cmake.build()
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ find_package(Doxygen)
|
||||
set_package_properties(Doxygen PROPERTIES
|
||||
TYPE OPTIONAL
|
||||
DESCRIPTION "API Documentation system"
|
||||
URL "http://www.doxygen.org"
|
||||
URL "https://www.doxygen.org"
|
||||
PURPOSE "Needed to build the API documentation."
|
||||
)
|
||||
if(DOXYGEN_DOT_EXECUTABLE)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<img src="kdab-logo-16x16.png">
|
||||
<font style="font-weight: bold;">Klarälvdalens Datakonsult AB (KDAB)</font>
|
||||
<br>
|
||||
"The Qt Experts"<br>
|
||||
"The Qt, C++ and OpenGL Experts"<br>
|
||||
<a href="https://www.kdab.com/">https://www.kdab.com/</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
init();
|
||||
}
|
||||
|
||||
~MyTitleBar() override;
|
||||
|
||||
void init()
|
||||
{
|
||||
setFixedHeight(60);
|
||||
@@ -56,6 +58,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
MyTitleBar::~MyTitleBar() = default;
|
||||
|
||||
// Inheriting from SeparatorWidget instead of Separator as it handles moving and mouse cursor changing
|
||||
class MySeparator : public Layouting::SeparatorWidget
|
||||
{
|
||||
@@ -65,6 +69,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~MySeparator() override;
|
||||
|
||||
void paintEvent(QPaintEvent *) override
|
||||
{
|
||||
QPainter p(this);
|
||||
@@ -72,6 +78,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
MySeparator::~MySeparator() = default;
|
||||
|
||||
KDDockWidgets::TitleBar * CustomWidgetFactory::createTitleBar(KDDockWidgets::Frame *frame) const
|
||||
{
|
||||
return new MyTitleBar(frame);
|
||||
|
||||
@@ -37,6 +37,7 @@ int main(int argc, char **argv)
|
||||
parser.setApplicationDescription("KDDockWidgets example application");
|
||||
parser.addHelpOption();
|
||||
|
||||
// Fusion looks better in general, but feel free to change
|
||||
qApp->setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
|
||||
|
||||
QCommandLineOption customStyle("p", QCoreApplication::translate("main", "Shows how to style framework internals via FrameworkWidgetFactory"));
|
||||
@@ -102,6 +103,9 @@ int main(int argc, char **argv)
|
||||
|
||||
QCommandLineOption centralFrame("f", QCoreApplication::translate("main", "Persistent central frame"));
|
||||
|
||||
QCommandLineOption autoHideSupport("w", QCoreApplication::translate("main", "Enables auto-hide/minimization to side-bar support"));
|
||||
parser.addOption(autoHideSupport);
|
||||
|
||||
#if defined(DOCKS_DEVELOPER_MODE)
|
||||
parser.addOption(centralFrame);
|
||||
|
||||
@@ -149,6 +153,9 @@ int main(int argc, char **argv)
|
||||
|
||||
#endif
|
||||
|
||||
if (parser.isSet(autoHideSupport))
|
||||
flags |= Config::Flag_AutoHideSupport;
|
||||
|
||||
if (parser.isSet(noTitleBars))
|
||||
flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <kddockwidgets/DockWidget.h>
|
||||
#include <kddockwidgets/MainWindow.h>
|
||||
|
||||
#include <QStyleFactory>
|
||||
|
||||
// clazy:excludeall=qstring-allocations
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
@@ -27,6 +29,9 @@ int main(int argc, char **argv)
|
||||
app.setOrganizationName(QStringLiteral("KDAB"));
|
||||
app.setApplicationName(QStringLiteral("Test app"));
|
||||
|
||||
// Fusion looks better in general, but feel free to change
|
||||
qApp->setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
|
||||
|
||||
// # 1. Create our main window
|
||||
|
||||
KDDockWidgets::MainWindow mainWindow(QStringLiteral("MyMainWindow"));
|
||||
|
||||
@@ -22,12 +22,15 @@ int main(int argc, char *argv[])
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQuickView view;
|
||||
view.setObjectName("MainWindow QQuickView");
|
||||
KDDockWidgets::Config::self().setQmlEngine(view.engine());
|
||||
view.resize(1000, 800);
|
||||
view.show();
|
||||
view.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
|
||||
auto dw1 = new KDDockWidgets::DockWidgetQuick("Dock #1");
|
||||
view.setSource(QUrl("qrc:/main.qml"));
|
||||
|
||||
dw1->setWidget(QStringLiteral("qrc:/Guest1.qml"));
|
||||
dw1->resize(QSize(800, 800));
|
||||
dw1->show();
|
||||
@@ -42,8 +45,6 @@ int main(int argc, char *argv[])
|
||||
dw3->resize(QSize(800, 800));
|
||||
dw3->show();
|
||||
|
||||
view.setSource(QUrl("qrc:/main.qml"));
|
||||
|
||||
dw1->addDockWidgetToContainingWindow(dw3, KDDockWidgets::Location_OnRight);
|
||||
|
||||
KDDockWidgets::MainWindowBase *mainWindow = KDDockWidgets::DockRegistry::self()->mainwindows().constFirst();
|
||||
|
||||
@@ -78,4 +78,4 @@ create_python_bindings(
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py)
|
||||
|
||||
# install
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<!-- this is used in a public virtual pure function we need to declare it
|
||||
otherwise shiboken will ignore the function and will fail to create a wrapper -->
|
||||
<primitive-type name="DropAreaWithCentralFrame"/>
|
||||
<primitive-type name="SideBar"/>
|
||||
|
||||
<!-- Some plublic enum and flags -->
|
||||
<enum-type name="Location"/>
|
||||
@@ -19,6 +20,9 @@
|
||||
<enum-type name="RestoreOption" flags="RestoreOptions"/>
|
||||
<enum-type name="DefaultSizeMode"/>
|
||||
<enum-type name="FrameOption" flags="FrameOptions"/>
|
||||
<enum-type name="DropIndicatorType"/>
|
||||
<enum-type name="SideBarLocation"/>
|
||||
<enum-type name="TitleBarButtonType"/>
|
||||
|
||||
<!-- our classes
|
||||
For class we can use two types:
|
||||
@@ -32,6 +36,7 @@
|
||||
<!-- this class contains a internal enum, so it should be declared
|
||||
inside of the object-type -->
|
||||
<enum-type name="Option" flags="Options" />
|
||||
<enum-type name="IconPlace" flags="IconPlaces" />
|
||||
</object-type>
|
||||
|
||||
<object-type name="DockWidget" />
|
||||
|
||||
@@ -37,6 +37,7 @@ set(DOCKSLIBS_SRCS
|
||||
private/FloatingWindow.cpp
|
||||
private/Logging.cpp
|
||||
private/TitleBar.cpp
|
||||
private/SideBar.cpp
|
||||
private/DockRegistry.cpp
|
||||
private/Draggable.cpp
|
||||
private/WindowBeingDragged.cpp
|
||||
@@ -61,16 +62,20 @@ set(DOCKS_INSTALLABLE_INCLUDES
|
||||
)
|
||||
|
||||
set(DOCKS_INSTALLABLE_PRIVATE_INCLUDES
|
||||
private/DragController_p.h
|
||||
private/Draggable_p.h
|
||||
private/DropIndicatorOverlayInterface_p.h
|
||||
private/FloatingWindow_p.h
|
||||
private/Frame_p.h
|
||||
private/SideBar_p.h
|
||||
private/TitleBar_p.h
|
||||
private/WindowBeingDragged_p.h
|
||||
)
|
||||
|
||||
set(DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES
|
||||
private/widgets/QWidgetAdapter_widgets_p.h
|
||||
private/widgets/TitleBarWidget_p.h
|
||||
private/widgets/SideBarWidget_p.h
|
||||
private/widgets/FloatingWindowWidget_p.h
|
||||
private/widgets/FrameWidget_p.h
|
||||
private/widgets/TabBarWidget_p.h
|
||||
@@ -78,7 +83,7 @@ set(DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES
|
||||
private/widgets/TabWidget_p.h
|
||||
)
|
||||
|
||||
if(OPTION_QTQUICK)
|
||||
if(${PROJECT_NAME}_QTQUICK)
|
||||
set(DOCKSLIBS_SRCS ${DOCKSLIBS_SRCS}
|
||||
private/quick/DockWidgetQuick.cpp
|
||||
private/quick/QWidgetAdapter_quick.cpp
|
||||
@@ -99,6 +104,7 @@ else()
|
||||
private/widgets/TabBarWidget.cpp
|
||||
private/widgets/FloatingWindowWidget.cpp
|
||||
private/widgets/FrameWidget.cpp
|
||||
private/widgets/SideBarWidget.cpp
|
||||
private/widgets/TabWidgetWidget.cpp
|
||||
private/widgets/TitleBarWidget.cpp
|
||||
private/widgets/DockWidget.cpp
|
||||
@@ -161,15 +167,17 @@ if(CMAKE_COMPILER_IS_GNUCXX OR IS_CLANG_BUILD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(OPTION_QTQUICK)
|
||||
if(${PROJECT_NAME}_QTQUICK)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::Widgets Qt5::Quick Qt5::QuickControls2 kddockwidgets_multisplitter PRIVATE Qt5::GuiPrivate)
|
||||
else()
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::Widgets kddockwidgets_multisplitter PRIVATE Qt5::GuiPrivate)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
find_package(Qt5X11Extras)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::X11Extras)
|
||||
if (WIN32)
|
||||
target_link_libraries(kddockwidgets PRIVATE Dwmapi)
|
||||
elseif(NOT APPLE)
|
||||
find_package(Qt5X11Extras)
|
||||
target_link_libraries(kddockwidgets PUBLIC Qt5::X11Extras)
|
||||
endif()
|
||||
|
||||
set_target_properties(kddockwidgets PROPERTIES VERSION ${${PROJECT_NAME}_SOVERSION})
|
||||
@@ -224,11 +232,11 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfig.cmake"
|
||||
DESTINATION ${INSTALL_LIBRARY_DIR}/cmake/KDDockWidgets
|
||||
)
|
||||
|
||||
if(OPTION_DEVELOPER_MODE)
|
||||
if(${PROJECT_NAME}_DEVELOPER_MODE)
|
||||
# Under developer mode since kddw might be a sub-folder of a project setting a different value for QT_DISABLE_DEPRECATED_BEFORE
|
||||
target_compile_definitions(kddockwidgets PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x060000)
|
||||
|
||||
if (NOT OPTION_QTQUICK) # TODO: We can support it
|
||||
if (NOT ${PROJECT_NAME}_QTQUICK) # TODO: We can support it
|
||||
add_executable(kddockwidgets_linter layoutlinter_main.cpp)
|
||||
target_link_libraries(kddockwidgets_linter kddockwidgets kddockwidgets_multisplitter Qt5::Widgets)
|
||||
endif()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "multisplitter/Widget_qwidget.h"
|
||||
#include "DockRegistry_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "Utils_p.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
@@ -83,8 +84,11 @@ Config::Flags Config::flags() const
|
||||
|
||||
void Config::setFlags(Flags f)
|
||||
{
|
||||
if (!DockRegistry::self()->isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "Only use this function at startup before creating any DockWidget or MainWindow";
|
||||
auto dr = DockRegistry::self();
|
||||
if (!dr->isEmpty(/*excludeBeingDeleted=*/ true)) {
|
||||
qWarning() << Q_FUNC_INFO << "Only use this function at startup before creating any DockWidget or MainWindow"
|
||||
<< "; These are already created: " << dr->mainWindowsNames()
|
||||
<< dr->dockWidgetNames() << dr->floatingWindows();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,7 +139,7 @@ int Config::separatorThickness() const
|
||||
|
||||
void Config::setSeparatorThickness(int value)
|
||||
{
|
||||
if (!DockRegistry::self()->isEmpty()) {
|
||||
if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/ true)) {
|
||||
qWarning() << Q_FUNC_INFO << "Only use this function at startup before creating any DockWidget or MainWindow";
|
||||
return;
|
||||
}
|
||||
@@ -174,6 +178,9 @@ void Config::Private::fixFlags()
|
||||
if (QOperatingSystemVersion::current().majorVersion() < 10) {
|
||||
// Aero-snap requires Windows 10
|
||||
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
|
||||
} else {
|
||||
// Unconditional now
|
||||
m_flags |= Flag_AeroSnapWithClientDecos;
|
||||
}
|
||||
|
||||
// These are mutually exclusive:
|
||||
@@ -185,10 +192,18 @@ void Config::Private::fixFlags()
|
||||
// Not supported on macOS:
|
||||
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
|
||||
#else
|
||||
// Not supported on linux.
|
||||
// On Linux, dragging the title bar of a window doesn't generate NonClientMouseEvents
|
||||
m_flags = m_flags & ~Flag_NativeTitleBar;
|
||||
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
|
||||
if (KDDockWidgets::isWayland()) {
|
||||
// Native title bar is forced on Wayland. Needed for moving the window.
|
||||
// The inner KDDW title bar is used for DnD.
|
||||
m_flags |= Flag_NativeTitleBar;
|
||||
} else {
|
||||
// Not supported on linux/X11
|
||||
// On Linux, dragging the title bar of a window doesn't generate NonClientMouseEvents
|
||||
// at least with KWin anyway. We can make this more granular and allow it for other
|
||||
// X11 window managers
|
||||
m_flags = m_flags & ~Flag_NativeTitleBar;
|
||||
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
enum Flag {
|
||||
Flag_None = 0, ///> No option set
|
||||
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_AeroSnapWithClientDecos = 2, ///> Deprecated. This is now default and cannot be turned off. Moving a window on Windows 10 uses native moving, as that works well across screens with different HDPI settings. There's no reason to use manual client/Qt window moving.
|
||||
Flag_AlwaysTitleBarWhenFloating = 4, ///> Floating windows will have a title bar even if Flag_HideTitleBarWhenTabsVisible is specified. Unneeded if Flag_HideTitleBarWhenTabsVisible isn't specified, as that's the default already.
|
||||
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,
|
||||
@@ -71,7 +71,10 @@ public:
|
||||
Flag_internal_DontUseParentForFloatingWindows = 0x1000, ///> FloatingWindows won't have a parent top-level. Internal, use Flag_DontUseUtilityFloatingWindows instead.
|
||||
|
||||
Flag_DontUseUtilityFloatingWindows = Flag_internal_DontUseQtToolWindowsForFloatingWindows | Flag_internal_DontUseParentForFloatingWindows,
|
||||
Flag_TitleBarHasMinimizeButton = 0x2000 | Flag_DontUseUtilityFloatingWindows, ///> The title bar will have a minimize button when floating. This implies Flag_DontUseUtilityFloatingWindows too, otherwise they wouldn't appear in the task bar.
|
||||
Flag_TitleBarHasMinimizeButton = 0x2000 | Flag_DontUseUtilityFloatingWindows, ///> The title bar will have a minimize button when floating. This implies Flag_DontUseUtilityFloatingWindows too, otherwise they wouldn't appear in the task bar.
|
||||
Flag_TitleBarNoFloatButton = 0x4000, ///> The TitleBar won't show the float button
|
||||
Flag_AutoHideSupport = 0x8000 | Flag_TitleBarNoFloatButton, ///> Supports minimizing dock widgets to the side-bar.
|
||||
///> By default it also turns off the float button, but you can remove Flag_TitleBarNoFloatButton to have both.
|
||||
Flag_Default = Flag_AeroSnapWithClientDecos ///> The defaults
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "private/Position_p.h"
|
||||
#include "WindowBeingDragged_p.h"
|
||||
#include "SideBar_p.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QEvent>
|
||||
@@ -57,10 +58,12 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
q->connect(floatAction, &QAction::toggled, q, [this] (bool enabled) {
|
||||
q->connect(floatAction, &QAction::toggled, q, [this] (bool checked) {
|
||||
if (!m_updatingFloatAction) { // guard against recursiveness
|
||||
q->setFloating(enabled);
|
||||
q->setFloating(checked);
|
||||
}
|
||||
|
||||
Q_EMIT q->isFloatingChanged(checked);
|
||||
});
|
||||
|
||||
toggleAction->setCheckable(true);
|
||||
@@ -107,7 +110,7 @@ public:
|
||||
void onDockWidgetHidden();
|
||||
void show();
|
||||
void close();
|
||||
void restoreToPreviousPosition();
|
||||
bool restoreToPreviousPosition();
|
||||
void maybeRestoreToPreviousPosition();
|
||||
int currentTabIndex() const;
|
||||
|
||||
@@ -264,15 +267,12 @@ bool DockWidgetBase::isFloating() const
|
||||
return fw && fw->hasSingleDockWidget();
|
||||
}
|
||||
|
||||
void DockWidgetBase::setFloating(bool floats)
|
||||
bool DockWidgetBase::setFloating(bool floats)
|
||||
{
|
||||
const bool alreadyFloating = isFloating();
|
||||
|
||||
qCDebug(docking) << Q_FUNC_INFO << "yes=" << floats
|
||||
<< "; already floating=" << alreadyFloating;
|
||||
|
||||
if ((floats && alreadyFloating) || (!floats && !alreadyFloating))
|
||||
return; // Nothing to do
|
||||
return true; // Nothing to do
|
||||
|
||||
if (floats) {
|
||||
d->saveTabIndex();
|
||||
@@ -282,6 +282,7 @@ void DockWidgetBase::setFloating(bool floats)
|
||||
qWarning() << "DockWidget::setFloating: Tabbed but no frame exists"
|
||||
<< this;
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
frame->detachTab(this);
|
||||
@@ -294,9 +295,10 @@ void DockWidgetBase::setFloating(bool floats)
|
||||
if (auto fw = floatingWindow())
|
||||
fw->setSuggestedGeometry(lastGeo, /*preserveCenter=*/true);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
saveLastFloatingGeometry();
|
||||
d->restoreToPreviousPosition();
|
||||
return d->restoreToPreviousPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,6 +491,30 @@ void DockWidgetBase::setAffinities(const QStringList &affinityNames)
|
||||
d->affinities = affinities;
|
||||
}
|
||||
|
||||
void DockWidgetBase::moveToSideBar()
|
||||
{
|
||||
if (MainWindowBase *m = mainWindow())
|
||||
m->moveToSideBar(this);
|
||||
}
|
||||
|
||||
bool DockWidgetBase::isOverlayed() const
|
||||
{
|
||||
if (MainWindowBase *m = mainWindow())
|
||||
return m->overlayedDockWidget() == this;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SideBarLocation DockWidgetBase::sideBarLocation() const
|
||||
{
|
||||
return DockRegistry::self()->sideBarLocationForDockWidget(this);
|
||||
}
|
||||
|
||||
bool DockWidgetBase::hasPreviousDockedLocation() const
|
||||
{
|
||||
return d->m_lastPositions.isValid();
|
||||
}
|
||||
|
||||
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
|
||||
{
|
||||
qCDebug(creation) << "DockWidget::morphIntoFloatingWindow() this=" << this
|
||||
@@ -651,19 +677,24 @@ void DockWidgetBase::Private::close()
|
||||
if (Frame *frame = q->frame()) {
|
||||
frame->removeWidget(q);
|
||||
q->setParent(nullptr);
|
||||
|
||||
if (SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
|
||||
sb->removeDockWidget(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::restoreToPreviousPosition()
|
||||
bool DockWidgetBase::Private::restoreToPreviousPosition()
|
||||
{
|
||||
if (!m_lastPositions.isValid())
|
||||
return;
|
||||
return false;
|
||||
|
||||
Layouting::Item *item = m_lastPositions.lastItem();
|
||||
|
||||
MultiSplitter *layout = DockRegistry::self()->layoutForItem(item);
|
||||
Q_ASSERT(layout);
|
||||
layout->restorePlaceholder(q, item, m_lastPositions.lastTabIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
|
||||
@@ -755,7 +786,7 @@ void DockWidgetBase::onHidden(bool spontaneous)
|
||||
}
|
||||
}
|
||||
|
||||
void DockWidgetBase::onClosed(QCloseEvent *e)
|
||||
void DockWidgetBase::onCloseEvent(QCloseEvent *e)
|
||||
{
|
||||
e->accept(); // By default we accept, means DockWidget closes
|
||||
if (d->widget)
|
||||
|
||||
@@ -61,6 +61,8 @@ class DOCKS_EXPORT DockWidgetBase : public QWidget
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isFocused READ isFocused NOTIFY isFocusedChanged)
|
||||
Q_PROPERTY(bool isFloating READ isFloating WRITE setFloating NOTIFY isFloatingChanged)
|
||||
public:
|
||||
typedef QVector<DockWidgetBase *> List;
|
||||
|
||||
@@ -137,7 +139,7 @@ public:
|
||||
* call setWidget(A) followed by setWidget(B) then A will have to be deleted by you, while B is
|
||||
* owned by the dock widget.
|
||||
*/
|
||||
void setWidget(QWidgetOrQuick *widget);
|
||||
virtual void setWidget(QWidgetOrQuick *widget);
|
||||
|
||||
/**
|
||||
* @brief returns the widget which this dock widget hosts
|
||||
@@ -156,8 +158,10 @@ public:
|
||||
/**
|
||||
* @brief setter to make the dock widget float or dock.
|
||||
* @param floats If true makes the dock widget float, otherwise docks it.
|
||||
*
|
||||
* Returns true if the request was accomplished
|
||||
*/
|
||||
void setFloating(bool floats);
|
||||
bool setFloating(bool floats);
|
||||
|
||||
/**
|
||||
* @brief Returns the QAction that allows to hide/show the dock widget
|
||||
@@ -338,6 +342,32 @@ public:
|
||||
///@sa isFocusedChanged()
|
||||
bool isFocused() const;
|
||||
|
||||
/**
|
||||
* @brief Minimizes this dock widget to the MainWindow's side-bar.
|
||||
*
|
||||
* It will be undocked from current layout. It's previous docked position will be remembered.
|
||||
*
|
||||
* This action is only available if the dock widget is docked into a MainWindow.
|
||||
* The dockwidget will initially be visible and overlayed on top of the current layout (this is
|
||||
* the auto-hide feature).
|
||||
*/
|
||||
void moveToSideBar();
|
||||
|
||||
/// @brief Returns whether this dock widget is overlayed on top of the main window, instead of
|
||||
/// docked into the layout. This is only relevant when using the auto-hide and side-bar feature.
|
||||
bool isOverlayed() const;
|
||||
|
||||
///@brief Returns whether this dock widget is in a side bar, and which.
|
||||
/// SideBarLocation::None is returned if it's not in a sidebar.
|
||||
/// This is only relevant when using the auto-hide and side-bar feature.
|
||||
SideBarLocation sideBarLocation() const;
|
||||
|
||||
/// @brief Returns whether this floating dock widget knows its previous docked location
|
||||
/// Result only makes sense if it's floating.
|
||||
///
|
||||
/// When you call dockWidget->setFloating(false) it will only dock if it knows where to.
|
||||
bool hasPreviousDockedLocation() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
///@brief signal emitted when the parent changed
|
||||
void parentChanged();
|
||||
@@ -366,11 +396,24 @@ Q_SIGNALS:
|
||||
///@sa isFocused
|
||||
void isFocusedChanged(bool);
|
||||
|
||||
///@brief emitted when isOverlayed changes
|
||||
///@sa isOverlayed
|
||||
void isOverlayedChanged(bool);
|
||||
|
||||
///@brief emitted when isFloating changes
|
||||
bool isFloatingChanged(bool);
|
||||
|
||||
///@brief emitted when this dock widget is removed from a side-bar.
|
||||
///Only relevant for the auto-hide/sidebar feature
|
||||
void removedFromSideBar();
|
||||
|
||||
protected:
|
||||
void onParentChanged();
|
||||
void onShown(bool spontaneous);
|
||||
void onHidden(bool spontaneous);
|
||||
void onClosed(QCloseEvent *e);
|
||||
#ifndef PYTHON_BINDINGS //Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327
|
||||
void onCloseEvent(QCloseEvent *e) override;
|
||||
#endif
|
||||
|
||||
#if defined(DOCKS_DEVELOPER_MODE)
|
||||
public Q_SLOTS:
|
||||
@@ -403,6 +446,7 @@ private:
|
||||
friend class KDDockWidgets::DragController;
|
||||
friend class KDDockWidgets::DockRegistry;
|
||||
friend class KDDockWidgets::LayoutSaver;
|
||||
friend class KDDockWidgets::MainWindowBase;
|
||||
|
||||
/**
|
||||
* @brief the Frame which contains this dock widgets.
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
m_inCtor = false;
|
||||
}
|
||||
|
||||
~Private() override;
|
||||
|
||||
void setIsFocused(bool);
|
||||
void onFocusObjectChanged(QObject *);
|
||||
bool isInFocusScope(WidgetType *) const;
|
||||
@@ -51,6 +53,9 @@ public:
|
||||
QPointer<WidgetType> m_lastFocusedInScope;
|
||||
};
|
||||
|
||||
FocusScope::Private::~Private()
|
||||
{
|
||||
}
|
||||
|
||||
FocusScope::FocusScope(QWidgetAdapter *thisWidget)
|
||||
: d(new Private(this, thisWidget))
|
||||
|
||||
@@ -30,7 +30,7 @@ class FocusScope
|
||||
public:
|
||||
///@brief constructor
|
||||
explicit FocusScope(QWidgetAdapter *thisWidget);
|
||||
~FocusScope();
|
||||
virtual ~FocusScope();
|
||||
|
||||
///@brief Returns true if this FocusScope is focused.
|
||||
///This is similar to the QWidget::hasFocus(), except that it counts with the children being focused too.
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
# include "widgets/FrameWidget_p.h"
|
||||
# include "widgets/TitleBarWidget_p.h"
|
||||
# include "widgets/TabBarWidget_p.h"
|
||||
# include "widgets/SideBarWidget_p.h"
|
||||
# include "widgets/TabWidgetWidget_p.h"
|
||||
# include "multisplitter/Separator_qwidget.h"
|
||||
# include "widgets/FloatingWindowWidget_p.h"
|
||||
# include "indicators/SegmentedIndicators_p.h"
|
||||
|
||||
# include <QRubberBand>
|
||||
# include <QToolButton>
|
||||
#else
|
||||
# include "quick/FrameQuick_p.h"
|
||||
# include "quick/DockWidgetQuick.h"
|
||||
@@ -102,6 +105,24 @@ QWidgetOrQuick *DefaultWidgetFactory::createRubberBand(QWidgetOrQuick *parent) c
|
||||
return new QRubberBand(QRubberBand::Rectangle, parent);
|
||||
}
|
||||
|
||||
SideBar *DefaultWidgetFactory::createSideBar(SideBarLocation loc, MainWindowBase *parent) const
|
||||
{
|
||||
return new SideBarWidget(loc, parent);
|
||||
}
|
||||
|
||||
QAbstractButton* DefaultWidgetFactory::createTitleBarButton(QWidget *parent, TitleBarButtonType type) const
|
||||
{
|
||||
if (!parent) {
|
||||
qWarning() << Q_FUNC_INFO << "Parent not provided";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto button = new Button(parent);
|
||||
button->setIcon(iconForButtonType(type, parent->devicePixelRatioF()));
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Frame *DefaultWidgetFactory::createFrame(QWidgetOrQuick *parent, FrameOptions options) const
|
||||
@@ -163,4 +184,69 @@ QWidgetOrQuick *DefaultWidgetFactory::createRubberBand(QWidgetOrQuick *parent) c
|
||||
return new QWidgetOrQuick(parent);
|
||||
}
|
||||
|
||||
SideBar *DefaultWidgetFactory::createSideBar(SideBarLocation loc, MainWindowBase *parent) const
|
||||
{
|
||||
Q_UNUSED(loc);
|
||||
Q_UNUSED(parent);
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "Not implemented yet";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // QtQuick
|
||||
|
||||
// iconForButtonType impl is the same for QtQuick and QtWidgets
|
||||
QIcon DefaultWidgetFactory::iconForButtonType(TitleBarButtonType type, qreal dpr) const
|
||||
{
|
||||
QString iconName;
|
||||
switch (type) {
|
||||
case TitleBarButtonType::AutoHide:
|
||||
iconName = QStringLiteral("auto-hide");
|
||||
break;
|
||||
case TitleBarButtonType::UnautoHide:
|
||||
iconName = QStringLiteral("unauto-hide");
|
||||
break;
|
||||
case TitleBarButtonType::Close:
|
||||
iconName = QStringLiteral("close");
|
||||
break;
|
||||
case TitleBarButtonType::Minimize:
|
||||
iconName = QStringLiteral("min");
|
||||
break;
|
||||
case TitleBarButtonType::Maximize:
|
||||
iconName = QStringLiteral("max");
|
||||
break;
|
||||
case TitleBarButtonType::Normal:
|
||||
// We're using the same icon as dock/float
|
||||
iconName = QStringLiteral("dock-float");
|
||||
break;
|
||||
case TitleBarButtonType::Float:
|
||||
iconName = QStringLiteral("dock-float");
|
||||
break;
|
||||
}
|
||||
|
||||
if (iconName.isEmpty())
|
||||
return {};
|
||||
|
||||
QIcon icon(QStringLiteral(":/img/%1.png").arg(iconName));
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
|
||||
const bool isFractional = int(dpr) != dpr;
|
||||
if (isFractional) {
|
||||
// We don't support 1.5x yet.
|
||||
// Linux is the only one affected as Windows and macOS use integral factors.
|
||||
// Problem with Linux is that rendering is off due to a rounding bug only fixed in 5.15.2
|
||||
// Will enable for fractional later.
|
||||
// QTBUG-86170
|
||||
return icon;
|
||||
}
|
||||
#else
|
||||
// Not using Qt's sugar syntax, which doesn't support 1.5x anyway when we need it.
|
||||
// Simply add the high-res files and Qt will pick them when needed
|
||||
|
||||
icon.addFile(QStringLiteral(":/img/%1-1.5x.png").arg(iconName));
|
||||
Q_UNUSED(dpr);
|
||||
#endif
|
||||
icon.addFile(QStringLiteral(":/img/%1-2x.png").arg(iconName));
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
* @author Sérgio Martins \<sergio.martins@kdab.com\>
|
||||
*/
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Layouting {
|
||||
class Separator;
|
||||
class Widget;
|
||||
@@ -37,6 +41,7 @@ class TabWidget;
|
||||
class TitleBar;
|
||||
class Frame;
|
||||
class DropArea;
|
||||
class SideBar;
|
||||
class TabBar;
|
||||
|
||||
/**
|
||||
@@ -121,6 +126,23 @@ public:
|
||||
///@brief Called internally by the framework to create a RubberBand to show as drop zone
|
||||
///Returns a rubber band
|
||||
virtual QWidgetOrQuick *createRubberBand(QWidgetOrQuick *parent) const = 0;
|
||||
|
||||
///@brief Called internally by the framework to create a SideBar
|
||||
///@param loc The side-bar location without the main window. Just forward into your SideBar sub-class ctor.
|
||||
///@param parent The MainWindow. Just forward into your SideBar sub-class ctor.
|
||||
virtual SideBar *createSideBar(SideBarLocation loc, MainWindowBase *parent) const = 0;
|
||||
|
||||
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
||||
///@brief Called internally by the framework to create a title bar button
|
||||
///@parent the button's parent
|
||||
virtual QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const = 0;
|
||||
#else
|
||||
// QtQuick will have some other base class for buttons
|
||||
#endif
|
||||
|
||||
/// @brief Returns the icon to be used with the specified @p type
|
||||
/// @param dpr the device pixel ratio of the button
|
||||
virtual QIcon iconForButtonType(TitleBarButtonType type, qreal dpr) const = 0;
|
||||
private:
|
||||
Q_DISABLE_COPY(FrameworkWidgetFactory)
|
||||
};
|
||||
@@ -142,6 +164,13 @@ public:
|
||||
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
|
||||
DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override;
|
||||
QWidgetOrQuick *createRubberBand(QWidgetOrQuick *parent) const override;
|
||||
SideBar *createSideBar(SideBarLocation loc, MainWindowBase *parent) const override;
|
||||
|
||||
#ifdef KDDOCKWIDGETS_QTWIDGETS
|
||||
QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const override;
|
||||
#endif
|
||||
|
||||
QIcon iconForButtonType(TitleBarButtonType type, qreal dpr) const override;
|
||||
|
||||
static DropIndicatorType s_dropIndicatorType;
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,8 @@ namespace KDDockWidgets
|
||||
enum FrameOption {
|
||||
FrameOption_None = 0,
|
||||
FrameOption_AlwaysShowsTabs = 1,
|
||||
FrameOption_IsCentralFrame = 2
|
||||
FrameOption_IsCentralFrame = 2,
|
||||
FrameOption_IsOverlayed = 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
|
||||
|
||||
@@ -96,6 +97,32 @@ namespace KDDockWidgets
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
/// @brief Each main window supports 4 sidebars
|
||||
enum class SideBarLocation {
|
||||
None,
|
||||
North,
|
||||
East,
|
||||
West,
|
||||
South
|
||||
};
|
||||
|
||||
///@brief describes a type of button you can have in the title bar
|
||||
enum class TitleBarButtonType {
|
||||
Close,
|
||||
Float,
|
||||
Minimize,
|
||||
Maximize,
|
||||
Normal, // Restore from maximized state
|
||||
AutoHide,
|
||||
UnautoHide
|
||||
};
|
||||
|
||||
///@internal
|
||||
inline uint qHash(SideBarLocation loc, uint seed)
|
||||
{
|
||||
return ::qHash(static_cast<uint>(loc), seed);
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KDDockWidgets::FrameOptions)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(Qt5Widgets REQUIRED)
|
||||
if (@OPTION_QTQUICK@)
|
||||
if (@KDDockWidgets_QTQUICK@)
|
||||
find_dependency(Qt5Quick REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ QByteArray LayoutSaver::serializeLayout() const
|
||||
layout.mainWindows.push_back(mainWindow->serialize());
|
||||
}
|
||||
|
||||
const QVector<KDDockWidgets::FloatingWindow*> floatingWindows = d->m_dockRegistry->nestedwindows();
|
||||
const QVector<KDDockWidgets::FloatingWindow*> floatingWindows = d->m_dockRegistry->floatingWindows();
|
||||
layout.floatingWindows.reserve(floatingWindows.size());
|
||||
for (KDDockWidgets::FloatingWindow *floatingWindow : floatingWindows) {
|
||||
if (d->matchesAffinity(floatingWindow->affinities()))
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
*/
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Config.h"
|
||||
#include "DropArea_p.h"
|
||||
#include "Frame_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "SideBar_p.h"
|
||||
#include "DropAreaWithCentralFrame_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QVBoxLayout>
|
||||
@@ -31,11 +34,21 @@ using namespace KDDockWidgets;
|
||||
class MainWindow::Private
|
||||
{
|
||||
public:
|
||||
explicit Private(MainWindowOptions, MainWindowBase *)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
explicit Private(MainWindowOptions, MainWindowBase *mainWindow)
|
||||
: m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
|
||||
{
|
||||
if (m_supportsAutoHide) {
|
||||
for (auto location : { SideBarLocation::North, SideBarLocation::East,
|
||||
SideBarLocation::West, SideBarLocation::South}) {
|
||||
m_sideBars.insert(location, Config::self().frameworkWidgetFactory()->createSideBar(location, mainWindow) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool m_supportsAutoHide;
|
||||
QHash<SideBarLocation, SideBar*> m_sideBars;
|
||||
};
|
||||
|
||||
namespace KDDockWidgets {
|
||||
class MyCentralWidget : public QWidget
|
||||
@@ -67,9 +80,24 @@ MainWindow::MainWindow(const QString &name, MainWindowOptions options,
|
||||
, d(new Private(options, this))
|
||||
{
|
||||
auto centralWidget = new MyCentralWidget(this);
|
||||
auto layout = new QVBoxLayout(centralWidget);
|
||||
layout->setContentsMargins(1, 5, 1, 1);
|
||||
layout->addWidget(dropArea()); // 1 level of indirection so we can add some margins
|
||||
auto layout = new QHBoxLayout(centralWidget); // 1 level of indirection so we can add some margins
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(centerWidgetMargins());
|
||||
|
||||
if (d->m_supportsAutoHide) {
|
||||
layout->addWidget(sideBar(SideBarLocation::West));
|
||||
auto innerVLayout = new QVBoxLayout();
|
||||
innerVLayout->setSpacing(0);
|
||||
innerVLayout->setContentsMargins(0, 0, 0, 0);
|
||||
innerVLayout->addWidget(sideBar(SideBarLocation::North));
|
||||
innerVLayout->addWidget(dropArea());
|
||||
innerVLayout->addWidget(sideBar(SideBarLocation::South));
|
||||
layout->addLayout(innerVLayout);
|
||||
layout->addWidget(sideBar(SideBarLocation::East));
|
||||
} else {
|
||||
layout->addWidget(dropArea());
|
||||
}
|
||||
|
||||
setCentralWidget(centralWidget);
|
||||
|
||||
// qApp->installEventFilter(this);
|
||||
@@ -84,3 +112,24 @@ void MainWindow::setCentralWidget(QWidget *w)
|
||||
{
|
||||
QMainWindow::setCentralWidget(w);
|
||||
}
|
||||
|
||||
SideBar *MainWindow::sideBar(SideBarLocation location) const
|
||||
{
|
||||
return d->m_sideBars.value(location);
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent *ev)
|
||||
{
|
||||
MainWindowBase::resizeEvent(ev);
|
||||
onResized(ev); // Also call our own handler, since QtQuick doesn't have resizeEvent()
|
||||
}
|
||||
|
||||
QMargins MainWindow::centerWidgetMargins() const
|
||||
{
|
||||
return { 1, 5, 1, 1};
|
||||
}
|
||||
|
||||
QRect MainWindow::centralAreaGeometry() const
|
||||
{
|
||||
return centralWidget()->geometry();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class SideBar;
|
||||
|
||||
/**
|
||||
* @brief The QMainwindow sub-class that the application should use to be able
|
||||
* to dock KDDockWidget::DockWidget instances.
|
||||
@@ -45,6 +47,13 @@ public:
|
||||
///@brief Destructor
|
||||
~MainWindow() override;
|
||||
|
||||
///@brief returns the sidebar for the specified location
|
||||
SideBar *sideBar(SideBarLocation) const override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
QMargins centerWidgetMargins() const override;
|
||||
QRect centralAreaGeometry() const override;
|
||||
private:
|
||||
using QMainWindow::setCentralWidget;
|
||||
void setCentralWidget(QWidget *); // overridden just to make it private
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
#include "DropArea_p.h"
|
||||
#include "Frame_p.h"
|
||||
#include "Utils_p.h"
|
||||
#include "SideBar_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "Item_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "DropAreaWithCentralFrame_p.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
@@ -42,15 +45,20 @@ public:
|
||||
return m_options & MainWindowOption_HasCentralFrame;
|
||||
}
|
||||
|
||||
QRect rectForOverlay(Frame *, SideBarLocation) const;
|
||||
SideBarLocation preferredSideBar(DockWidgetBase *) const;
|
||||
void updateOverlayGeometry();
|
||||
|
||||
QString name;
|
||||
QStringList affinities;
|
||||
const MainWindowOptions m_options;
|
||||
MainWindowBase *const q;
|
||||
QPointer<DockWidgetBase> m_overlayedDockWidget;
|
||||
DropAreaWithCentralFrame *const m_dropArea;
|
||||
};
|
||||
|
||||
MainWindowBase::MainWindowBase(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
|
||||
QWidgetOrQuick *parent, Qt::WindowFlags flags)
|
||||
WidgetType *parent, Qt::WindowFlags flags)
|
||||
: QMainWindowOrQuick(parent, flags)
|
||||
, d(new Private(this, options))
|
||||
{
|
||||
@@ -149,6 +157,295 @@ void MainWindowBase::layoutParentContainerEqually(DockWidgetBase *dockWidget)
|
||||
dropArea()->layoutParentContainerEqually(dockWidget);
|
||||
}
|
||||
|
||||
QRect MainWindowBase::Private::rectForOverlay(Frame *frame, SideBarLocation location) const
|
||||
{
|
||||
SideBar *sb = q->sideBar(location);
|
||||
if (!sb)
|
||||
return {};
|
||||
|
||||
const QRect centralAreaGeo = q->centralAreaGeometry();
|
||||
const QMargins centerWidgetMargins = q->centerWidgetMargins();
|
||||
|
||||
QRect rect;
|
||||
const int margin = 1;
|
||||
switch (location) {
|
||||
case SideBarLocation::North:
|
||||
case SideBarLocation::South: {
|
||||
|
||||
SideBar *leftSideBar = q->sideBar(SideBarLocation::West);
|
||||
SideBar *rightSideBar = q->sideBar(SideBarLocation::East);
|
||||
const int leftSideBarWidth = (leftSideBar && leftSideBar->isVisible()) ? leftSideBar->width()
|
||||
: 0;
|
||||
const int rightSideBarWidth = (rightSideBar && rightSideBar->isVisible()) ? rightSideBar->width()
|
||||
: 0;
|
||||
rect.setHeight(qMax(300, frame->minSize().height()));
|
||||
rect.setWidth(centralAreaGeo.width() - margin * 2 - leftSideBarWidth - rightSideBarWidth);
|
||||
rect.moveLeft(margin + leftSideBarWidth);
|
||||
if (location == SideBarLocation::South) {
|
||||
rect.moveTop(centralAreaGeo.bottom() - centerWidgetMargins.bottom() - rect.height() - sb->height());
|
||||
} else {
|
||||
rect.moveTop(centralAreaGeo.y() + sb->height() + centerWidgetMargins.top());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SideBarLocation::West:
|
||||
case SideBarLocation::East: {
|
||||
SideBar *topSideBar = q->sideBar(SideBarLocation::North);
|
||||
SideBar *bottomSideBar = q->sideBar(SideBarLocation::South);
|
||||
const int topSideBarHeight = (topSideBar && topSideBar->isVisible()) ? topSideBar->height()
|
||||
: 0;
|
||||
const int bottomSideBarHeight = (bottomSideBar && bottomSideBar->isVisible()) ? bottomSideBar->height()
|
||||
: 0;
|
||||
rect.setWidth(qMax(300, frame->minSize().width()));
|
||||
rect.setHeight(centralAreaGeo.height() - topSideBarHeight - bottomSideBarHeight - centerWidgetMargins.top() - centerWidgetMargins.bottom());
|
||||
rect.moveTop(sb->mapTo(q, QPoint(0, 0)).y() + topSideBarHeight - 1);
|
||||
if (location == SideBarLocation::East) {
|
||||
rect.moveLeft(centralAreaGeo.width() - rect.width() - sb->width() - centerWidgetMargins.right() - margin);
|
||||
} else {
|
||||
rect.moveLeft(margin + centralAreaGeo.x() + centerWidgetMargins.left() + sb->width());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SideBarLocation::None:
|
||||
break;
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
static SideBarLocation opposedSideBarLocationForBorder(Layouting::Item::LayoutBorderLocation loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Layouting::Item::LayoutBorderLocation_North:
|
||||
return SideBarLocation::South;
|
||||
case Layouting::Item::LayoutBorderLocation_East:
|
||||
return SideBarLocation::West;
|
||||
case Layouting::Item::LayoutBorderLocation_West:
|
||||
return SideBarLocation::East;
|
||||
case Layouting::Item::LayoutBorderLocation_South:
|
||||
return SideBarLocation::North;
|
||||
case Layouting::Item::LayoutBorderLocation_All:
|
||||
case Layouting::Item::LayoutBorderLocation_Verticals:
|
||||
case Layouting::Item::LayoutBorderLocation_Horizontals:
|
||||
case Layouting::Item::LayoutBorderLocation_None:
|
||||
break;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "Unknown loc" << loc;
|
||||
return SideBarLocation::None;
|
||||
}
|
||||
|
||||
static SideBarLocation sideBarLocationForBorder(Layouting::Item::LayoutBorderLocations loc)
|
||||
{
|
||||
switch (loc) {
|
||||
case Layouting::Item::LayoutBorderLocation_North:
|
||||
return SideBarLocation::North;
|
||||
case Layouting::Item::LayoutBorderLocation_East:
|
||||
return SideBarLocation::East;
|
||||
case Layouting::Item::LayoutBorderLocation_West:
|
||||
return SideBarLocation::West;
|
||||
case Layouting::Item::LayoutBorderLocation_South:
|
||||
return SideBarLocation::South;
|
||||
case Layouting::Item::LayoutBorderLocation_All:
|
||||
case Layouting::Item::LayoutBorderLocation_Verticals:
|
||||
case Layouting::Item::LayoutBorderLocation_Horizontals:
|
||||
case Layouting::Item::LayoutBorderLocation_None:
|
||||
break;
|
||||
}
|
||||
|
||||
return SideBarLocation::None;
|
||||
}
|
||||
|
||||
SideBarLocation MainWindowBase::Private::preferredSideBar(DockWidgetBase *dw) const
|
||||
{
|
||||
// TODO: Algorithm can still be made smarter
|
||||
|
||||
Layouting::Item *item = q->multiSplitter()->itemForFrame(dw->frame());
|
||||
if (!item) {
|
||||
qWarning() << Q_FUNC_INFO << "No item for dock widget";
|
||||
return SideBarLocation::None;
|
||||
}
|
||||
|
||||
const Layouting::Item::LayoutBorderLocations borders = item->adjacentLayoutBorders();
|
||||
const qreal aspectRatio = dw->width() / (dw->height() * 1.0);
|
||||
|
||||
/// 1. It's touching all borders
|
||||
if (borders == Layouting::Item::LayoutBorderLocation_All) {
|
||||
return aspectRatio > 1.0 ? SideBarLocation::South
|
||||
: SideBarLocation::East;
|
||||
}
|
||||
|
||||
/// 2. It's touching 3 borders
|
||||
for (auto borderLoc : { Layouting::Item::LayoutBorderLocation_North, Layouting::Item::LayoutBorderLocation_East,
|
||||
Layouting::Item::LayoutBorderLocation_West, Layouting::Item::LayoutBorderLocation_South }) {
|
||||
if (borders == (Layouting::Item::LayoutBorderLocation_All & ~borderLoc))
|
||||
return opposedSideBarLocationForBorder(borderLoc);
|
||||
}
|
||||
|
||||
/// 3. It's touching left and right borders
|
||||
if ((borders & Layouting::Item::LayoutBorderLocation_Verticals) == Layouting::Item::LayoutBorderLocation_Verticals) {
|
||||
// We could measure the distance to the top though.
|
||||
return SideBarLocation::South;
|
||||
}
|
||||
|
||||
/// 4. It's touching top and bottom borders
|
||||
if ((borders & Layouting::Item::LayoutBorderLocation_Horizontals) == Layouting::Item::LayoutBorderLocation_Horizontals) {
|
||||
// We could measure the distance to the left though.
|
||||
return SideBarLocation::East;
|
||||
}
|
||||
|
||||
// 5. It's in a corner
|
||||
if (borders == (Layouting::Item::LayoutBorderLocation_West | Layouting::Item::LayoutBorderLocation_South)) {
|
||||
return aspectRatio > 1.0 ? SideBarLocation::South
|
||||
: SideBarLocation::West;
|
||||
} else if (borders == (Layouting::Item::LayoutBorderLocation_East | Layouting::Item::LayoutBorderLocation_South)) {
|
||||
return aspectRatio > 1.0 ? SideBarLocation::South
|
||||
: SideBarLocation::East;
|
||||
} else if (borders == (Layouting::Item::LayoutBorderLocation_West | Layouting::Item::LayoutBorderLocation_North)) {
|
||||
return aspectRatio > 1.0 ? SideBarLocation::North
|
||||
: SideBarLocation::West;
|
||||
} else if (borders == (Layouting::Item::LayoutBorderLocation_East | Layouting::Item::LayoutBorderLocation_North)) {
|
||||
return aspectRatio > 1.0 ? SideBarLocation::North
|
||||
: SideBarLocation::East;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// 6. It's only touching 1 border
|
||||
SideBarLocation loc = sideBarLocationForBorder(borders);
|
||||
if (loc != SideBarLocation::None)
|
||||
return loc;
|
||||
}
|
||||
|
||||
// It's not touching any border, use aspect ratio.
|
||||
return aspectRatio > 1.0 ? SideBarLocation::South
|
||||
: SideBarLocation::West;
|
||||
}
|
||||
|
||||
void MainWindowBase::Private::updateOverlayGeometry()
|
||||
{
|
||||
if (!m_overlayedDockWidget)
|
||||
return;
|
||||
|
||||
SideBar *sb = q->sideBarForDockWidget(m_overlayedDockWidget);
|
||||
if (!sb) {
|
||||
qWarning() << Q_FUNC_INFO << "Expected a sidebar";
|
||||
return;
|
||||
}
|
||||
|
||||
m_overlayedDockWidget->frame()->QWidgetAdapter::setGeometry(rectForOverlay(m_overlayedDockWidget->frame(), sb->location()));
|
||||
}
|
||||
|
||||
void MainWindowBase::moveToSideBar(DockWidgetBase *dw)
|
||||
{
|
||||
moveToSideBar(dw, d->preferredSideBar(dw));
|
||||
}
|
||||
|
||||
void MainWindowBase::moveToSideBar(DockWidgetBase *dw, SideBarLocation location)
|
||||
{
|
||||
if (SideBar *sb = sideBar(location)) {
|
||||
dw->forceClose();
|
||||
sb->addDockWidget(dw);
|
||||
} else {
|
||||
// Shouldn't happen
|
||||
qWarning() << Q_FUNC_INFO << "Minimization supported, probably disabled in Config::self().flags()";
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowBase::restoreFromSideBar(DockWidgetBase *dw)
|
||||
{
|
||||
// First un-overlay it, if it's overlayed
|
||||
if (dw == d->m_overlayedDockWidget)
|
||||
clearSideBarOverlay();
|
||||
|
||||
SideBar *sb = sideBarForDockWidget(dw);
|
||||
if (!sb) {
|
||||
// Doesn't happen
|
||||
qWarning() << Q_FUNC_INFO << "Dock widget isn't in any sidebar";
|
||||
return;
|
||||
}
|
||||
|
||||
sb->removeDockWidget(dw);
|
||||
dw->setFloating(false); // dock it
|
||||
}
|
||||
|
||||
void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
|
||||
{
|
||||
if (!dw)
|
||||
return;
|
||||
|
||||
const SideBar *sb = sideBarForDockWidget(dw);
|
||||
if (sb == nullptr) {
|
||||
qWarning() << Q_FUNC_INFO << "You need to add the dock widget to the sidebar before you can overlay it";
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_overlayedDockWidget == dw) {
|
||||
// Already overlayed
|
||||
return;
|
||||
}
|
||||
|
||||
// We only support one overlay at a time, remove any existing overlay
|
||||
clearSideBarOverlay();
|
||||
|
||||
auto frame = Config::self().frameworkWidgetFactory()->createFrame(this, FrameOption_IsOverlayed);
|
||||
d->m_overlayedDockWidget = dw;
|
||||
frame->addWidget(dw);
|
||||
d->updateOverlayGeometry();
|
||||
frame->QWidgetAdapter::show();
|
||||
|
||||
Q_EMIT dw->isOverlayedChanged(true);
|
||||
}
|
||||
|
||||
void MainWindowBase::toggleOverlayOnSideBar(DockWidgetBase *dw)
|
||||
{
|
||||
const bool wasOverlayed = d->m_overlayedDockWidget == dw;
|
||||
clearSideBarOverlay();
|
||||
if (!wasOverlayed) {
|
||||
overlayOnSideBar(dw);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowBase::clearSideBarOverlay()
|
||||
{
|
||||
if (!d->m_overlayedDockWidget)
|
||||
return;
|
||||
|
||||
Frame *frame = d->m_overlayedDockWidget->frame();
|
||||
d->m_overlayedDockWidget->setParent(nullptr);
|
||||
Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false);
|
||||
d->m_overlayedDockWidget = nullptr;
|
||||
delete frame;
|
||||
}
|
||||
|
||||
SideBar *MainWindowBase::sideBarForDockWidget(const DockWidgetBase *dw) const
|
||||
{
|
||||
for (auto loc : { SideBarLocation::North, SideBarLocation::South,
|
||||
SideBarLocation::East, SideBarLocation::West }) {
|
||||
|
||||
if (SideBar *sb = sideBar(loc)) {
|
||||
if (sb->contains(const_cast<DockWidgetBase *>(dw)))
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DockWidgetBase *MainWindowBase::overlayedDockWidget() const
|
||||
{
|
||||
return d->m_overlayedDockWidget;
|
||||
}
|
||||
|
||||
bool MainWindowBase::sideBarIsVisible(SideBarLocation loc) const
|
||||
{
|
||||
if (SideBar *sb = sideBar(loc))
|
||||
return sb->isVisible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindowBase::setUniqueName(const QString &uniqueName)
|
||||
{
|
||||
if (uniqueName.isEmpty())
|
||||
@@ -163,6 +460,12 @@ void MainWindowBase::setUniqueName(const QString &uniqueName)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowBase::onResized(QResizeEvent *)
|
||||
{
|
||||
if (d->m_overlayedDockWidget)
|
||||
d->updateOverlayGeometry();
|
||||
}
|
||||
|
||||
bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
|
||||
{
|
||||
if (mw.options != options()) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "LayoutSaver_p.h"
|
||||
|
||||
#include <QVector>
|
||||
#include <QMargins>
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
@@ -34,6 +35,7 @@ class Frame;
|
||||
class DropArea;
|
||||
class MultiSplitter;
|
||||
class DropAreaWithCentralFrame;
|
||||
class SideBar;
|
||||
|
||||
/**
|
||||
* @brief The MainWindow base-class. MainWindow and MainWindowBase are only
|
||||
@@ -52,7 +54,7 @@ class DOCKS_EXPORT MainWindowBase : public QMainWindow
|
||||
public:
|
||||
typedef QVector<MainWindowBase*> List;
|
||||
explicit MainWindowBase(const QString &uniqueName, MainWindowOptions options = MainWindowOption_HasCentralFrame,
|
||||
QWidgetOrQuick *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
WidgetType *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
|
||||
~MainWindowBase() override;
|
||||
|
||||
@@ -131,8 +133,45 @@ public:
|
||||
/// sub-tree.
|
||||
void layoutParentContainerEqually(DockWidgetBase *dockWidget);
|
||||
|
||||
///@brief Moves the dock widget into one of the MainWindow's sidebar.
|
||||
/// Means the dock widget is removed from the layout, and the sidebar shows a button that if pressed
|
||||
/// will toggle the dock widget's visibility as an overlay over the layout. This is the auto-hide
|
||||
/// functionality.
|
||||
///
|
||||
/// The chosen side bar will depend on some heuristics, mostly proximity.
|
||||
void moveToSideBar(DockWidgetBase *);
|
||||
|
||||
/// @brief overload that allows to specify which sidebar to use, instead of using heuristics.
|
||||
void moveToSideBar(DockWidgetBase *, SideBarLocation);
|
||||
|
||||
/// @brief Removes the dock widget from the sidebar and docks it into the main window again
|
||||
void restoreFromSideBar(DockWidgetBase *);
|
||||
|
||||
///@brief Shows the dock widget overlayed on top of the main window, placed next to the sidebar
|
||||
void overlayOnSideBar(DockWidgetBase *);
|
||||
|
||||
///@brief Shows or hides an overlay. It's assumed the dock widget is already in a side-bar.
|
||||
void toggleOverlayOnSideBar(DockWidgetBase *);
|
||||
|
||||
/// @brief closes any overlayed dock widget. The sidebar still displays them as button.
|
||||
void clearSideBarOverlay();
|
||||
|
||||
/// @brief Returns the sidebar this dockwidget is in. nullptr if not in any.
|
||||
SideBar *sideBarForDockWidget(const DockWidgetBase *) const;
|
||||
|
||||
/// @brief returns the dock widget which is currently overlayed. nullptr if none.
|
||||
/// This is only relevant when using the auto-hide and side-bar feature.
|
||||
DockWidgetBase *overlayedDockWidget() const;
|
||||
|
||||
/// @brief Returns whether the specified sidebar is visible
|
||||
bool sideBarIsVisible(SideBarLocation) const;
|
||||
|
||||
protected:
|
||||
void setUniqueName(const QString &uniqueName);
|
||||
void onResized(QResizeEvent *); // Because QtQuick doesn't have resizeEvent()
|
||||
virtual QMargins centerWidgetMargins() const = 0;
|
||||
virtual SideBar* sideBar(SideBarLocation) const = 0;
|
||||
virtual QRect centralAreaGeometry() const { return {}; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void uniqueNameChanged();
|
||||
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
, LayoutGuestWidgetBase(this)
|
||||
{
|
||||
}
|
||||
|
||||
~LayoutGuestWidget() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sergio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
#include "../../../../private/widgets/SideBarWidget_p.h"
|
||||
BIN
src/img/auto-hide-1.5x.png
Normal file
|
After Width: | Height: | Size: 473 B |
BIN
src/img/auto-hide-2x.png
Normal file
|
After Width: | Height: | Size: 477 B |
BIN
src/img/auto-hide.png
Normal file
|
After Width: | Height: | Size: 208 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
src/img/close-1.5x.png
Normal file
|
After Width: | Height: | Size: 496 B |
BIN
src/img/close-2x.png
Normal file
|
After Width: | Height: | Size: 543 B |
BIN
src/img/close.png
Normal file
|
After Width: | Height: | Size: 405 B |
BIN
src/img/dock-float-1.5x.png
Normal file
|
After Width: | Height: | Size: 438 B |
BIN
src/img/dock-float-2x.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
src/img/dock-float.png
Normal file
|
After Width: | Height: | Size: 286 B |
BIN
src/img/max-1.5x.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
src/img/max-2x.png
Normal file
|
After Width: | Height: | Size: 445 B |
BIN
src/img/max.png
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
src/img/min-1.5x.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
src/img/min-2x.png
Normal file
|
After Width: | Height: | Size: 386 B |
BIN
src/img/min.png
Normal file
|
After Width: | Height: | Size: 239 B |
BIN
src/img/unauto-hide-1.5x.png
Normal file
|
After Width: | Height: | Size: 461 B |
BIN
src/img/unauto-hide-2x.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
src/img/unauto-hide.png
Normal file
|
After Width: | Height: | Size: 218 B |
@@ -200,7 +200,7 @@ DebugWindow::DebugWindow(QWidget *parent)
|
||||
mainWindow->multiSplitter()->checkSanity();
|
||||
}
|
||||
|
||||
const auto floatingWindows = DockRegistry::self()->nestedwindows();
|
||||
const auto floatingWindows = DockRegistry::self()->floatingWindows();
|
||||
for (FloatingWindow *floatingWindow : floatingWindows) {
|
||||
floatingWindow->multiSplitter()->checkSanity();
|
||||
}
|
||||
@@ -308,7 +308,7 @@ void DebugWindow::repaintWidgetRecursive(QWidget *w)
|
||||
|
||||
void DebugWindow::dumpDockWidgetInfo()
|
||||
{
|
||||
const QVector<FloatingWindow*> floatingWindows = DockRegistry::self()->nestedwindows();
|
||||
const QVector<FloatingWindow*> floatingWindows = DockRegistry::self()->floatingWindows();
|
||||
const MainWindowBase::List mainWindows = DockRegistry::self()->mainwindows();
|
||||
const DockWidgetBase::List dockWidgets = DockRegistry::self()->dockwidgets();
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "MultiSplitter_p.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
#include "Config.h"
|
||||
#include "SideBar_p.h"
|
||||
#include "WindowBeingDragged_p.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QDebug>
|
||||
@@ -99,9 +101,13 @@ void DockRegistry::onFocusObjectChanged(QObject *obj)
|
||||
Q_EMIT m_focusedDockWidget->isFocusedChanged(true);
|
||||
}
|
||||
|
||||
bool DockRegistry::isEmpty() const
|
||||
bool DockRegistry::isEmpty(bool excludeBeingDeleted) const
|
||||
{
|
||||
return m_dockWidgets.isEmpty() && m_mainWindows.isEmpty() && m_nestedWindows.isEmpty();
|
||||
if (!m_dockWidgets.isEmpty() || !m_mainWindows.isEmpty())
|
||||
return false;
|
||||
|
||||
return excludeBeingDeleted ? !hasFloatingWindows()
|
||||
: m_floatingWindows.isEmpty();
|
||||
}
|
||||
|
||||
void DockRegistry::checkSanityAll(bool dumpLayout)
|
||||
@@ -159,7 +165,7 @@ bool DockRegistry::isProbablyObscured(QWindow *window, FloatingWindow *exclude)
|
||||
return false;
|
||||
|
||||
const QRect geo = window->geometry();
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
for (FloatingWindow *fw : m_floatingWindows) {
|
||||
QWindow *fwWindow = fw->QWidgetAdapter::windowHandle();
|
||||
if (fw == exclude || fwWindow == window)
|
||||
continue;
|
||||
@@ -185,6 +191,32 @@ bool DockRegistry::isProbablyObscured(QWindow *window, FloatingWindow *exclude)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DockRegistry::isProbablyObscured(QWindow *target, WindowBeingDragged *exclude) const
|
||||
{
|
||||
FloatingWindow *fw = exclude ? exclude->floatingWindow()
|
||||
: nullptr; // It's null on Wayland. On wayland obscuring never happens anyway, so not a problem.
|
||||
|
||||
return isProbablyObscured(target, fw);
|
||||
}
|
||||
|
||||
SideBarLocation DockRegistry::sideBarLocationForDockWidget(const DockWidgetBase *dw) const
|
||||
{
|
||||
if (SideBar *sb = sideBarForDockWidget(dw))
|
||||
return sb->location();
|
||||
|
||||
return SideBarLocation::None;
|
||||
}
|
||||
|
||||
SideBar *DockRegistry::sideBarForDockWidget(const DockWidgetBase *dw) const
|
||||
{
|
||||
for (auto mw : m_mainWindows) {
|
||||
if (SideBar *sb = mw->sideBarForDockWidget(dw))
|
||||
return sb;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &affinities) const
|
||||
{
|
||||
MainWindowBase::List result;
|
||||
@@ -203,7 +235,7 @@ MultiSplitter *DockRegistry::layoutForItem(const Layouting::Item *item) const
|
||||
if (!item->hostWidget())
|
||||
return nullptr;
|
||||
|
||||
if (auto ms = qobject_cast<MultiSplitter*>(item->hostWidget()->asQWidget()))
|
||||
if (auto ms = qobject_cast<MultiSplitter*>(item->hostWidget()->asQObject()))
|
||||
return ms;
|
||||
|
||||
return nullptr;
|
||||
@@ -241,6 +273,9 @@ void DockRegistry::registerDockWidget(DockWidgetBase *dock)
|
||||
|
||||
void DockRegistry::unregisterDockWidget(DockWidgetBase *dock)
|
||||
{
|
||||
if (m_focusedDockWidget == dock)
|
||||
m_focusedDockWidget = nullptr;
|
||||
|
||||
m_dockWidgets.removeOne(dock);
|
||||
maybeDelete();
|
||||
}
|
||||
@@ -262,14 +297,14 @@ void DockRegistry::unregisterMainWindow(MainWindowBase *mainWindow)
|
||||
maybeDelete();
|
||||
}
|
||||
|
||||
void DockRegistry::registerNestedWindow(FloatingWindow *window)
|
||||
void DockRegistry::registerFloatingWindow(FloatingWindow *window)
|
||||
{
|
||||
m_nestedWindows << window;
|
||||
m_floatingWindows << window;
|
||||
}
|
||||
|
||||
void DockRegistry::unregisterNestedWindow(FloatingWindow *window)
|
||||
void DockRegistry::unregisterFloatingWindow(FloatingWindow *window)
|
||||
{
|
||||
m_nestedWindows.removeOne(window);
|
||||
m_floatingWindows.removeOne(window);
|
||||
maybeDelete();
|
||||
}
|
||||
|
||||
@@ -421,12 +456,12 @@ const Frame::List DockRegistry::frames() const
|
||||
return m_frames;
|
||||
}
|
||||
|
||||
const QVector<FloatingWindow *> DockRegistry::nestedwindows() const
|
||||
const QVector<FloatingWindow *> DockRegistry::floatingWindows() const
|
||||
{
|
||||
// Returns all the FloatingWindow which aren't being deleted
|
||||
QVector<FloatingWindow *> result;
|
||||
result.reserve(m_nestedWindows.size());
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
result.reserve(m_floatingWindows.size());
|
||||
for (FloatingWindow *fw : m_floatingWindows) {
|
||||
if (!fw->beingDeleted())
|
||||
result.push_back(fw);
|
||||
}
|
||||
@@ -434,11 +469,11 @@ const QVector<FloatingWindow *> DockRegistry::nestedwindows() const
|
||||
return result;
|
||||
}
|
||||
|
||||
const QVector<QWindow *> DockRegistry::floatingWindows() const
|
||||
const QVector<QWindow *> DockRegistry::floatingQWindows() const
|
||||
{
|
||||
QVector<QWindow *> windows;
|
||||
windows.reserve(m_nestedWindows.size());
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
windows.reserve(m_floatingWindows.size());
|
||||
for (FloatingWindow *fw : m_floatingWindows) {
|
||||
if (!fw->beingDeleted()) {
|
||||
if (QWindow *window = fw->windowHandle()) {
|
||||
window->setProperty("kddockwidgets_qwidget", QVariant::fromValue<QWidgetOrQuick*>(fw)); // Since QWidgetWindow is private API
|
||||
@@ -452,9 +487,16 @@ const QVector<QWindow *> DockRegistry::floatingWindows() const
|
||||
return windows;
|
||||
}
|
||||
|
||||
bool DockRegistry::hasFloatingWindows() const
|
||||
{
|
||||
return std::any_of(m_floatingWindows.begin(), m_floatingWindows.end(), [] (FloatingWindow *fw) {
|
||||
return !fw->beingDeleted();
|
||||
});
|
||||
}
|
||||
|
||||
FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) const
|
||||
{
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
for (FloatingWindow *fw : m_floatingWindows) {
|
||||
if (fw->windowHandle() == windowHandle)
|
||||
return fw;
|
||||
}
|
||||
@@ -465,10 +507,10 @@ FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) con
|
||||
QVector<QWindow *> DockRegistry::topLevels(bool excludeFloatingDocks) const
|
||||
{
|
||||
QVector<QWindow *> windows;
|
||||
windows.reserve(m_nestedWindows.size() + m_mainWindows.size());
|
||||
windows.reserve(m_floatingWindows.size() + m_mainWindows.size());
|
||||
|
||||
if (!excludeFloatingDocks) {
|
||||
for (FloatingWindow *fw : m_nestedWindows) {
|
||||
for (FloatingWindow *fw : m_floatingWindows) {
|
||||
if (fw->isVisible()) {
|
||||
if (QWindow *window = fw->windowHandle()) {
|
||||
window->setProperty("kddockwidgets_qwidget", QVariant::fromValue<QWidgetOrQuick*>(fw)); // Since QWidgetWindow is private API
|
||||
@@ -537,11 +579,39 @@ bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
|
||||
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);
|
||||
m_floatingWindows.removeOne(fw);
|
||||
m_floatingWindows.append(fw);
|
||||
}
|
||||
}
|
||||
} else if (event->type() == QEvent::MouseButtonPress) {
|
||||
if (!(Config::self().flags() & Config::Flag_AutoHideSupport))
|
||||
return false;
|
||||
|
||||
auto p = watched;
|
||||
while (p) {
|
||||
if (auto dw = qobject_cast<DockWidgetBase*>(p)) {
|
||||
onDockWidgetPressed(dw);
|
||||
return false;
|
||||
}
|
||||
|
||||
p = p->parent();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DockRegistry::onDockWidgetPressed(DockWidgetBase *dw)
|
||||
{
|
||||
// Here we implement "auto-hide". If there's a overlayed dock widget, we hide it if some other
|
||||
// dock widget is clicked.
|
||||
|
||||
MainWindowBase *mainWindow = dw->mainWindow();
|
||||
if (!mainWindow) // Only docked widgets are interesting
|
||||
return;
|
||||
|
||||
DockWidgetBase *overlayedDockWidget = mainWindow->overlayedDockWidget();
|
||||
if (overlayedDockWidget && dw != overlayedDockWidget) {
|
||||
mainWindow->clearSideBarOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
namespace KDDockWidgets
|
||||
{
|
||||
|
||||
class SideBar;
|
||||
|
||||
class DOCKS_EXPORT DockRegistry : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -40,8 +42,8 @@ public:
|
||||
void registerMainWindow(MainWindowBase *);
|
||||
void unregisterMainWindow(MainWindowBase *);
|
||||
|
||||
void registerNestedWindow(FloatingWindow *);
|
||||
void unregisterNestedWindow(FloatingWindow *);
|
||||
void registerFloatingWindow(FloatingWindow *);
|
||||
void unregisterFloatingWindow(FloatingWindow *);
|
||||
|
||||
void registerLayout(MultiSplitter *);
|
||||
void unregisterLayout(MultiSplitter *);
|
||||
@@ -82,10 +84,13 @@ public:
|
||||
|
||||
///@brief returns all FloatingWindow instances. Not necessarily all floating dock widgets,
|
||||
/// As there might be DockWidgets which weren't morphed yet.
|
||||
const QVector<FloatingWindow*> nestedwindows() const;
|
||||
const QVector<FloatingWindow*> floatingWindows() const;
|
||||
|
||||
///@brief overload that returns list of QWindow. This is more friendly for supporting both QtWidgets and QtQuick
|
||||
const QVector<QWindow*> floatingWindows() const;
|
||||
const QVector<QWindow*> floatingQWindows() const;
|
||||
|
||||
///@brief returns whether if there's at least one floating window
|
||||
bool hasFloatingWindows() const;
|
||||
|
||||
///@brief returns the FloatingWindow with handle @p windowHandle
|
||||
FloatingWindow *floatingWindowForHandle(QWindow *windowHandle) const;
|
||||
@@ -125,8 +130,10 @@ public:
|
||||
|
||||
/**
|
||||
* @brief returns true if there's 0 dockwidgets, 0 main windows
|
||||
*
|
||||
* @param excludeBeingDeleted if true, any window currently being deleted won't count
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
bool isEmpty(bool excludeBeingDeleted = false) const;
|
||||
|
||||
/**
|
||||
* @brief Calls MultiSplitter::checkSanity() on all layouts.
|
||||
@@ -172,17 +179,29 @@ public:
|
||||
/// @param exclude This window should not be counted as an obscurer. (It's being dragged).
|
||||
bool isProbablyObscured(QWindow *target, FloatingWindow *exclude) const;
|
||||
|
||||
/// @overload
|
||||
bool isProbablyObscured(QWindow *target, WindowBeingDragged *exclude) const;
|
||||
|
||||
///@brief Returns whether the specified dock widget is in a side bar, and which.
|
||||
/// SideBarLocation::None is returned if it's not in a sidebar.
|
||||
/// This is only relevant when using the auto-hide and side-bar feature.
|
||||
SideBarLocation sideBarLocationForDockWidget(const DockWidgetBase *) const;
|
||||
|
||||
///@brief Overload that returns the SideBar itself
|
||||
SideBar* sideBarForDockWidget(const DockWidgetBase *) const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
private:
|
||||
explicit DockRegistry(QObject *parent = nullptr);
|
||||
void onDockWidgetPressed(DockWidgetBase *dw);
|
||||
void maybeDelete();
|
||||
void onFocusObjectChanged(QObject *);
|
||||
bool m_isProcessingAppQuitEvent = false;
|
||||
DockWidgetBase::List m_dockWidgets;
|
||||
MainWindowBase::List m_mainWindows;
|
||||
Frame::List m_frames;
|
||||
QVector<FloatingWindow*> m_nestedWindows;
|
||||
QVector<FloatingWindow*> m_floatingWindows;
|
||||
QVector<MultiSplitter*> m_layouts;
|
||||
QPointer<DockWidgetBase> m_focusedDockWidget;
|
||||
};
|
||||
|
||||
@@ -89,6 +89,11 @@ StateBase::StateBase(DragController *parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool StateBase::isActiveState() const
|
||||
{
|
||||
return q->activeState() == this;
|
||||
}
|
||||
|
||||
StateBase::~StateBase() = default;
|
||||
|
||||
StateNone::StateNone(DragController *parent)
|
||||
@@ -175,6 +180,27 @@ void StateDragging::onEntry(QEvent *)
|
||||
|
||||
q->m_windowBeingDragged = q->m_draggable->makeWindow();
|
||||
if (q->m_windowBeingDragged) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
# ifdef Q_OS_WIN
|
||||
if (!q->m_nonClientDrag && KDDockWidgets::usesNativeDraggingAndResizing()) {
|
||||
// Started as a client move, as the dock widget was docked,
|
||||
// but now that we're dragging it as a floating window, switch to native drag
|
||||
FloatingWindow *fw = q->m_windowBeingDragged->floatingWindow();
|
||||
q->m_nonClientDrag = true;
|
||||
q->m_windowBeingDragged.reset();
|
||||
const HWND hwnd = HWND(fw->windowHandle()->winId());
|
||||
q->m_windowBeingDragged = fw->makeWindow();
|
||||
|
||||
QWindow *window = fw->windowHandle();
|
||||
window->startSystemMove();
|
||||
|
||||
// Mouse press was done in another window, so we need to ungrab
|
||||
ReleaseCapture();
|
||||
PostMessage(hwnd, WM_SYSCOMMAND, 0xF012, 0); // SC_DRAGMOVE
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
qCDebug(state) << "StateDragging entered. m_draggable=" << q->m_draggable << "; m_windowBeingDragged=" << q->m_windowBeingDragged->floatingWindow();
|
||||
|
||||
auto fw = q->m_windowBeingDragged->floatingWindow();
|
||||
@@ -212,7 +238,7 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
|
||||
}
|
||||
|
||||
if (q->m_currentDropArea) {
|
||||
if (q->m_currentDropArea->drop(floatingWindow, globalPos)) {
|
||||
if (q->m_currentDropArea->drop(q->m_windowBeingDragged.get(), globalPos)) {
|
||||
Q_EMIT q->dropped();
|
||||
} else {
|
||||
qCDebug(state) << "StateDragging: Bailling out, drop not accepted";
|
||||
@@ -260,7 +286,7 @@ bool StateDragging::handleMouseMove(QPoint globalPos)
|
||||
}
|
||||
}
|
||||
|
||||
dropArea->hover(fw, globalPos);
|
||||
dropArea->hover(q->m_windowBeingDragged.get(), globalPos);
|
||||
}
|
||||
|
||||
q->m_currentDropArea = dropArea;
|
||||
@@ -268,13 +294,39 @@ bool StateDragging::handleMouseMove(QPoint globalPos)
|
||||
return true;
|
||||
}
|
||||
|
||||
StateDraggingWayland::StateDraggingWayland(DragController *parent)
|
||||
: StateDragging(parent)
|
||||
{
|
||||
}
|
||||
|
||||
StateDraggingWayland::~StateDraggingWayland()
|
||||
{
|
||||
}
|
||||
|
||||
void StateDraggingWayland::onEntry(QEvent *)
|
||||
{
|
||||
// Create a QDrag here
|
||||
}
|
||||
|
||||
bool StateDraggingWayland::handleMouseButtonRelease(QPoint /*globalPos*/)
|
||||
{
|
||||
Q_EMIT q->dragCanceled();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateDraggingWayland::handleMouseMove(QPoint /*globalPos*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DragController::DragController(QObject *)
|
||||
{
|
||||
qCDebug(creation) << "DragController()";
|
||||
|
||||
auto stateNone = new StateNone(this);
|
||||
auto statepreDrag = new StatePreDrag(this);
|
||||
auto stateDragging = new StateDragging(this);
|
||||
auto stateDragging = isWayland() ? new StateDraggingWayland(this)
|
||||
: new StateDragging(this);
|
||||
|
||||
stateNone->addTransition(this, &DragController::mousePressed, statepreDrag);
|
||||
statepreDrag->addTransition(this, &DragController::dragCanceled, stateNone);
|
||||
@@ -337,12 +389,23 @@ void DragController::releaseMouse(QWidgetOrQuick *target)
|
||||
}
|
||||
}
|
||||
|
||||
FloatingWindow *DragController::windowBeingDragged() const
|
||||
FloatingWindow *DragController::floatingWindowBeingDragged() const
|
||||
{
|
||||
return m_windowBeingDragged ? m_windowBeingDragged->floatingWindow()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void DragController::enableFallbackMouseGrabber()
|
||||
{
|
||||
if (!m_fallbackMouseGrabber)
|
||||
m_fallbackMouseGrabber = new FallbackMouseGrabber(this);
|
||||
}
|
||||
|
||||
WindowBeingDragged *DragController::windowBeingDragged() const
|
||||
{
|
||||
return m_windowBeingDragged.get();
|
||||
}
|
||||
|
||||
static QMouseEvent *mouseEvent(QEvent *e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
@@ -377,7 +440,8 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
|
||||
if (!w)
|
||||
return QStateMachine::eventFilter(o, e);
|
||||
|
||||
qCDebug(mouseevents) << "DragController::eventFilter e=" << e->type() << "; o=" << o;
|
||||
qCDebug(mouseevents) << "DragController::eventFilter e=" << e->type() << "; o=" << o
|
||||
<< "; m_nonClientDrag=" << m_nonClientDrag;
|
||||
|
||||
switch (e->type()) {
|
||||
case QEvent::NonClientAreaMouseButtonPress: {
|
||||
@@ -506,7 +570,7 @@ WidgetType *DragController::qtTopLevelUnderCursor() const
|
||||
// The floating window list is sorted by z-order, as we catch QEvent::Expose and move it to last of the list
|
||||
|
||||
FloatingWindow *tlwBeingDragged = m_windowBeingDragged->floatingWindow();
|
||||
if (auto tl = qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->floatingWindows(), tlwBeingDragged))
|
||||
if (auto tl = qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->floatingQWindows(), tlwBeingDragged))
|
||||
return tl;
|
||||
|
||||
return qtTopLevelUnderCursor_impl<WidgetType*>(globalPos,
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef KD_DRAGCONTROLLER_P_H
|
||||
#define KD_DRAGCONTROLLER_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
|
||||
#include "TitleBar_p.h"
|
||||
#include "WindowBeingDragged_p.h"
|
||||
|
||||
@@ -26,7 +28,7 @@ class DropArea;
|
||||
class Draggable;
|
||||
class FallbackMouseGrabber;
|
||||
|
||||
class DragController : public QStateMachine
|
||||
class DOCKS_EXPORT DragController : public QStateMachine
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -50,7 +52,13 @@ public:
|
||||
void grabMouseFor(QWidgetOrQuick *);
|
||||
void releaseMouse(QWidgetOrQuick *);
|
||||
|
||||
FloatingWindow *windowBeingDragged() const;
|
||||
FloatingWindow *floatingWindowBeingDragged() const;
|
||||
|
||||
///@brief Returns the window being dragged
|
||||
WindowBeingDragged* windowBeingDragged() const;
|
||||
|
||||
/// Experimental, internal, not for general use.
|
||||
void enableFallbackMouseGrabber();
|
||||
|
||||
Q_SIGNALS:
|
||||
void mousePressed();
|
||||
@@ -96,6 +104,9 @@ public:
|
||||
virtual bool handleMouseMove(QPoint /*globalPos*/) { return false; }
|
||||
virtual bool handleMouseButtonRelease(QPoint /*globalPos*/) { return false; }
|
||||
|
||||
// Returns whether this is the current state
|
||||
bool isActiveState() const;
|
||||
|
||||
DragController *const q;
|
||||
};
|
||||
|
||||
@@ -120,6 +131,7 @@ public:
|
||||
bool handleMouseButtonRelease(QPoint) override;
|
||||
};
|
||||
|
||||
// Used on all platforms except Wayland. @see StateDraggingWayland
|
||||
class StateDragging : public StateBase
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -131,6 +143,18 @@ public:
|
||||
bool handleMouseMove(QPoint globalPos) override;
|
||||
};
|
||||
|
||||
// Used on wayland only to use QDrag instead of setting geometry on mouse-move.
|
||||
class StateDraggingWayland : public StateDragging
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit StateDraggingWayland(DragController *parent);
|
||||
~StateDraggingWayland() override;
|
||||
void onEntry(QEvent *) override;
|
||||
bool handleMouseButtonRelease(QPoint globalPos) override;
|
||||
bool handleMouseMove(QPoint globalPos) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -71,6 +71,13 @@ Frame *DropArea::frameContainingPos(QPoint globalPos) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DropArea::updateFloatingActions()
|
||||
{
|
||||
const Frame::List frames = this->frames();
|
||||
for (Frame *frame : frames)
|
||||
frame->updateFloatingActions();
|
||||
}
|
||||
|
||||
Layouting::Item *DropArea::centralFrame() const
|
||||
{
|
||||
for (Layouting::Item *item : this->items()) {
|
||||
@@ -103,6 +110,8 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
|
||||
|
||||
dw->saveLastFloatingGeometry();
|
||||
|
||||
const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
|
||||
|
||||
// Check if the dock widget already exists in the layout
|
||||
if (contains(dw)) {
|
||||
Frame *oldFrame = dw->frame();
|
||||
@@ -124,6 +133,12 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
|
||||
} else {
|
||||
addWidget(frame, location, relativeToFrame, DefaultSizeMode::Fair, option);
|
||||
}
|
||||
|
||||
if (hadSingleFloatingFrame && !hasSingleFloatingFrame()) {
|
||||
// The dock widgets that already existed in our layout need to have their floatAction() updated
|
||||
// otherwise it's still checked. Only the dropped dock widget got updated
|
||||
updateFloatingActions();
|
||||
}
|
||||
}
|
||||
|
||||
bool DropArea::contains(DockWidgetBase *dw) const
|
||||
@@ -131,6 +146,12 @@ bool DropArea::contains(DockWidgetBase *dw) const
|
||||
return dw->frame() && MultiSplitter::contains(dw->frame());
|
||||
}
|
||||
|
||||
bool DropArea::hasSingleFloatingFrame() const
|
||||
{
|
||||
const Frame::List frames = this->frames();
|
||||
return frames.size() == 1 && frames.first()->isFloating();
|
||||
}
|
||||
|
||||
QStringList DropArea::affinities() const
|
||||
{
|
||||
if (auto mw = mainWindow()) {
|
||||
@@ -153,7 +174,7 @@ void DropArea::layoutParentContainerEqually(DockWidgetBase *dw)
|
||||
layoutEqually(item->parentContainer());
|
||||
}
|
||||
|
||||
void DropArea::hover(FloatingWindow *floatingWindow, QPoint globalPos)
|
||||
void DropArea::hover(WindowBeingDragged *floatingWindow, QPoint globalPos)
|
||||
{
|
||||
if (!validateAffinity(floatingWindow))
|
||||
return;
|
||||
@@ -164,7 +185,7 @@ void DropArea::hover(FloatingWindow *floatingWindow, QPoint globalPos)
|
||||
}
|
||||
|
||||
Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow);
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow != nullptr);
|
||||
m_dropIndicatorOverlay->setHoveredFrame(frame);
|
||||
m_dropIndicatorOverlay->hover(globalPos);
|
||||
}
|
||||
@@ -182,9 +203,12 @@ static bool isOutterLocation(DropIndicatorOverlayInterface::DropLocation locatio
|
||||
}
|
||||
}
|
||||
|
||||
bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
|
||||
bool DropArea::drop(WindowBeingDragged *droppedWindow, QPoint globalPos)
|
||||
{
|
||||
if (droppedWindow == window()) {
|
||||
FloatingWindow *floatingWindow = droppedWindow ? droppedWindow->floatingWindow()
|
||||
: nullptr;
|
||||
|
||||
if (floatingWindow == window()) {
|
||||
qWarning() << "Refusing to drop onto itself"; // Doesn't happen
|
||||
return false;
|
||||
}
|
||||
@@ -197,15 +221,24 @@ bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
|
||||
qCDebug(dropping) << "DropArea::drop:" << droppedWindow;
|
||||
|
||||
hover(droppedWindow, globalPos);
|
||||
auto droploc = m_dropIndicatorOverlay->currentDropLocation();
|
||||
Frame *acceptingFrame = m_dropIndicatorOverlay->hoveredFrame();
|
||||
if (!(acceptingFrame || isOutterLocation(m_dropIndicatorOverlay->currentDropLocation()))) {
|
||||
qWarning() << "DropArea::drop: asserted with frame=" << acceptingFrame << "; Location=" << m_dropIndicatorOverlay->currentDropLocation();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
return drop(floatingWindow, acceptingFrame, droploc);
|
||||
}
|
||||
|
||||
bool DropArea::drop(FloatingWindow *droppedWindow, Frame *acceptingFrame,
|
||||
DropIndicatorOverlayInterface::DropLocation droploc)
|
||||
{
|
||||
bool result = true;
|
||||
const bool needToFocusNewlyDroppedWidgets = Config::self().flags() & Config::Flag_TitleBarIsFocusable;
|
||||
const DockWidgetBase::List droppedDockWidgets = needToFocusNewlyDroppedWidgets ? droppedWindow->multiSplitter()->dockWidgets()
|
||||
: DockWidgetBase::List(); // just so save some memory allocations for the case where this variable isn't used
|
||||
|
||||
auto droploc = m_dropIndicatorOverlay->currentDropLocation();
|
||||
switch (droploc) {
|
||||
case DropIndicatorOverlayInterface::DropLocation_Left:
|
||||
case DropIndicatorOverlayInterface::DropLocation_Top:
|
||||
@@ -221,7 +254,7 @@ bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
|
||||
break;
|
||||
case DropIndicatorOverlayInterface::DropLocation_Center:
|
||||
qCDebug(hovering) << "Tabbing" << droppedWindow << "into" << acceptingFrame;
|
||||
if (!validateAffinity(droppedWindow))
|
||||
if (!validateAffinity(droppedWindow, acceptingFrame))
|
||||
return false;
|
||||
acceptingFrame->addWidget(droppedWindow);
|
||||
break;
|
||||
@@ -232,9 +265,23 @@ bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
if (result) {
|
||||
// Window receiving the drop gets raised:
|
||||
raiseAndActivate();
|
||||
|
||||
if (needToFocusNewlyDroppedWidgets) {
|
||||
// Let's also focus the newly dropped dock widget
|
||||
if (droppedDockWidgets.size() > 0) {
|
||||
// If more than 1 was dropped, we only focus the first one
|
||||
Frame *frame = droppedDockWidgets.first()->frame();
|
||||
frame->FocusScope::focus(Qt::MouseFocusReason);
|
||||
} else {
|
||||
// Doesn't happen.
|
||||
qWarning() << Q_FUNC_INFO << "Nothing was dropped?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -253,7 +300,11 @@ bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location locat
|
||||
if (!validateAffinity(floatingWindow))
|
||||
return false;
|
||||
|
||||
const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
|
||||
addMultiSplitter(floatingWindow->dropArea(), location, relativeTo, DefaultSizeMode::FairButFloor);
|
||||
if (hadSingleFloatingFrame != hasSingleFloatingFrame())
|
||||
updateFloatingActions();
|
||||
|
||||
floatingWindow->scheduleDeleteLater();
|
||||
return true;
|
||||
} else {
|
||||
@@ -266,19 +317,24 @@ bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location locat
|
||||
|
||||
void DropArea::removeHover()
|
||||
{
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(nullptr);
|
||||
m_dropIndicatorOverlay->setWindowBeingDragged(false);
|
||||
m_dropIndicatorOverlay->setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool DropArea::validateAffinity(T *window) const
|
||||
bool DropArea::validateAffinity(T *window, Frame *acceptingFrame) const
|
||||
{
|
||||
if (!DockRegistry::self()->affinitiesMatch(window->affinities(), affinities())) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (acceptingFrame) {
|
||||
// We're dropping into another frame (as tabbed), so also check the affinity of the frame
|
||||
// not only of the main window, which might be more forgiving
|
||||
if (!DockRegistry::self()->affinitiesMatch(window->affinities(), acceptingFrame->affinities())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
#include "MultiSplitter_p.h"
|
||||
#include "DropIndicatorOverlayInterface_p.h"
|
||||
|
||||
class TestCommon;
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class Frame;
|
||||
class Draggable;
|
||||
class DropIndicatorOverlayInterface;
|
||||
struct WindowBeingDragged;
|
||||
|
||||
/**
|
||||
@@ -43,9 +44,9 @@ public:
|
||||
~DropArea();
|
||||
|
||||
void removeHover();
|
||||
void hover(FloatingWindow *floatingWindow, QPoint globalPos);
|
||||
bool drop(FloatingWindow *droppedWindow, QPoint globalPos);
|
||||
bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo);
|
||||
void hover(WindowBeingDragged *floatingWindow, QPoint globalPos);
|
||||
///@brief Called when a user drops a widget via DND
|
||||
bool drop(WindowBeingDragged *droppedWindow, QPoint globalPos);
|
||||
int numFrames() const;
|
||||
Frame::List frames() const;
|
||||
|
||||
@@ -55,17 +56,28 @@ public:
|
||||
|
||||
bool contains(DockWidgetBase *) const;
|
||||
|
||||
/// Returns whether this layout has a single dock widget which is floating
|
||||
/// Implies it's in a FloatingWindow and that it has only one dock widget
|
||||
bool hasSingleFloatingFrame() const;
|
||||
|
||||
QStringList affinities() const;
|
||||
void layoutParentContainerEqually(DockWidgetBase *);
|
||||
private:
|
||||
Q_DISABLE_COPY(DropArea)
|
||||
friend class Frame;
|
||||
friend class TestDocks;
|
||||
friend class ::TestCommon;
|
||||
friend class DropIndicatorOverlayInterface;
|
||||
friend class AnimatedIndicators;
|
||||
friend class FloatingWindow;
|
||||
|
||||
template <typename T>
|
||||
bool validateAffinity(T *) const;
|
||||
bool validateAffinity(T *, Frame *acceptingFrame = nullptr) const;
|
||||
bool drop(FloatingWindow *droppedWindow, Frame *acceptingFrame, DropIndicatorOverlayInterface::DropLocation);
|
||||
bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo);
|
||||
Frame *frameContainingPos(QPoint globalPos) const;
|
||||
void updateFloatingActions();
|
||||
|
||||
bool m_inDestructor = false;
|
||||
QString m_affinityName;
|
||||
DropIndicatorOverlayInterface *m_dropIndicatorOverlay = nullptr;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "Frame_p.h"
|
||||
#include "DropArea_p.h"
|
||||
#include "FloatingWindow_p.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
@@ -25,20 +24,20 @@ DropIndicatorOverlayInterface::DropIndicatorOverlayInterface(DropArea *dropArea)
|
||||
setObjectName(QStringLiteral("DropIndicatorOverlayInterface"));
|
||||
}
|
||||
|
||||
void DropIndicatorOverlayInterface::setWindowBeingDragged(const FloatingWindow *window)
|
||||
void DropIndicatorOverlayInterface::setWindowBeingDragged(bool is)
|
||||
{
|
||||
if (window == m_windowBeingDragged)
|
||||
if (is == m_draggedWindowIsHovering)
|
||||
return;
|
||||
|
||||
m_windowBeingDragged = window;
|
||||
if (m_windowBeingDragged) {
|
||||
m_draggedWindowIsHovering = is;
|
||||
if (is) {
|
||||
setGeometry(m_dropArea->QWidgetAdapter::rect());
|
||||
raise();
|
||||
} else {
|
||||
setHoveredFrame(nullptr);
|
||||
}
|
||||
|
||||
setVisible(m_windowBeingDragged != nullptr);
|
||||
setVisible(is);
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ void DropIndicatorOverlayInterface::setHoveredFrame(Frame *frame)
|
||||
|
||||
bool DropIndicatorOverlayInterface::isHovered() const
|
||||
{
|
||||
return m_windowBeingDragged != nullptr;
|
||||
return m_draggedWindowIsHovering;
|
||||
}
|
||||
|
||||
DropIndicatorOverlayInterface::DropLocation DropIndicatorOverlayInterface::currentDropLocation() const
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class FloatingWindow;
|
||||
class DropArea;
|
||||
|
||||
class DOCKS_EXPORT DropIndicatorOverlayInterface : public QWidgetAdapter
|
||||
@@ -47,7 +46,7 @@ public:
|
||||
|
||||
explicit DropIndicatorOverlayInterface(DropArea *dropArea);
|
||||
void setHoveredFrame(Frame *);
|
||||
void setWindowBeingDragged(const FloatingWindow *);
|
||||
void setWindowBeingDragged(bool);
|
||||
QRect hoveredFrameRect() const;
|
||||
bool isHovered() const;
|
||||
DropLocation currentDropLocation() const;
|
||||
@@ -77,8 +76,8 @@ protected:
|
||||
virtual void updateVisibility() {};
|
||||
|
||||
Frame *m_hoveredFrame = nullptr;
|
||||
QPointer<const FloatingWindow> m_windowBeingDragged;
|
||||
DropArea *const m_dropArea;
|
||||
bool m_draggedWindowIsHovering = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
#include <QCloseEvent>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QWindow>
|
||||
#include <QScopedValueRollback>
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
|
||||
#if defined(Q_OS_WIN)
|
||||
# include <Windows.h>
|
||||
# include <dwmapi.h>
|
||||
#endif
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
@@ -132,23 +134,30 @@ FloatingWindow::FloatingWindow(MainWindowBase *parent)
|
||||
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
|
||||
{
|
||||
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
|
||||
// On Windows with Qt 5.9 (and maybe earlier), 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();
|
||||
|
||||
if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
|
||||
m_nchittestFilter = new NCHITTESTEventFilter(this);
|
||||
qApp->installNativeEventFilter(m_nchittestFilter);
|
||||
|
||||
connect(windowHandle(), &QWindow::screenChanged, this, [this] {
|
||||
// Qt honors our frame hijacking usually... but when screen changes we must give it a nudge.
|
||||
// Otherwise what Qt thinks is the client area is not what Windows knows it is.
|
||||
// SetWindowPos() will trigger an NCCALCSIZE message, which Qt will intercept and take note of the margins we're using.
|
||||
SetWindowPos(HWND(winId()), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
});
|
||||
|
||||
// Show drop-shadow:
|
||||
MARGINS margins = {0, 0, 0, 1}; // arbitrary, just needs to be > 0 it seems
|
||||
DwmExtendFrameIntoClientArea(HWND(winId()), &margins);
|
||||
}
|
||||
#endif
|
||||
|
||||
DockRegistry::self()->registerNestedWindow(this);
|
||||
DockRegistry::self()->registerFloatingWindow(this);
|
||||
qCDebug(creation) << "FloatingWindow()" << this;
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
|
||||
# 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();
|
||||
|
||||
updateTitleBarVisibility();
|
||||
@@ -175,7 +184,7 @@ FloatingWindow::~FloatingWindow()
|
||||
disconnect(m_layoutDestroyedConnection);
|
||||
delete m_nchittestFilter;
|
||||
|
||||
DockRegistry::self()->unregisterNestedWindow(this);
|
||||
DockRegistry::self()->unregisterFloatingWindow(this);
|
||||
qCDebug(creation) << "~FloatingWindow";
|
||||
}
|
||||
|
||||
@@ -250,7 +259,7 @@ void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, bool preserveCent
|
||||
void FloatingWindow::scheduleDeleteLater()
|
||||
{
|
||||
m_deleteScheduled = true;
|
||||
DockRegistry::self()->unregisterNestedWindow(this);
|
||||
DockRegistry::self()->unregisterFloatingWindow(this);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
@@ -261,7 +270,7 @@ MultiSplitter *FloatingWindow::multiSplitter() const
|
||||
|
||||
bool FloatingWindow::isInDragArea(QPoint globalPoint) const
|
||||
{
|
||||
return dragRect().adjusted(8, 8, 0, 0).contains(globalPoint);
|
||||
return dragRect().contains(globalPoint);
|
||||
}
|
||||
|
||||
bool FloatingWindow::anyNonClosable() const
|
||||
@@ -318,6 +327,8 @@ void FloatingWindow::onFrameCountChanged(int count)
|
||||
scheduleDeleteLater();
|
||||
} else {
|
||||
updateTitleBarVisibility();
|
||||
if (count == 1) // if something was removed, then our single dock widget is floating, we need to check the QAction
|
||||
dropArea()->updateFloatingActions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,13 +342,15 @@ void FloatingWindow::onVisibleFrameCountChanged(int count)
|
||||
|
||||
void FloatingWindow::updateTitleBarVisibility()
|
||||
{
|
||||
if (m_updatingTitleBarVisibility)
|
||||
return; // Break recursion
|
||||
|
||||
QScopedValueRollback<bool> guard(m_updatingTitleBarVisibility, true);
|
||||
updateTitleAndIcon();
|
||||
|
||||
bool visible = true;
|
||||
|
||||
if (KDDockWidgets::usesNativeTitleBar()) {
|
||||
visible = false;
|
||||
} else {
|
||||
if (KDDockWidgets::usesClientTitleBar()) {
|
||||
const auto flags = Config::self().flags();
|
||||
if ((flags & Config::Flag_HideTitleBarWhenTabsVisible) && !(flags & Config::Flag_AlwaysTitleBarWhenFloating)) {
|
||||
if (hasSingleFrame()) {
|
||||
@@ -347,6 +360,8 @@ void FloatingWindow::updateTitleBarVisibility()
|
||||
|
||||
for (Frame *frame : frames())
|
||||
frame->updateTitleBarVisibility();
|
||||
} else {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
m_titleBar->setVisible(visible);
|
||||
|
||||
@@ -145,6 +145,7 @@ private:
|
||||
bool m_disableSetVisible = false;
|
||||
bool m_deleteScheduled = false;
|
||||
bool m_inDtor = false;
|
||||
bool m_updatingTitleBarVisibility = false;
|
||||
QMetaObject::Connection m_layoutDestroyedConnection;
|
||||
QAbstractNativeEventFilter *m_nchittestFilter = nullptr;
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
|
||||
qCDebug(creation) << "Frame" << ((void*)this) << s_dbg_numFrames;
|
||||
|
||||
connect(this, &Frame::currentDockWidgetChanged, this, &Frame::updateTitleAndIcon);
|
||||
setDropArea(qobject_cast<DropArea *>(QWidgetAdapter::parentWidget()));
|
||||
m_inCtor = false;
|
||||
}
|
||||
|
||||
@@ -93,6 +94,16 @@ void Frame::updateTitleAndIcon()
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::onDockWidgetTitleChanged()
|
||||
{
|
||||
updateTitleAndIcon();
|
||||
|
||||
if (!m_inCtor) { // don't call pure virtual in ctor
|
||||
if (auto dw = qobject_cast<DockWidgetBase*>(sender()))
|
||||
renameTab(indexOfDockWidget(dw), dw->title());
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::addWidget(DockWidgetBase *dockWidget, AddingOption addingOption)
|
||||
{
|
||||
insertWidget(dockWidget, dockWidgetCount(), addingOption); // append
|
||||
@@ -149,14 +160,14 @@ void Frame::insertWidget(DockWidgetBase *dockWidget, int index, AddingOption add
|
||||
}
|
||||
}
|
||||
|
||||
connect(dockWidget, &DockWidgetBase::titleChanged, this, &Frame::updateTitleAndIcon);
|
||||
connect(dockWidget, &DockWidgetBase::iconChanged, this, &Frame::updateTitleAndIcon);
|
||||
connect(dockWidget, &DockWidgetBase::titleChanged, this, &Frame::onDockWidgetTitleChanged);
|
||||
connect(dockWidget, &DockWidgetBase::iconChanged, this, &Frame::onDockWidgetTitleChanged);
|
||||
}
|
||||
|
||||
void Frame::removeWidget(DockWidgetBase *dw)
|
||||
{
|
||||
disconnect(dw, &DockWidgetBase::titleChanged, this, &Frame::updateTitleAndIcon);
|
||||
disconnect(dw, &DockWidgetBase::iconChanged, this, &Frame::updateTitleAndIcon);
|
||||
disconnect(dw, &DockWidgetBase::titleChanged, this, &Frame::onDockWidgetTitleChanged);
|
||||
disconnect(dw, &DockWidgetBase::iconChanged, this, &Frame::onDockWidgetTitleChanged);
|
||||
removeWidget_impl(dw);
|
||||
}
|
||||
|
||||
@@ -283,6 +294,13 @@ void Frame::updateTitleBarVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
void Frame::updateFloatingActions()
|
||||
{
|
||||
const QVector<DockWidgetBase *> widgets = dockWidgets();
|
||||
for (DockWidgetBase *dw : widgets)
|
||||
dw->updateFloatAction();
|
||||
}
|
||||
|
||||
bool Frame::containsMouse(QPoint globalPos) const
|
||||
{
|
||||
return QWidgetAdapter::rect().contains(KDDockWidgets::QWidgetAdapter::mapFromGlobal(globalPos));
|
||||
@@ -500,23 +518,26 @@ QStringList Frame::affinities() const
|
||||
|
||||
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_visibleWidgetCountChangedConnection);
|
||||
if (dt == m_dropArea)
|
||||
return;
|
||||
|
||||
m_dropArea = dt;
|
||||
|
||||
if (m_dropArea) {
|
||||
// We keep the connect result so we don't dereference m_dropArea at shutdown
|
||||
m_visibleWidgetCountChangedConnection = connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged,
|
||||
this, &Frame::updateTitleBarVisibility);
|
||||
updateTitleBarVisibility();
|
||||
if (wasInMainWindow != isInMainWindow())
|
||||
Q_EMIT isInMainWindowChanged();
|
||||
}
|
||||
qCDebug(docking) << "Frame::setDropArea dt=" << dt;
|
||||
const bool wasInMainWindow = dt && isInMainWindow();
|
||||
if (m_dropArea)
|
||||
disconnect(m_visibleWidgetCountChangedConnection);
|
||||
|
||||
m_dropArea = dt;
|
||||
|
||||
if (m_dropArea) {
|
||||
// We keep the connect result so we don't dereference m_dropArea at shutdown
|
||||
m_visibleWidgetCountChangedConnection = connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged,
|
||||
this, &Frame::updateTitleBarVisibility);
|
||||
updateTitleBarVisibility();
|
||||
if (wasInMainWindow != isInMainWindow())
|
||||
Q_EMIT isInMainWindowChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Frame::isTheOnlyFrame() const
|
||||
@@ -526,6 +547,11 @@ bool Frame::isTheOnlyFrame() const
|
||||
return m_dropArea && m_dropArea->numFrames() == 1;
|
||||
}
|
||||
|
||||
bool Frame::isOverlayed() const
|
||||
{
|
||||
return m_options & FrameOption_IsOverlayed;
|
||||
}
|
||||
|
||||
bool Frame::isFloating() const
|
||||
{
|
||||
if (isInMainWindow())
|
||||
|
||||
@@ -105,7 +105,9 @@ public:
|
||||
int dockWidgetCount() const;
|
||||
|
||||
void updateTitleAndIcon();
|
||||
void onDockWidgetTitleChanged();
|
||||
void updateTitleBarVisibility();
|
||||
void updateFloatingActions();
|
||||
bool containsMouse(QPoint globalPos) const;
|
||||
TitleBar *titleBar() const;
|
||||
TitleBar *actualTitleBar() const;
|
||||
@@ -120,6 +122,9 @@ public:
|
||||
|
||||
bool isTheOnlyFrame() const;
|
||||
|
||||
///@brief Returns whether this frame is overlayed on top of the MainWindow (auto-hide feature);
|
||||
bool isOverlayed() const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this frame is floating. A floating frame isn't attached to any other MainWindow,
|
||||
* and if it's attached to a FloatingWindow then it's considered floating if it's the only frame in that Window.
|
||||
@@ -239,6 +244,8 @@ Q_SIGNALS:
|
||||
void focusedWidgetChanged() override;
|
||||
protected:
|
||||
|
||||
virtual void renameTab(int index, const QString &) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the minimum size of the dock widgets.
|
||||
* This might be slightly smaller than Frame::minSize() due to the QTabWidget having some margins
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "multisplitter/Widget_qwidget.h"
|
||||
#include "DropArea_p.h"
|
||||
#include "WindowBeingDragged_p.h"
|
||||
|
||||
#include <QScopedValueRollback>
|
||||
|
||||
@@ -92,6 +93,9 @@ MainWindowBase *MultiSplitter::mainWindow() const
|
||||
// Note that if pw is a FloatingWindow then pw->parentWidget() can be a MainWindow too, as it's parented
|
||||
if (pw->objectName() == QLatin1String("MyCentralWidget"))
|
||||
return qobject_cast<MainWindowBase*>(pw->parentWidget());
|
||||
|
||||
if (auto mw = qobject_cast<MainWindowBase*>(pw))
|
||||
return mw;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -200,7 +204,17 @@ void MultiSplitter::addWidget(QWidgetOrQuick *w, Location location,
|
||||
} else if (auto ms = qobject_cast<MultiSplitter*>(w)) {
|
||||
newItem = ms->rootItem();
|
||||
newItem->setHostWidget(this);
|
||||
|
||||
if (FloatingWindow *fw = ms->floatingWindow()) {
|
||||
newItem->setSize_recursive(fw->size());
|
||||
}
|
||||
|
||||
delete ms;
|
||||
} else {
|
||||
// This doesn't happen but let's make coverity happy.
|
||||
// Tests will fail if this is ever printed.
|
||||
qWarning() << Q_FUNC_INFO << "Unknown widget added" << w;
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(!newItem->geometry().isEmpty());
|
||||
@@ -288,6 +302,16 @@ Layouting::Item *MultiSplitter::itemForFrame(const Frame *frame) const
|
||||
return m_rootItem->itemForWidget(frame);
|
||||
}
|
||||
|
||||
DockWidgetBase::List MultiSplitter::dockWidgets() const
|
||||
{
|
||||
DockWidgetBase::List dockWidgets;
|
||||
const Frame::List frames = this->frames();
|
||||
for (Frame *frame : frames)
|
||||
dockWidgets << frame->dockWidgets();
|
||||
|
||||
return dockWidgets;
|
||||
}
|
||||
|
||||
Frame::List MultiSplitter::framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const
|
||||
{
|
||||
if (auto frame = qobject_cast<Frame*>(frameOrMultiSplitter))
|
||||
@@ -413,17 +437,16 @@ Layouting::ItemContainer *MultiSplitter::rootItem() const
|
||||
return m_rootItem;
|
||||
}
|
||||
|
||||
QRect MultiSplitter::rectForDrop(const FloatingWindow *fw, Location location,
|
||||
QRect MultiSplitter::rectForDrop(const WindowBeingDragged *wbd, Location location,
|
||||
const Layouting::Item *relativeTo) const
|
||||
{
|
||||
Layouting::Item item(nullptr);
|
||||
if (!fw)
|
||||
if (!wbd)
|
||||
return {};
|
||||
|
||||
Layouting::ItemContainer *root = fw->dropArea()->rootItem();
|
||||
item.setSize(root->size());
|
||||
item.setMinSize(root->minSize());
|
||||
item.setMaxSizeHint(root->maxSizeHint());
|
||||
item.setSize(wbd->size().boundedTo(wbd->maxSize()));
|
||||
item.setMinSize(wbd->minSize());
|
||||
item.setMaxSizeHint(wbd->maxSize());
|
||||
|
||||
Layouting::ItemContainer *container = relativeTo ? relativeTo->parentContainer()
|
||||
: m_rootItem;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace KDDockWidgets {
|
||||
class MainWindowBase;
|
||||
class FloatingWindow;
|
||||
class Frame;
|
||||
|
||||
class WindowBeingDragged;
|
||||
|
||||
/**
|
||||
* MultiSplitter is simply a wrapper around Layouting::Item in which the hosted widgets are
|
||||
@@ -53,7 +53,7 @@ class Frame;
|
||||
* It supports adding a widget to the left/top/bottom/right of the whole MultiSplitter or adding
|
||||
* relative to a single widget.
|
||||
*/
|
||||
class DOCKS_EXPORT_FOR_UNIT_TESTS MultiSplitter
|
||||
class DOCKS_EXPORT MultiSplitter
|
||||
: public LayoutGuestWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
* Excludes the Separator thickness, result is actually smaller than what needed. In other words,
|
||||
* the result will be exactly the same as the geometry the widget will get.
|
||||
*/
|
||||
QRect rectForDrop(const FloatingWindow *, KDDockWidgets::Location location,
|
||||
QRect rectForDrop(const WindowBeingDragged *wbd, KDDockWidgets::Location location,
|
||||
const Layouting::Item *relativeTo) const;
|
||||
|
||||
bool deserialize(const LayoutSaver::MultiSplitter &);
|
||||
@@ -188,10 +188,13 @@ public:
|
||||
Layouting::Item *itemForFrame(const Frame *frame) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of Frame objects contained in this layout
|
||||
* @brief Returns this list of Frame objects contained in this layout
|
||||
*/
|
||||
QList<Frame*> frames() const;
|
||||
|
||||
/// @brief Returns the list of dock widgets contained in this layout
|
||||
QVector<DockWidgetBase*> dockWidgets() const;
|
||||
|
||||
/// @brief restores the dockwidget @p dw to its previous position
|
||||
void restorePlaceholder(DockWidgetBase *dw, Layouting::Item *, int tabIndex);
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ void Position::deserialize(const LayoutSaver::Position &lp)
|
||||
if (index == -1) {
|
||||
continue; // Skip
|
||||
} else {
|
||||
const auto floatingWindows = DockRegistry::self()->nestedwindows();
|
||||
const auto floatingWindows = DockRegistry::self()->floatingWindows();
|
||||
if (index >= 0 && index < floatingWindows.size()) {
|
||||
FloatingWindow *fw = floatingWindows.at(index);
|
||||
layout = fw->multiSplitter();
|
||||
@@ -170,7 +170,7 @@ LayoutSaver::Position Position::serialize() const
|
||||
p.isFloatingWindow = fw;
|
||||
|
||||
if (p.isFloatingWindow) {
|
||||
p.indexOfFloatingWindow = fw->beingDeleted() ? -1 : DockRegistry::self()->nestedwindows().indexOf(fw); // TODO: Remove once we stop using deleteLater with FloatingWindow. delete would be better
|
||||
p.indexOfFloatingWindow = fw->beingDeleted() ? -1 : DockRegistry::self()->floatingWindows().indexOf(fw); // TODO: Remove once we stop using deleteLater with FloatingWindow. delete would be better
|
||||
} else {
|
||||
p.mainWindowUniqueName = mainWindow->uniqueName();
|
||||
Q_ASSERT(!p.mainWindowUniqueName.isEmpty());
|
||||
|
||||
109
src/private/SideBar.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
#include "SideBar_p.h"
|
||||
#include "DockWidgetBase.h"
|
||||
#include "MainWindowBase.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
SideBar::SideBar(SideBarLocation location, MainWindowBase *parent)
|
||||
: QWidgetAdapter(parent)
|
||||
, m_mainWindow(parent)
|
||||
, m_location(location)
|
||||
, m_orientation((location == SideBarLocation::North || location == SideBarLocation::South) ? Qt::Horizontal
|
||||
: Qt::Vertical)
|
||||
{
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void SideBar::addDockWidget(DockWidgetBase *dw)
|
||||
{
|
||||
if (!dw)
|
||||
return;
|
||||
|
||||
if (m_dockWidgets.contains(dw)) {
|
||||
qWarning() << Q_FUNC_INFO << "Already contains dock widget" << dw->title();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(dw, &QObject::destroyed, this, &SideBar::onDockWidgetDestroyed);
|
||||
|
||||
m_dockWidgets << dw;
|
||||
addDockWidget_Impl(dw);
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void SideBar::removeDockWidget(DockWidgetBase *dw)
|
||||
{
|
||||
if (!m_dockWidgets.contains(dw)) {
|
||||
qWarning() << Q_FUNC_INFO << "Doesn't contain dock widget" << dw->title();
|
||||
return;
|
||||
}
|
||||
|
||||
disconnect(dw, &QObject::destroyed, this, &SideBar::onDockWidgetDestroyed);
|
||||
m_dockWidgets.removeOne(dw);
|
||||
removeDockWidget_Impl(dw);
|
||||
Q_EMIT dw->removedFromSideBar();
|
||||
updateSize();
|
||||
}
|
||||
|
||||
bool SideBar::contains(DockWidgetBase *dw) const
|
||||
{
|
||||
return m_dockWidgets.contains(dw);
|
||||
}
|
||||
|
||||
void SideBar::onButtonClicked(DockWidgetBase *dw)
|
||||
{
|
||||
toggleOverlay(dw);
|
||||
}
|
||||
|
||||
void SideBar::onDockWidgetDestroyed(QObject *dw)
|
||||
{
|
||||
removeDockWidget(static_cast<DockWidgetBase*>(dw));
|
||||
}
|
||||
|
||||
void SideBar::updateSize()
|
||||
{
|
||||
const int thickness = isEmpty() ? 0 : 30;
|
||||
if (isVertical()) {
|
||||
setFixedWidth(thickness);
|
||||
} else {
|
||||
setFixedHeight(thickness);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Orientation SideBar::orientation() const
|
||||
{
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
bool SideBar::isEmpty() const
|
||||
{
|
||||
return m_dockWidgets.isEmpty();
|
||||
}
|
||||
|
||||
SideBarLocation SideBar::location() const
|
||||
{
|
||||
return m_location;
|
||||
}
|
||||
|
||||
MainWindowBase *SideBar::mainWindow() const
|
||||
{
|
||||
return m_mainWindow;
|
||||
}
|
||||
|
||||
void SideBar::toggleOverlay(DockWidgetBase *dw)
|
||||
{
|
||||
m_mainWindow->toggleOverlayOnSideBar(dw);
|
||||
}
|
||||
70
src/private/SideBar_p.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
#ifndef KD_SIDEBAR_P_H
|
||||
#define KD_SIDEBAR_P_H
|
||||
|
||||
#include "docks_export.h"
|
||||
#include "KDDockWidgets.h"
|
||||
#include "QWidgetAdapter.h"
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class DockWidgetBase;
|
||||
class MainWindowBase;
|
||||
|
||||
class DOCKS_EXPORT SideBar : public QWidgetAdapter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SideBar(SideBarLocation, MainWindowBase *parent = nullptr);
|
||||
|
||||
void addDockWidget(DockWidgetBase *dw);
|
||||
void removeDockWidget(DockWidgetBase *dw);
|
||||
bool contains(DockWidgetBase *) const;
|
||||
|
||||
/// @brief Returns this side bar's orientation
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
/// @brief returns if this side bar has vertical orientation
|
||||
bool isVertical() const { return m_orientation == Qt::Vertical; }
|
||||
|
||||
/// @brief returns whether there's no dock widgets
|
||||
bool isEmpty() const;
|
||||
|
||||
/// @brief returns the sidebar's location in the main window
|
||||
SideBarLocation location() const;
|
||||
|
||||
/// @brief Returns the main window this side bar belongs to
|
||||
MainWindowBase *mainWindow() const;
|
||||
|
||||
/// @brief Toggles the dock widget overlay. Equivalent to the user clicking on the button.
|
||||
void toggleOverlay(DockWidgetBase *);
|
||||
|
||||
protected:
|
||||
virtual void addDockWidget_Impl(DockWidgetBase *dock) = 0;
|
||||
virtual void removeDockWidget_Impl(DockWidgetBase *dock) = 0;
|
||||
|
||||
void onButtonClicked(DockWidgetBase *dw);
|
||||
|
||||
private:
|
||||
void onDockWidgetDestroyed(QObject *dw);
|
||||
void updateSize();
|
||||
|
||||
MainWindowBase *const m_mainWindow;
|
||||
QVector<DockWidgetBase *> m_dockWidgets;
|
||||
const SideBarLocation m_location;
|
||||
const Qt::Orientation m_orientation;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,7 +16,10 @@
|
||||
#include "WindowBeingDragged_p.h"
|
||||
#include "Utils_p.h"
|
||||
#include "FrameworkWidgetFactory.h"
|
||||
#include "Config.h"
|
||||
#include "MainWindowBase.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QWindowStateChangeEvent>
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
@@ -26,14 +29,19 @@ TitleBar::TitleBar(Frame *parent)
|
||||
, Draggable(this)
|
||||
, m_frame(parent)
|
||||
, m_floatingWindow(nullptr)
|
||||
, m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
|
||||
{
|
||||
connect(m_frame, &Frame::numDockWidgetsChanged, this, &TitleBar::updateCloseButton);
|
||||
connect(m_frame, &Frame::isFocusedChanged, this, &TitleBar::isFocusedChanged);
|
||||
connect(m_frame, &Frame::isInMainWindowChanged, this, &TitleBar::updateAutoHideButton);
|
||||
|
||||
init();
|
||||
|
||||
if (Config::self().flags() & Config::Flag_TitleBarIsFocusable)
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
QTimer::singleShot(0, this, &TitleBar::updateAutoHideButton); // have to wait after the frame is constructed
|
||||
updateAutoHideButton();
|
||||
}
|
||||
|
||||
TitleBar::TitleBar(FloatingWindow *parent)
|
||||
@@ -41,6 +49,7 @@ TitleBar::TitleBar(FloatingWindow *parent)
|
||||
, Draggable(this)
|
||||
, m_frame(nullptr)
|
||||
, m_floatingWindow(parent)
|
||||
, m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
|
||||
{
|
||||
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateCloseButton);
|
||||
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateFloatButton);
|
||||
@@ -49,6 +58,7 @@ TitleBar::TitleBar(FloatingWindow *parent)
|
||||
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
|
||||
connect(m_floatingWindow, &FloatingWindow::activatedChanged , this, &TitleBar::isFocusedChanged);
|
||||
init();
|
||||
updateAutoHideButton(); // always hidden when we're in a FloatingWindow.
|
||||
}
|
||||
|
||||
void TitleBar::init()
|
||||
@@ -101,6 +111,11 @@ void TitleBar::focusInEvent(QFocusEvent *ev)
|
||||
m_frame->FocusScope::focus(ev->reason());
|
||||
}
|
||||
|
||||
bool TitleBar::isOverlayed() const
|
||||
{
|
||||
return m_frame && m_frame->isOverlayed();
|
||||
}
|
||||
|
||||
void TitleBar::setTitle(const QString &title)
|
||||
{
|
||||
if (title != m_title) {
|
||||
@@ -151,7 +166,7 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
|
||||
|
||||
QRect r = m_frame->QWidgetAdapter::geometry();
|
||||
qCDebug(hovering) << "TitleBar::makeWindow original geometry" << r;
|
||||
r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0))); // TODO: Remove static_cast if it compiles. Ambiguous base for now
|
||||
r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0)));
|
||||
|
||||
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
|
||||
floatingWindow->setSuggestedGeometry(r);
|
||||
@@ -178,6 +193,11 @@ bool TitleBar::supportsFloatingButton() const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Config::self().flags() & Config::Flag_TitleBarNoFloatButton) {
|
||||
// Was explicitly disabled
|
||||
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();
|
||||
@@ -199,6 +219,12 @@ bool TitleBar::supportsMinimizeButton() const
|
||||
return m_floatingWindow != nullptr;
|
||||
}
|
||||
|
||||
bool TitleBar::supportsAutoHideButton() const
|
||||
{
|
||||
// Only dock widgets docked into the MainWindow can minimize
|
||||
return m_supportsAutoHide && m_frame && m_frame->isInMainWindow();
|
||||
}
|
||||
|
||||
bool TitleBar::hasIcon() const
|
||||
{
|
||||
return !m_icon.isNull();
|
||||
@@ -235,7 +261,6 @@ void TitleBar::onCloseClicked()
|
||||
|
||||
bool TitleBar::isFloating() const
|
||||
{
|
||||
|
||||
if (m_floatingWindow)
|
||||
return m_floatingWindow->hasSingleDockWidget(); // Debatable! Maybe it's always floating.
|
||||
|
||||
@@ -316,3 +341,23 @@ void TitleBar::onMinimizeClicked()
|
||||
|
||||
m_floatingWindow->showMinimized();
|
||||
}
|
||||
|
||||
void TitleBar::onAutoHideClicked()
|
||||
{
|
||||
if (!m_frame) {
|
||||
// Doesn't happen
|
||||
qWarning() << Q_FUNC_INFO << "Minimize not supported on floating windows";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &dockwidgets = m_frame->dockWidgets();
|
||||
for (DockWidgetBase *dw : dockwidgets) {
|
||||
if (dw->isOverlayed()) {
|
||||
// restore
|
||||
MainWindowBase *mainWindow = dw->mainWindow();
|
||||
mainWindow->restoreFromSideBar(dw);
|
||||
} else {
|
||||
dw->moveToSideBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class QHBoxLayout;
|
||||
class QLabel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class TestCommon;
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
class DockWidgetBase;
|
||||
@@ -41,6 +43,7 @@ class DOCKS_EXPORT TitleBar : public QWidgetAdapter
|
||||
Q_PROPERTY(bool hasIcon READ hasIcon NOTIFY iconChanged)
|
||||
public:
|
||||
typedef QVector<TitleBar *> List;
|
||||
|
||||
explicit TitleBar(Frame *parent);
|
||||
explicit TitleBar(FloatingWindow *parent);
|
||||
~TitleBar() override;
|
||||
@@ -69,6 +72,9 @@ public:
|
||||
///@brief returns whether this title bar supports a minimize button
|
||||
bool supportsMinimizeButton() const;
|
||||
|
||||
///@brief returns whether this title bar supports the auto-hide button
|
||||
bool supportsAutoHideButton() const;
|
||||
|
||||
///@brief returns whether this title bar has an icon
|
||||
bool hasIcon() const;
|
||||
|
||||
@@ -97,15 +103,19 @@ Q_SIGNALS:
|
||||
void isFocusedChanged();
|
||||
|
||||
protected:
|
||||
|
||||
Q_INVOKABLE void onCloseClicked();
|
||||
Q_INVOKABLE void onFloatClicked();
|
||||
Q_INVOKABLE void onMaximizeClicked();
|
||||
Q_INVOKABLE void onMinimizeClicked();
|
||||
Q_INVOKABLE void toggleMaximized();
|
||||
Q_INVOKABLE void onAutoHideClicked();
|
||||
|
||||
virtual void updateFloatButton() {}
|
||||
virtual void updateMaximizeButton() {}
|
||||
|
||||
virtual void updateMinimizeButton() {}
|
||||
virtual void updateAutoHideButton() {}
|
||||
|
||||
// The following are needed for the unit-tests
|
||||
virtual bool isCloseButtonVisible() const { return true; }
|
||||
@@ -115,8 +125,11 @@ protected:
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override;
|
||||
|
||||
bool isOverlayed() const;
|
||||
|
||||
private:
|
||||
friend class TestDocks;
|
||||
friend class ::TestCommon;
|
||||
|
||||
void init();
|
||||
|
||||
@@ -126,6 +139,7 @@ private:
|
||||
|
||||
Frame *const m_frame;
|
||||
FloatingWindow *const m_floatingWindow;
|
||||
const bool m_supportsAutoHide;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
|
||||
namespace KDDockWidgets {
|
||||
|
||||
inline bool isWayland()
|
||||
{
|
||||
return qApp->platformName() == QLatin1String("wayland");
|
||||
}
|
||||
|
||||
inline bool isLeftButtonPressed()
|
||||
{
|
||||
return qApp->mouseButtons() & Qt::LeftButton;
|
||||
@@ -40,6 +45,17 @@ inline bool usesNativeTitleBar()
|
||||
return Config::self().flags() & Config::Flag_NativeTitleBar;
|
||||
}
|
||||
|
||||
inline bool usesClientTitleBar()
|
||||
{
|
||||
if (isWayland()) {
|
||||
// Wayland has both client and native title bars, due to limitations.
|
||||
return true;
|
||||
}
|
||||
|
||||
// All other platforms have either the OS native title bar or a Qt title bar (aka client title bar).
|
||||
return !usesNativeTitleBar();
|
||||
}
|
||||
|
||||
inline bool usesAeroSnapWithCustomDecos()
|
||||
{
|
||||
return Config::self().flags() & Config::Flag_AeroSnapWithClientDecos;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <QAbstractButton>
|
||||
#include <QLineEdit>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# include <QtGui/private/qhighdpiscaling_p.h>
|
||||
@@ -253,7 +254,8 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
|
||||
const QRect htCaptionRect = w->dragRect(); // The rect on which we allow for Windows to do Ba native drag
|
||||
if (globalPosQt.y() >= htCaptionRect.top() && globalPosQt.y() <= htCaptionRect.bottom() && globalPosQt.x() >= htCaptionRect.left() && globalPosQt.x() <= htCaptionRect.right()) {
|
||||
QWidget *hoveredWidget = qApp->widgetAt(globalPosQt);
|
||||
if (!qobject_cast<QAbstractButton*>(hoveredWidget)) {
|
||||
if (!qobject_cast<QAbstractButton*>(hoveredWidget) &&
|
||||
!qobject_cast<QLineEdit*>(hoveredWidget)) { // User might have a line edit on the toolbar. TODO: Not so elegant fix, we should make the user's tabbar implement some virtual method...
|
||||
// User clicked on the title bar, let's allow it, so we get Aero-Snap.
|
||||
*result = HTCAPTION;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,15 @@
|
||||
#include "DragController_p.h"
|
||||
#include "Logging_p.h"
|
||||
#include "Utils_p.h"
|
||||
#include "DropArea_p.h"
|
||||
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
static Draggable* bestDraggable(Draggable *draggable)
|
||||
{
|
||||
if (!draggable)
|
||||
return nullptr;
|
||||
|
||||
// When de detach a title bar it will get hidden and we only the title bar of the FloatingWindow is visible
|
||||
/// Apparently that causes problems with grabbing the mouse, so instead use a visible draggable.
|
||||
// grabbing mouse on an hidden window works usually, it's some edge case on Windows with MFC.
|
||||
@@ -45,6 +49,7 @@ static Draggable* bestDraggable(Draggable *draggable)
|
||||
WindowBeingDragged::WindowBeingDragged(FloatingWindow *fw, Draggable *draggable)
|
||||
: m_floatingWindow(fw)
|
||||
, m_draggable(bestDraggable(draggable)->asWidget())
|
||||
, m_affinities(fw->affinities())
|
||||
{
|
||||
init();
|
||||
|
||||
@@ -53,6 +58,17 @@ WindowBeingDragged::WindowBeingDragged(FloatingWindow *fw, Draggable *draggable)
|
||||
if (!qIsNaN(opacity) && !qFuzzyCompare(1.0, opacity))
|
||||
fw->setWindowOpacity(opacity);
|
||||
}
|
||||
#if DOCKS_DEVELOPER_MODE
|
||||
|
||||
// Just used by tests
|
||||
WindowBeingDragged::WindowBeingDragged(FloatingWindow *fw)
|
||||
: m_floatingWindow(fw)
|
||||
, m_draggable(nullptr)
|
||||
, m_affinities(fw->affinities())
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
WindowBeingDragged::~WindowBeingDragged()
|
||||
{
|
||||
@@ -82,3 +98,36 @@ void WindowBeingDragged::grabMouse(bool grab)
|
||||
else
|
||||
DragController::instance()->releaseMouse(m_draggable);
|
||||
}
|
||||
|
||||
QStringList WindowBeingDragged::affinities() const
|
||||
{
|
||||
return m_affinities;
|
||||
}
|
||||
|
||||
QSize WindowBeingDragged::size() const
|
||||
{
|
||||
if (m_floatingWindow)
|
||||
return m_floatingWindow->size();
|
||||
|
||||
return QSize();
|
||||
}
|
||||
|
||||
QSize WindowBeingDragged::minSize() const
|
||||
{
|
||||
if (m_floatingWindow) {
|
||||
Layouting::ItemContainer *root = m_floatingWindow->dropArea()->rootItem();
|
||||
return root->minSize();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QSize WindowBeingDragged::maxSize() const
|
||||
{
|
||||
if (m_floatingWindow) {
|
||||
Layouting::ItemContainer *root = m_floatingWindow->dropArea()->rootItem();
|
||||
return root->maxSizeHint();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ struct DOCKS_EXPORT_FOR_UNIT_TESTS WindowBeingDragged
|
||||
{
|
||||
public:
|
||||
explicit WindowBeingDragged(FloatingWindow *fw, Draggable *draggable);
|
||||
#if DOCKS_DEVELOPER_MODE
|
||||
// For tests.
|
||||
explicit WindowBeingDragged(FloatingWindow *fw);
|
||||
#endif
|
||||
|
||||
~WindowBeingDragged();
|
||||
void init();
|
||||
|
||||
@@ -34,10 +39,23 @@ public:
|
||||
///@brief grabs or releases the mouse
|
||||
void grabMouse(bool grab);
|
||||
|
||||
///@brief returns the affinities of the window being dragged
|
||||
QStringList affinities() const;
|
||||
|
||||
///@brief size of the window being dragged contents
|
||||
QSize size() const;
|
||||
|
||||
/// @brief returns the min-size of the window being dragged contents
|
||||
QSize minSize() const;
|
||||
|
||||
/// @brief returns the max-size of the window being dragged contents
|
||||
QSize maxSize() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(WindowBeingDragged)
|
||||
QPointer<FloatingWindow> m_floatingWindow;
|
||||
QPointer<QWidgetOrQuick> m_draggable;
|
||||
const QStringList m_affinities;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,11 @@ bool ClassicIndicators::outterIndicatorsVisible() const
|
||||
return m_outterIndicatorsVisible;
|
||||
}
|
||||
|
||||
bool ClassicIndicators::tabIndicatorVisible() const
|
||||
{
|
||||
return m_tabIndicatorVisible;
|
||||
}
|
||||
|
||||
bool ClassicIndicators::onResize(QSize)
|
||||
{
|
||||
m_indicatorWindow->resize(window()->size());
|
||||
@@ -83,15 +88,21 @@ void ClassicIndicators::updateVisibility()
|
||||
|
||||
void ClassicIndicators::updateIndicatorsVisibility(bool visible)
|
||||
{
|
||||
Frame *hoveredFrame = m_hoveredFrame;
|
||||
const bool isTheOnlyFrame = hoveredFrame && hoveredFrame->isTheOnlyFrame();
|
||||
const bool isTheOnlyFrame = m_hoveredFrame && m_hoveredFrame->isTheOnlyFrame();
|
||||
|
||||
m_innerIndicatorsVisible = visible && hoveredFrame;
|
||||
m_innerIndicatorsVisible = visible && m_hoveredFrame;
|
||||
|
||||
WindowBeingDragged *windowBeingDragged = DragController::instance()->windowBeingDragged();
|
||||
|
||||
// If there's only 1 frame in the layout, the outter indicators are redundant, as they do the same thing as the internal ones.
|
||||
// But there might be another window obscuring our target, so it's useful to show the outter indicators in this case
|
||||
m_outterIndicatorsVisible = visible && (!isTheOnlyFrame ||
|
||||
DockRegistry::self()->isProbablyObscured(hoveredFrame->window()->windowHandle(), DragController::instance()->windowBeingDragged()));
|
||||
DockRegistry::self()->isProbablyObscured(m_hoveredFrame->window()->windowHandle(), windowBeingDragged));
|
||||
|
||||
|
||||
// Only allow to dock to center if the affinities match
|
||||
m_tabIndicatorVisible = m_innerIndicatorsVisible && windowBeingDragged &&
|
||||
DockRegistry::self()->affinitiesMatch(m_hoveredFrame->affinities(), windowBeingDragged->affinities());
|
||||
|
||||
Q_EMIT innerIndicatorsVisibleChanged();
|
||||
Q_EMIT outterIndicatorsVisibleChanged();
|
||||
@@ -152,7 +163,7 @@ void ClassicIndicators::setDropLocation(ClassicIndicators::DropLocation location
|
||||
case DropLocation_Bottom:
|
||||
if (!m_hoveredFrame) {
|
||||
qWarning() << "ClassicIndicators::setCurrentDropLocation: frame is null. location=" << location
|
||||
<< "; windowBeingDragged=" << m_windowBeingDragged
|
||||
<< "; isHovered=" << isHovered()
|
||||
<< "; dropArea->widgets=" << m_dropArea->items();
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
@@ -168,7 +179,9 @@ void ClassicIndicators::setDropLocation(ClassicIndicators::DropLocation location
|
||||
break;
|
||||
}
|
||||
|
||||
QRect rect = m_dropArea->rectForDrop(m_windowBeingDragged, multisplitterLocation,
|
||||
auto windowBeingDragged = DragController::instance()->windowBeingDragged();
|
||||
|
||||
QRect rect = m_dropArea->rectForDrop(windowBeingDragged, multisplitterLocation,
|
||||
m_dropArea->itemForFrame(relativeToFrame));
|
||||
|
||||
m_rubberBand->setGeometry(rect);
|
||||
|
||||
@@ -176,12 +176,14 @@ void IndicatorWindow::resizeEvent(QResizeEvent *ev)
|
||||
|
||||
void IndicatorWindow::updateIndicatorVisibility()
|
||||
{
|
||||
for (Indicator *indicator : { m_center, m_left, m_right, m_bottom, m_top })
|
||||
for (Indicator *indicator : { m_left, m_right, m_bottom, m_top })
|
||||
indicator->setVisible(classicIndicators->innerIndicatorsVisible());
|
||||
|
||||
for (Indicator *indicator : { m_outterTop, m_outterLeft, m_outterRight, m_outterBottom })
|
||||
indicator->setVisible(classicIndicators->outterIndicatorsVisible());
|
||||
|
||||
m_center->setVisible(classicIndicators->tabIndicatorVisible());
|
||||
|
||||
updateMask();
|
||||
}
|
||||
|
||||
@@ -239,8 +241,9 @@ IndicatorWindow::IndicatorWindow(KDDockWidgets::ClassicIndicators *classicIndica
|
||||
: QQuickView()
|
||||
, m_classicIndicators(classicIndicators)
|
||||
{
|
||||
setFlags(flags() | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool);
|
||||
setColor(Qt::transparent);
|
||||
setFlags(flags() | Qt::FramelessWindowHint);
|
||||
|
||||
rootContext()->setContextProperty(QStringLiteral("_window"), QVariant::fromValue<QObject*>(this));
|
||||
setSource(QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/ClassicIndicatorsOverlay.qml")));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public:
|
||||
bool innerIndicatorsVisible() const;
|
||||
bool outterIndicatorsVisible() const;
|
||||
|
||||
// The tab/center indicator
|
||||
bool tabIndicatorVisible() const;
|
||||
|
||||
protected:
|
||||
bool onResize(QSize newSize) override;
|
||||
void updateVisibility() override;
|
||||
@@ -53,6 +56,7 @@ private:
|
||||
IndicatorWindow *const m_indicatorWindow;
|
||||
bool m_innerIndicatorsVisible = false;
|
||||
bool m_outterIndicatorsVisible = false;
|
||||
bool m_tabIndicatorVisible = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ set(MULTISPLITTER_SRCS_QTQUICK
|
||||
)
|
||||
|
||||
option(BUILD_MULTISPLITTER_QTWIDGETS_FRONTEND "Build support for QtWidgets" ON)
|
||||
if (OPTION_QTQUICK)
|
||||
if (${PROJECT_NAME}_QTQUICK)
|
||||
# We're building the layouting engine as part of KDDW. If KDDW has set to build with QtQuick
|
||||
# then so does the layouting engine
|
||||
SET(BUILD_MULTISPLITTER_QTQUICK_FRONTEND ON)
|
||||
@@ -79,7 +79,7 @@ else()
|
||||
target_compile_definitions(kddockwidgets_multisplitter PRIVATE BUILDING_MULTISPLITTER_LIBRARY)
|
||||
endif()
|
||||
|
||||
if(OPTION_DEVELOPER_MODE)
|
||||
if(${PROJECT_NAME}_DEVELOPER_MODE)
|
||||
# Under developer mode since kddw might be a sub-folder of a project setting a different value for QT_DISABLE_DEPRECATED_BEFORE
|
||||
target_compile_definitions(kddockwidgets_multisplitter PRIVATE QT_DISABLE_DEPRECATED_BEFORE=0x060000)
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ QObject *Item::guestAsQObject() const
|
||||
}
|
||||
|
||||
void Item::setGuestWidget(Widget *guest)
|
||||
{
|
||||
{
|
||||
Q_ASSERT(!guest || !m_guest);
|
||||
QObject *newWidget = guest ? guest->asQObject() : nullptr;
|
||||
QObject *oldWidget = guestAsQObject();
|
||||
@@ -279,7 +279,12 @@ QObject *Item::host() const
|
||||
|
||||
void Item::restore(Widget *guest)
|
||||
{
|
||||
Q_ASSERT(!isVisible() && !guestAsQObject());
|
||||
if (isVisible() || guestAsQObject()) {
|
||||
qWarning() << Q_FUNC_INFO << "Hitting assert. visible="
|
||||
<< isVisible() << "; guest=" << guestAsQObject();
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
if (isContainer()) {
|
||||
qWarning() << Q_FUNC_INFO << "Containers can't be restored";
|
||||
} else {
|
||||
@@ -634,9 +639,14 @@ bool Item::checkSanity()
|
||||
|
||||
if (m_guest) {
|
||||
if (m_guest->parent() != hostWidget()->asQObject()) {
|
||||
qWarning() << Q_FUNC_INFO << "Unexpected parent for our guest"
|
||||
<< m_guest->parent() << "; host=" << hostWidget()
|
||||
<< m_guest->asQObject() << this;
|
||||
if (root())
|
||||
root()->dumpLayout();
|
||||
qWarning() << Q_FUNC_INFO << "Unexpected parent for our guest. guest.parent="
|
||||
<< m_guest->parent() << "; host=" << hostWidget()->asQObject()
|
||||
<< "; guest.asObj=" << m_guest->asQObject()
|
||||
<< "; this=" << this
|
||||
<< "; item.parentContainer=" << parentContainer()
|
||||
<< "; item.root.parent=" << (root() ? root()->parent() : nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -660,13 +670,6 @@ bool Item::checkSanity()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (!isVisible()) {
|
||||
if (m_guest && m_guest->isVisible()) {
|
||||
qWarning() << Q_FUNC_INFO << "Item is not visible but guest is visible";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -843,6 +846,75 @@ bool Item::isRoot() const
|
||||
return m_parent == nullptr;
|
||||
}
|
||||
|
||||
Item::LayoutBorderLocations Item::adjacentLayoutBorders() const
|
||||
{
|
||||
if (isRoot()) {
|
||||
return Item::LayoutBorderLocation_All;
|
||||
}
|
||||
|
||||
ItemContainer *c = parentContainer();
|
||||
if (!c)
|
||||
return Item::LayoutBorderLocation_None;
|
||||
|
||||
const int indexInParent = c->indexOfVisibleChild(this);
|
||||
const int numVisibleChildren = c->numVisibleChildren();
|
||||
const bool isFirst = indexInParent == 0;
|
||||
const bool isLast = indexInParent == numVisibleChildren - 1;
|
||||
if (indexInParent == -1)
|
||||
return Item::LayoutBorderLocation_None;
|
||||
|
||||
Item::LayoutBorderLocations locations = Item::LayoutBorderLocation_None;
|
||||
if (c->isRoot()) {
|
||||
if (c->isVertical()) {
|
||||
locations |= Item::LayoutBorderLocation_West;
|
||||
locations |= Item::LayoutBorderLocation_East;
|
||||
|
||||
if (isFirst)
|
||||
locations |= Item::LayoutBorderLocation_North;
|
||||
if (isLast)
|
||||
locations |= Item::LayoutBorderLocation_South;
|
||||
} else {
|
||||
locations |= Item::LayoutBorderLocation_North;
|
||||
locations |= Item::LayoutBorderLocation_South;
|
||||
|
||||
if (isFirst)
|
||||
locations |= Item::LayoutBorderLocation_West;
|
||||
if (isLast)
|
||||
locations |= Item::LayoutBorderLocation_East;
|
||||
}
|
||||
} else {
|
||||
const Item::LayoutBorderLocations parentBorders = c->adjacentLayoutBorders();
|
||||
if (c->isVertical()) {
|
||||
if (parentBorders & Item::LayoutBorderLocation_West)
|
||||
locations |= Item::LayoutBorderLocation_West;
|
||||
|
||||
if (parentBorders & Item::LayoutBorderLocation_East)
|
||||
locations |= Item::LayoutBorderLocation_East;
|
||||
|
||||
if (isFirst && (parentBorders & Item::LayoutBorderLocation_North))
|
||||
locations |= Item::LayoutBorderLocation_North;
|
||||
|
||||
if (isLast && (parentBorders & Item::LayoutBorderLocation_South))
|
||||
locations |= Item::LayoutBorderLocation_South;
|
||||
|
||||
} else {
|
||||
if (parentBorders & Item::LayoutBorderLocation_North)
|
||||
locations |= Item::LayoutBorderLocation_North;
|
||||
|
||||
if (parentBorders & Item::LayoutBorderLocation_South)
|
||||
locations |= Item::LayoutBorderLocation_South;
|
||||
|
||||
if (isFirst && (parentBorders & Item::LayoutBorderLocation_West))
|
||||
locations |= Item::LayoutBorderLocation_West;
|
||||
|
||||
if (isLast && (parentBorders & Item::LayoutBorderLocation_East))
|
||||
locations |= Item::LayoutBorderLocation_East;
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
int Item::visibleCount_recursive() const
|
||||
{
|
||||
return isVisible() ? 1 : 0;
|
||||
@@ -856,6 +928,12 @@ struct ItemContainer::Private
|
||||
(void) Config::self(); // Ensure Config ctor runs, as it registers qml types
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
qDeleteAll(m_separators);
|
||||
m_separators.clear();
|
||||
}
|
||||
|
||||
int defaultLengthFor(Item *item, DefaultSizeMode) const;
|
||||
bool isOverflowing() const;
|
||||
void relayoutIfNeeded();
|
||||
@@ -3133,8 +3211,8 @@ void ItemContainer::simplify()
|
||||
childContainer->simplify(); // recurse down the hierarchy
|
||||
|
||||
if (childContainer->orientation() == d->m_orientation || childContainer->d->m_children.size() == 1) {
|
||||
// This sub-container is reduntant, as it has the same orientation as its parent
|
||||
// Canibalize it.
|
||||
// This sub-container is redundant, as it has the same orientation as its parent
|
||||
// Cannibalize it.
|
||||
for (Item *child2 : childContainer->childItems()) {
|
||||
child2->setParentContainer(this);
|
||||
newChildren.push_back(child2);
|
||||
@@ -3270,7 +3348,7 @@ void ItemContainer::fillFromVariantMap(const QVariantMap &map,
|
||||
|
||||
for (const QVariant &childV : childrenV) {
|
||||
const QVariantMap childMap = childV.toMap();
|
||||
const bool isContainer = childMap[QStringLiteral("isContainer")].toBool();
|
||||
const bool isContainer = childMap.value(QStringLiteral("isContainer")).toBool();
|
||||
Item *child = isContainer ? new ItemContainer(hostWidget(), this)
|
||||
: new Item(hostWidget(), this);
|
||||
child->fillFromVariantMap(childMap, widgets);
|
||||
|
||||
@@ -229,6 +229,19 @@ public:
|
||||
};
|
||||
Q_ENUM(Location)
|
||||
|
||||
enum LayoutBorderLocation {
|
||||
LayoutBorderLocation_None = 0,
|
||||
LayoutBorderLocation_North = 1,
|
||||
LayoutBorderLocation_East = 2,
|
||||
LayoutBorderLocation_West = 4,
|
||||
LayoutBorderLocation_South = 8,
|
||||
LayoutBorderLocation_All = LayoutBorderLocation_North | LayoutBorderLocation_East |
|
||||
LayoutBorderLocation_West | LayoutBorderLocation_South,
|
||||
LayoutBorderLocation_Verticals = LayoutBorderLocation_West | LayoutBorderLocation_East,
|
||||
LayoutBorderLocation_Horizontals = LayoutBorderLocation_North | LayoutBorderLocation_South,
|
||||
};
|
||||
Q_DECLARE_FLAGS(LayoutBorderLocations, LayoutBorderLocation)
|
||||
|
||||
enum AddingOption {
|
||||
AddingOption_None = 0, ///> No option set
|
||||
AddingOption_StartHidden ///< Don't show the dock widget when adding it
|
||||
@@ -249,6 +262,10 @@ public:
|
||||
|
||||
bool isRoot() const;
|
||||
|
||||
///@brief Returns whether the item is touching the layout's borders.
|
||||
///Returns Location_None if it's not touching a border.
|
||||
LayoutBorderLocations adjacentLayoutBorders() const;
|
||||
|
||||
virtual int visibleCount_recursive() const;
|
||||
virtual void insertItem(Item *item, Location,
|
||||
DefaultSizeMode defaultSizeMode = DefaultSizeMode::Fair,
|
||||
@@ -400,11 +417,16 @@ public:
|
||||
QVariantMap toVariantMap() const override;
|
||||
void fillFromVariantMap(const QVariantMap &map, const QHash<QString, Widget *> &widgets) override;
|
||||
void clear();
|
||||
private:
|
||||
bool isEmpty() const;
|
||||
bool hasOrientation() const;
|
||||
Qt::Orientation orientation() const;
|
||||
bool isVertical() const;
|
||||
bool isHorizontal() const;
|
||||
int numChildren() const;
|
||||
int numVisibleChildren() const;
|
||||
bool isEmpty() const;
|
||||
int length() const;
|
||||
QRect rect() const;
|
||||
private:
|
||||
bool hasOrientation() const;
|
||||
bool hasChildren() const;
|
||||
bool hasVisibleChildren(bool excludeBeingInserted = false) const;
|
||||
int indexOfVisibleChild(const Item *) const;
|
||||
@@ -420,8 +442,6 @@ private:
|
||||
bool hasSingleVisibleItem() const;
|
||||
void setChildren(const Item::List &children, Qt::Orientation o);
|
||||
void setOrientation(Qt::Orientation);
|
||||
int length() const;
|
||||
QRect rect() const;
|
||||
void updateChildPercentages();
|
||||
void updateChildPercentages_recursive();
|
||||
void updateWidgetGeometries() override;
|
||||
@@ -485,9 +505,6 @@ private:
|
||||
void setLength_recursive(int length, Qt::Orientation) override;
|
||||
void applyGeometries(const SizingInfo::List &sizes, ChildrenResizeStrategy = ChildrenResizeStrategy::Percentage);
|
||||
void applyPositions(const SizingInfo::List &sizes);
|
||||
Qt::Orientation orientation() const;
|
||||
bool isVertical() const;
|
||||
bool isHorizontal() const;
|
||||
|
||||
int indexOf(Separator *) const;
|
||||
bool isInSimplify() const;
|
||||
|
||||
@@ -25,6 +25,9 @@ using namespace Layouting;
|
||||
|
||||
Separator* Separator::s_separatorBeingDragged = nullptr;
|
||||
|
||||
/// @brief internal counter just for unit-tests
|
||||
static int s_numSeparators = 0;
|
||||
|
||||
struct Separator::Private
|
||||
{
|
||||
// Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise.
|
||||
@@ -46,10 +49,12 @@ struct Separator::Private
|
||||
Separator::Separator(Widget *hostWidget)
|
||||
: d(new Private(hostWidget))
|
||||
{
|
||||
s_numSeparators++;
|
||||
}
|
||||
|
||||
Separator::~Separator()
|
||||
{
|
||||
s_numSeparators--;
|
||||
delete d;
|
||||
if (isBeingDragged())
|
||||
s_separatorBeingDragged = nullptr;
|
||||
@@ -136,7 +141,7 @@ void Separator::onMouseMove(QPoint pos)
|
||||
|
||||
d->lastMoveDirection = positionToGoTo < position() ? Side1
|
||||
: (positionToGoTo > position() ? Side2
|
||||
: Side2); // Last case shouldn't happen though.
|
||||
: Side1); // Last case shouldn't happen though.
|
||||
|
||||
if (d->lazyResizeRubberBand)
|
||||
setLazyPosition(positionToGoTo);
|
||||
@@ -216,6 +221,11 @@ bool Separator::isResizing()
|
||||
return s_separatorBeingDragged != nullptr;
|
||||
}
|
||||
|
||||
int Separator::numSeparators()
|
||||
{
|
||||
return s_numSeparators;
|
||||
}
|
||||
|
||||
void Separator::setLazyPosition(int pos)
|
||||
{
|
||||
if (d->lazyPosition != pos) {
|
||||
|
||||
@@ -47,6 +47,10 @@ public:
|
||||
static bool isResizing();
|
||||
virtual Widget* asWidget() = 0;
|
||||
|
||||
/// @internal Just for the unit-tests.
|
||||
/// Returns the total amount of Separator() instances currently alive.
|
||||
static int numSeparators();
|
||||
|
||||
protected:
|
||||
explicit Separator(Widget *hostWidget);
|
||||
virtual Widget* createRubberBand(Widget *parent) { Q_UNUSED(parent); return nullptr; }
|
||||
|
||||
@@ -51,7 +51,10 @@ public:
|
||||
virtual void setLayoutItem(Item *) = 0;
|
||||
|
||||
// Not strickly necessary, but it's nice conveniance for kddw which is widget based.
|
||||
virtual QWidget *asQWidget() const { return nullptr; }
|
||||
virtual QWidget *asQWidget() const {
|
||||
Q_ASSERT(false); // Only wanted for QtWidgets. All other should not call this.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual QSize sizeHint() const { return {}; }
|
||||
virtual QSize minSize() const = 0;
|
||||
|
||||
@@ -45,8 +45,8 @@ void Widget_quick::setParent(Widget *parent)
|
||||
}
|
||||
|
||||
if (auto qquickitem = qobject_cast<QQuickItem*>(parent->asQObject())) {
|
||||
m_thisWidget->setParentItem(qquickitem);
|
||||
m_thisWidget->setParent(qquickitem);
|
||||
m_thisWidget->setParentItem(qquickitem);
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "parent is not a widget, you have a bug" << parent->asQObject();
|
||||
Q_ASSERT(false);
|
||||
|
||||
@@ -193,6 +193,7 @@ private Q_SLOTS:
|
||||
void tst_requestEqualSize();
|
||||
void tst_maxSizeHonouredWhenAnotherRemoved();
|
||||
void tst_simplify();
|
||||
void tst_adjacentLayoutBorders();
|
||||
};
|
||||
|
||||
class MyHostWidget : public QWidget
|
||||
@@ -206,9 +207,7 @@ class MyHostWidget : public QWidget
|
||||
s_testObject->m_hostWidgets << this;
|
||||
}
|
||||
|
||||
~MyHostWidget() {
|
||||
s_testObject->m_hostWidgets.removeOne(this);
|
||||
}
|
||||
~MyHostWidget() override;
|
||||
|
||||
void paintEvent(QPaintEvent *) override
|
||||
{
|
||||
@@ -217,6 +216,10 @@ class MyHostWidget : public QWidget
|
||||
}
|
||||
};
|
||||
|
||||
MyHostWidget::~MyHostWidget() {
|
||||
s_testObject->m_hostWidgets.removeOne(this);
|
||||
}
|
||||
|
||||
static bool serializeDeserializeTest(const std::unique_ptr<ItemContainer> &root)
|
||||
{
|
||||
// Serializes and deserializes a layout
|
||||
@@ -1835,8 +1838,51 @@ void TestMultiSplitter::tst_simplify()
|
||||
|
||||
root->simplify();
|
||||
|
||||
for (Item *item : root->childItems())
|
||||
for (Item *item : root->childItems()) {
|
||||
QVERIFY(!item->isContainer());
|
||||
}
|
||||
}
|
||||
|
||||
void TestMultiSplitter::tst_adjacentLayoutBorders()
|
||||
{
|
||||
auto root = createRoot();
|
||||
auto item1 = createItem();
|
||||
auto item2 = createItem();
|
||||
auto item3 = createItem();
|
||||
auto item4 = createItem();
|
||||
auto item5 = createItem();
|
||||
|
||||
root->insertItem(item1, Item::Location_OnTop);
|
||||
const int allBorders = int(Item::LayoutBorderLocation_All);
|
||||
|
||||
auto borders1 = item1->adjacentLayoutBorders();
|
||||
QCOMPARE(borders1, allBorders);
|
||||
root->insertItem(item2, Item::Location_OnBottom);
|
||||
|
||||
borders1 = item1->adjacentLayoutBorders();
|
||||
QCOMPARE(borders1, allBorders & ~Item::LayoutBorderLocation_South);
|
||||
|
||||
auto borders2 = item2->adjacentLayoutBorders();
|
||||
QCOMPARE(borders2, allBorders & ~Item::LayoutBorderLocation_North);
|
||||
|
||||
root->insertItem(item3, Item::Location_OnRight);
|
||||
|
||||
borders1 = item1->adjacentLayoutBorders();
|
||||
QCOMPARE(borders1, Item::LayoutBorderLocation_North | Item::LayoutBorderLocation_West);
|
||||
|
||||
borders2 = item2->adjacentLayoutBorders();
|
||||
QCOMPARE(borders2, Item::LayoutBorderLocation_South | Item::LayoutBorderLocation_West);
|
||||
|
||||
auto borders3 = item3->adjacentLayoutBorders();
|
||||
QCOMPARE(borders3, allBorders & ~(Item::LayoutBorderLocation_West));
|
||||
|
||||
item3->insertItem(item4, Item::Location_OnBottom);
|
||||
auto borders4 = item4->adjacentLayoutBorders();
|
||||
QCOMPARE(borders4, Item::LayoutBorderLocation_East | Item::LayoutBorderLocation_South);
|
||||
|
||||
root->insertItem(item5, Item::Location_OnRight);
|
||||
borders4 = item4->adjacentLayoutBorders();
|
||||
QCOMPARE(borders4, Item::LayoutBorderLocation_South);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
||||
@@ -44,6 +44,8 @@ DockWidgetQuick::DockWidgetQuick(const QString &name, Options options)
|
||||
: DockWidgetBase(name, options)
|
||||
, d(new Private(this))
|
||||
{
|
||||
// To mimic what QtWidgets does when creating a new QWidget.
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
DockWidgetQuick::~DockWidgetQuick()
|
||||
@@ -60,9 +62,15 @@ void DockWidgetQuick::setWidget(const QString &qmlFilename)
|
||||
auto adapter = new QWidgetAdapter(this);
|
||||
guest->setParentItem(adapter);
|
||||
guest->setParent(adapter);
|
||||
QWidgetAdapter::makeItemFillParent(adapter);
|
||||
|
||||
DockWidgetBase::setWidget(adapter);
|
||||
setWidget(adapter);
|
||||
}
|
||||
|
||||
void DockWidgetQuick::setWidget(QWidgetOrQuick *widget)
|
||||
{
|
||||
widget->QWidgetAdapter::setParent(this);
|
||||
QWidgetAdapter::makeItemFillParent(widget);
|
||||
DockWidgetBase::setWidget(widget);
|
||||
}
|
||||
|
||||
bool DockWidgetQuick::event(QEvent *e)
|
||||
@@ -74,7 +82,7 @@ bool DockWidgetQuick::event(QEvent *e)
|
||||
} else if (e->type() == QEvent::Hide) {
|
||||
onHidden(e->spontaneous());
|
||||
} else if (e->type() == QEvent::Close) {
|
||||
onClosed(static_cast<QCloseEvent*>(e));
|
||||
onCloseEvent(static_cast<QCloseEvent*>(e));
|
||||
}
|
||||
|
||||
return DockWidgetBase::event(e);
|
||||
|
||||
@@ -54,6 +54,9 @@ public:
|
||||
/// Similar to DockWidgetBase::setWidget(QQuickItem*)
|
||||
void setWidget(const QString &qmlFilename);
|
||||
|
||||
/// @reimp
|
||||
void setWidget(QWidgetOrQuick *widget) override;
|
||||
|
||||
protected:
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
|
||||
@@ -58,7 +58,14 @@ FloatingWindowQuick::FloatingWindowQuick(Frame *frame, MainWindowBase *parent)
|
||||
|
||||
FloatingWindowQuick::~FloatingWindowQuick()
|
||||
{
|
||||
m_quickWindow->deleteLater();
|
||||
QWidgetAdapter::setParent(nullptr);
|
||||
delete m_quickWindow;
|
||||
}
|
||||
|
||||
void FloatingWindowQuick::setGeometry(QRect geo)
|
||||
{
|
||||
parentItem()->setSize(geo.size());
|
||||
m_quickWindow->setGeometry(geo);
|
||||
}
|
||||
|
||||
QWindow *FloatingWindowQuick::candidateParentWindow() const
|
||||
@@ -78,13 +85,17 @@ void FloatingWindowQuick::init()
|
||||
}
|
||||
});
|
||||
|
||||
m_quickWindow->setResizeMode(QQuickView::SizeViewToRootObject);
|
||||
const QSize minSize(100, 100);
|
||||
m_quickWindow->resize(minSize);
|
||||
m_quickWindow->contentItem()->setSize(minSize);
|
||||
|
||||
|
||||
m_quickWindow->setTransientParent(candidateParentWindow());
|
||||
|
||||
QWidgetAdapter::setParent(m_quickWindow->contentItem());
|
||||
QWidgetAdapter::makeItemFillParent(this);
|
||||
|
||||
m_quickWindow->setResizeMode(QQuickView::SizeViewToRootObject);
|
||||
|
||||
QQuickItem *visualItem = createItem(Config::self().qmlEngine(), QStringLiteral("qrc:/kddockwidgets/private/quick/qml/FloatingWindow.qml"));
|
||||
Q_ASSERT(visualItem);
|
||||
visualItem->setParent(this);
|
||||
@@ -92,5 +103,4 @@ void FloatingWindowQuick::init()
|
||||
|
||||
m_quickWindow->setFlags(windowFlags());
|
||||
m_quickWindow->show();
|
||||
m_quickWindow->setGeometry(200, 200, 800, 800); // TODO: remove
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public:
|
||||
explicit FloatingWindowQuick(Frame *frame, MainWindowBase *parent = nullptr);
|
||||
~FloatingWindowQuick();
|
||||
|
||||
protected:
|
||||
void setGeometry(QRect) override;
|
||||
|
||||
private:
|
||||
QWindow *candidateParentWindow() const;
|
||||
void init();
|
||||
|
||||
@@ -30,13 +30,13 @@ FrameQuick::FrameQuick(QWidgetAdapter *parent, FrameOptions options)
|
||||
connect(m_dockWidgetModel, &DockWidgetModel::countChanged,
|
||||
this, &FrameQuick::onDockWidgetCountChanged);
|
||||
|
||||
auto component = new QQmlComponent(Config::self().qmlEngine(),
|
||||
QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/Frame.qml")));
|
||||
QQmlComponent component(Config::self().qmlEngine(),
|
||||
QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/Frame.qml")));
|
||||
|
||||
auto visualItem = static_cast<QQuickItem*>(component->create());
|
||||
auto visualItem = static_cast<QQuickItem*>(component.create());
|
||||
|
||||
if (!visualItem) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to create item" << component->errorString();
|
||||
qWarning() << Q_FUNC_INFO << "Failed to create item" << component.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,11 @@ int FrameQuick::dockWidgetCount_impl() const
|
||||
return m_dockWidgetModel->count();
|
||||
}
|
||||
|
||||
void FrameQuick::renameTab(int, const QString &)
|
||||
{
|
||||
// Not needed for QtQuick. Our model reacts to titleChanged()
|
||||
}
|
||||
|
||||
void FrameQuick::setStackLayout(QQuickItem *stackLayout)
|
||||
{
|
||||
if (m_stackLayout || !stackLayout) {
|
||||
@@ -165,7 +170,7 @@ QVariant DockWidgetModel::data(const QModelIndex &index, int role) const
|
||||
DockWidgetBase *DockWidgetModel::dockWidgetAt(int index) const
|
||||
{
|
||||
if (index < 0 || index >= m_dockWidgets.size()) {
|
||||
qWarning() << Q_FUNC_INFO << "Shouldn't happen" << index << m_dockWidgets.size();
|
||||
// Can happen. Benign.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ protected:
|
||||
DockWidgetBase *dockWidgetAt_impl(int index) const override;
|
||||
DockWidgetBase *currentDockWidget_impl() const override;
|
||||
int dockWidgetCount_impl() const override;
|
||||
|
||||
void renameTab(int index, const QString &) override;
|
||||
Q_INVOKABLE void setStackLayout(QQuickItem *);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@@ -17,9 +17,21 @@
|
||||
using namespace KDDockWidgets;
|
||||
|
||||
MainWindowQuick::MainWindowQuick(const QString &uniqueName, MainWindowOptions options,
|
||||
QWidgetAdapter *parent)
|
||||
QQuickItem *parent)
|
||||
: MainWindowBase(uniqueName, options, parent)
|
||||
{
|
||||
QWidgetAdapter::makeItemFillParent(this);
|
||||
QWidgetAdapter::makeItemFillParent(dropArea());
|
||||
}
|
||||
|
||||
SideBar *MainWindowQuick::sideBar(SideBarLocation) const
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "SideBar hasn't been implemented yet";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QMargins MainWindowQuick::centerWidgetMargins() const
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "SideBar hasn't been implemented yet";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ class DOCKS_EXPORT MainWindowQuick : public MainWindowBase
|
||||
public:
|
||||
explicit MainWindowQuick(const QString &uniqueName,
|
||||
MainWindowOptions options = MainWindowOption_HasCentralFrame,
|
||||
QWidgetAdapter *parent = nullptr);
|
||||
QQuickItem *parent = nullptr);
|
||||
|
||||
protected:
|
||||
SideBar *sideBar(SideBarLocation) const override;
|
||||
QMargins centerWidgetMargins() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ using namespace KDDockWidgets;
|
||||
|
||||
QWidgetAdapter::QWidgetAdapter(QQuickItem *parent, Qt::WindowFlags flags)
|
||||
: QQuickItem(parent)
|
||||
, m_requestedWindowFlags(flags)
|
||||
, m_windowFlags(flags)
|
||||
{
|
||||
this->setParent(parent); // also set parentItem
|
||||
|
||||
@@ -79,7 +79,7 @@ void QWidgetAdapter::itemChange(QQuickItem::ItemChange change, const QQuickItem:
|
||||
switch (change) {
|
||||
case QQuickItem::ItemParentHasChanged: {
|
||||
QEvent ev(QEvent::ParentChange);
|
||||
event(&ev);
|
||||
qApp->sendEvent(this, &ev); // Not calling event() directly, otherwise it would skip event filters
|
||||
Q_EMIT parentChanged();
|
||||
break;
|
||||
}
|
||||
@@ -93,6 +93,24 @@ void QWidgetAdapter::itemChange(QQuickItem::ItemChange change, const QQuickItem:
|
||||
}
|
||||
}
|
||||
|
||||
void QWidgetAdapter::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||
{
|
||||
// Send a few events manually, since QQuickItem doesn't do it for us.
|
||||
QQuickItem::geometryChanged(newGeometry, oldGeometry);
|
||||
|
||||
// Not calling event() directly, otherwise it would skip event filters
|
||||
|
||||
if (newGeometry.size() != oldGeometry.size()) {
|
||||
QEvent ev(QEvent::Resize);
|
||||
qApp->sendEvent(this, &ev);
|
||||
}
|
||||
|
||||
if (newGeometry.topLeft() != oldGeometry.topLeft()) {
|
||||
QEvent ev(QEvent::Move);
|
||||
qApp->sendEvent(this, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
void QWidgetAdapter::raise()
|
||||
{
|
||||
if (QWindow *w = windowHandle())
|
||||
@@ -141,8 +159,7 @@ void QWidgetAdapter::setGeometry(QRect rect)
|
||||
{
|
||||
setWidth(rect.width());
|
||||
setHeight(rect.height());
|
||||
setX(rect.x());
|
||||
setY(rect.y());
|
||||
move(rect.topLeft());
|
||||
}
|
||||
|
||||
void QWidgetAdapter::grabMouse()
|
||||
@@ -196,6 +213,12 @@ void QWidgetAdapter::showMaximized()
|
||||
w->showMaximized();
|
||||
}
|
||||
|
||||
void QWidgetAdapter::showMinimized()
|
||||
{
|
||||
if (QWindow *w = windowHandle())
|
||||
w->showMinimized();
|
||||
}
|
||||
|
||||
void QWidgetAdapter::showNormal()
|
||||
{
|
||||
if (QWindow *w = windowHandle())
|
||||
@@ -240,6 +263,27 @@ QPoint QWidgetAdapter::mapFromGlobal(QPoint pt) const
|
||||
return QQuickItem::mapFromGlobal(pt).toPoint();
|
||||
}
|
||||
|
||||
QPoint QWidgetAdapter::mapTo(const QQuickItem *parent, const QPoint &pos) const
|
||||
{
|
||||
if (!parent)
|
||||
return {};
|
||||
|
||||
return parent->mapFromGlobal(QQuickItem::mapToGlobal(pos)).toPoint();
|
||||
}
|
||||
|
||||
bool QWidgetAdapter::testAttribute(Qt::WidgetAttribute attr) const
|
||||
{
|
||||
return m_widgetAttributes & attr;
|
||||
}
|
||||
|
||||
void QWidgetAdapter::setAttribute(Qt::WidgetAttribute attr, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
m_widgetAttributes |= attr;
|
||||
else
|
||||
m_widgetAttributes &= ~attr;
|
||||
}
|
||||
|
||||
void QWidgetAdapter::setWindowTitle(const QString &title)
|
||||
{
|
||||
if (QWindow *window = windowHandle())
|
||||
@@ -267,10 +311,16 @@ QQuickItem *QWidgetAdapter::childAt(QPoint p) const
|
||||
return QQuickItem::childAt(p.x(), p.y());
|
||||
}
|
||||
|
||||
void QWidgetAdapter::move(QPoint pt)
|
||||
{
|
||||
move(pt.x(), pt.y());
|
||||
}
|
||||
|
||||
void QWidgetAdapter::move(int x, int y)
|
||||
{
|
||||
setX(x);
|
||||
setY(y);
|
||||
setAttribute(Qt::WA_Moved);
|
||||
}
|
||||
|
||||
void QWidgetAdapter::setParent(QQuickItem *p)
|
||||
@@ -302,10 +352,7 @@ QSize QWidgetAdapter::sizeHint() const
|
||||
|
||||
Qt::WindowFlags QWidgetAdapter::windowFlags() const
|
||||
{
|
||||
if (QWindow *w = windowHandle())
|
||||
return w->flags();
|
||||
|
||||
return m_requestedWindowFlags;
|
||||
return m_windowFlags;
|
||||
}
|
||||
|
||||
/** static */
|
||||
@@ -347,15 +394,10 @@ void QWidgetAdapter::makeItemFillParent(QQuickItem *item)
|
||||
|
||||
void QWidgetAdapter::setFlag(Qt::WindowType f, bool on)
|
||||
{
|
||||
if (QWindow *w = windowHandle()) {
|
||||
w->setFlag(f, on);
|
||||
if (on) {
|
||||
m_windowFlags |= f;
|
||||
} else {
|
||||
// When we create a QWindow we'll set these
|
||||
if (on) {
|
||||
m_requestedWindowFlags |= f;
|
||||
} else {
|
||||
m_requestedWindowFlags &= ~f;
|
||||
}
|
||||
m_windowFlags &= ~f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
int width() const { return int(QQuickItem::width()); }
|
||||
int height() const { return int(QQuickItem::height()); }
|
||||
|
||||
void setGeometry(QRect);
|
||||
virtual void setGeometry(QRect);
|
||||
QRect geometry() const;
|
||||
QRect rect() const;
|
||||
void show();
|
||||
@@ -115,6 +115,7 @@ public:
|
||||
bool isMaximized() const;
|
||||
bool isActiveWindow() const;
|
||||
void showMaximized();
|
||||
void showMinimized();
|
||||
void showNormal();
|
||||
|
||||
QWindow *windowHandle() const;
|
||||
@@ -122,13 +123,16 @@ public:
|
||||
QWidgetAdapter *parentWidget() const;
|
||||
QPoint mapToGlobal(QPoint pt) const;
|
||||
QPoint mapFromGlobal(QPoint) const;
|
||||
bool testAttribute(Qt::WidgetAttribute) { return false; }
|
||||
QPoint mapTo(const QQuickItem *parent, const QPoint &pos) const;
|
||||
bool testAttribute(Qt::WidgetAttribute) const;
|
||||
void setAttribute(Qt::WidgetAttribute, bool enabled = true);
|
||||
|
||||
void setWindowTitle(const QString &);
|
||||
void setWindowIcon(const QIcon &);
|
||||
void close();
|
||||
QQuickItem *childAt(QPoint) const;
|
||||
void move(int x, int y);
|
||||
void move(QPoint);
|
||||
|
||||
void setParent(QQuickItem*);
|
||||
void activateWindow();
|
||||
@@ -147,6 +151,7 @@ public:
|
||||
Q_SIGNALS:
|
||||
void parentChanged();
|
||||
protected:
|
||||
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||
void raiseAndActivate();
|
||||
virtual bool onResize(QSize newSize);
|
||||
virtual void onLayoutRequest();
|
||||
@@ -160,7 +165,8 @@ private:
|
||||
QSizePolicy m_sizePolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
QSize m_minimumSize = {KDDOCKWIDGETS_MIN_WIDTH, KDDOCKWIDGETS_MIN_HEIGHT};
|
||||
QSize m_maximumSize = {KDDOCKWIDGETS_MAX_WIDTH, KDDOCKWIDGETS_MAX_HEIGHT};
|
||||
Qt::WindowFlags m_requestedWindowFlags;
|
||||
Qt::WindowFlags m_windowFlags;
|
||||
int m_widgetAttributes = 0; // Qt::WidgetAttribute
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
void KDDockWidgets::registerQmlTypes()
|
||||
{
|
||||
qDebug() << "Registering types";
|
||||
qmlRegisterType<DropAreaWithCentralFrame>("com.kdab.dockwidgets", 1, 0, "DropAreaWithCentralFrame");
|
||||
qmlRegisterType<MainWindowWrapper>("com.kdab.dockwidgets", 1, 0, "MainWindow");
|
||||
|
||||
|
||||
@@ -23,44 +23,33 @@ TitleBarBase {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
TitleBarButton {
|
||||
id: floatButton
|
||||
color: "red"
|
||||
imageSource: "qrc:/img/dock-float.png"
|
||||
anchors {
|
||||
top: parent ? parent.top : undefined
|
||||
bottom: parent ? parent.bottom : undefined
|
||||
verticalCenter: parent ? parent.verticalCenter : undefined
|
||||
right: closeButton.left
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
width: height
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
titleBarCpp.onFloatClicked();
|
||||
}
|
||||
onClicked: {
|
||||
titleBarCpp.onFloatClicked();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
TitleBarButton {
|
||||
id: closeButton
|
||||
color: "red"
|
||||
imageSource: "qrc:/img/close.png"
|
||||
anchors {
|
||||
top: parent ? parent.top : undefined
|
||||
bottom: parent ? parent.bottom : undefined
|
||||
verticalCenter: parent ? parent.verticalCenter : undefined
|
||||
right: parent ? parent.right : undefined
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
leftMargin: 5
|
||||
}
|
||||
width: height
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
titleBarCpp.onCloseClicked();
|
||||
}
|
||||
onClicked: {
|
||||
titleBarCpp.onCloseClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
src/private/quick/qml/TitleBarButton.qml
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
This file is part of KDDockWidgets.
|
||||
|
||||
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Author: Sérgio Martins <sergio.martins@kdab.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
Contact KDAB at <info@kdab.com> for commercial licensing options.
|
||||
*/
|
||||
|
||||
import QtQuick 2.9
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
signal clicked()
|
||||
property alias imageSource: image.source
|
||||
|
||||
color: "transparent"
|
||||
height: 16
|
||||
width: 16
|
||||
|
||||
radius: 3
|
||||
border {
|
||||
color: "#666666"
|
||||
width: mouseArea.containsMouse ? 1 : 0
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.centerIn: parent
|
||||
anchors {
|
||||
verticalCenterOffset: 1
|
||||
horizontalCenterOffset: 1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||