Compare commits

...

392 Commits

Author SHA1 Message Date
Sergio Martins
f674aa27a9 Enable the Aero-snap paths on Windows 8
Even if it's not actually aero snap the native resize is quite good.
2022-04-28 17:47:20 +01:00
Sergio Martins
5f78110025 qtquick: Fix calling QWindow::setTitle()
For QtWidgets, it happens fine in FloatingWindow's constructor,
because we get the corresponding QWindow already. But for QtQuick
the QWindow is only created afterwards, so we have to delay
setting the title.
2021-04-29 17:21:03 +01:00
Sergio Martins
ca3c49f32d Remove a bogus setWindowTitle()
Setting a widget would change the top-level window title!
By luck people set the widget before docking, but doesn't have
to be that way.
2021-04-29 16:25:07 +01:00
Sergio Martins
def6a0a6b2 windows: Fix white line appearing at the top when using round corners
Don't call DwmExtendFrameIntoClientArea if we have round corners.
The client area needs to be rectangular
2021-04-23 12:23:03 +01:00
Sergio Martins
b0cb983830 qtquick: relax the warning about removing dock widget twice
can happen due to benign reentrancy
2021-03-10 13:38:22 +00:00
Sergio Martins
02515d8956 MDI: Fix dock widget size getting out of sync with the layout item
When resizing with mouse, update the layout item too.

Wasn't a big deal, as this is MDI, layout doesn't do much. But
last used geometries werent being saved
2021-03-09 20:30:41 +00:00
Sergio Martins
447b4337dd Add DockWidget::frameGeometry() 2021-03-09 17:55:36 +00:00
Sergio Martins
633daf124e Fix some warnings about shadowing methods 2021-03-09 15:43:36 +00:00
Sergio Martins
446560931b MDI: Fix docked widget geometries not being remembered when floating
We shouldn't move the Frame directly. Instead, do it through the
layout, which will then remember the last docked geometry
2021-03-07 16:56:37 +00:00
Sergio Martins
9fe11a7ad5 qtwidgets|MDI: Fix showing resize handles
if there was no frame being resized it would be nullptr, and hence
enter that block.

QtQuick not affected since it uses mouse areas to change the cursor
shape
2021-03-05 12:19:16 +00:00
Sergio Martins
b099994ac5 qtquick|MDI: Remove the QtQuick special case
Now behaves as QtWidgets
2021-03-05 12:05:19 +00:00
Sergio Martins
a8d56ef4e6 qtquick6: Fix build 2021-03-04 13:57:25 +00:00
Sergio Martins
1f8d212dee Qt6: Fix build
Types in properties can't be forward declared anymore
2021-03-04 13:27:58 +00:00
Sergio Martins
64ea87f4cc Add missing fwd header for Frame_p.h 2021-03-04 13:27:32 +00:00
Sergio Martins
e6fef433d5 Added DockWidgetQuick::frame() 2021-03-03 17:59:43 +00:00
Sergio Martins
2f212031b3 qtquick|MDI: Don't set a size smaller than its min 2021-03-03 17:03:06 +00:00
Sergio Martins
586ab710f6 qtquick: Expose the tabbar height property 2021-03-03 16:36:23 +00:00
Sergio Martins
d263ae649e Allow to configure the MDI popup threshold 2021-03-03 16:33:32 +00:00
Sergio Martins
c81ca45e41 qtquick|MDI: Improvements for resizing 2021-03-03 15:21:22 +00:00
Sergio Martins
9ce50fa305 qtquick: Minor refactoring, deal in CursorPosition 2021-03-03 14:59:24 +00:00
Sergio Martins
f0240deaeb qtquick: Use Q_NAMESPACE for the KDDockWidgets namespace 2021-03-03 14:40:24 +00:00
Sergio Martins
017f06dcfb qtquick|MDI: Improvements for the resize handler
More margin, fixed sizes of some handlers and fixed z order
2021-03-03 13:47:14 +00:00
Sergio Martins
54f17cb546 qtquick: Remove unneeded mouse area from frame
Just to simplify things. Let's just have the resize handlers.
A target can have many event sources.
2021-03-03 13:22:12 +00:00
Sergio Martins
f174ab6ebc qtquick: Create ResizeHandlerHelper.qml to save some code 2021-03-03 12:57:37 +00:00
Sergio Martins
263f936690 Fix dock widgets in MDI getting bogus sizes
Setting the Item's min-size would resize the guest while the
guest was being set
2021-03-03 12:20:40 +00:00
Sergio Martins
bd903a0353 qtquick: Fix Frame's minimum size
It wasn't accounting for the margins, and the "heightWhenVisible"
property was Nan, making the entire expression be 0
2021-03-03 10:08:37 +00:00
Sergio Martins
5040d3a6b6 qtquick: Allow to update the size constraints from QML 2021-03-03 10:02:45 +00:00
Sergio Martins
3684da776d Added DockWidgetQuick::frameVisualItem() 2021-03-03 09:27:27 +00:00
Sergio Martins
bc17c8e5b3 MDI: Don't active other resize handlers when resize in progress
When we're resizing a docked dock widget, the other dock widget's
resize handlers should be disabled
2021-03-02 22:21:12 +00:00
Sergio Martins
778c647782 qtquick: Register Frame 2021-03-02 22:14:07 +00:00
Sergio Martins
2a91f6279b qtquick: Minor code refactoring
Uses less code
2021-03-02 22:05:48 +00:00
Sergio Martins
4ef2ddbfec Add DockRegistry::frameInMDIResize()
Only emitted in a MDI layout. Emitted when one dockwidget is
being resized
2021-03-02 22:00:54 +00:00
Sergio Martins
ff06f0cc73 Fix build 2021-03-02 21:06:57 +00:00
Sergio Martins
a37ab34fae qtquick|MDI: When dragging don't trigger the resize drag handlers
MouseArea.enabled, even if false, will change the cursor shape.
So explicitly change the cursor to ArrowShape
2021-03-02 20:58:44 +00:00
Sergio Martins
cb2ffe66af qtquick|MDI: Fix resizing inner MDI dock widgets
It was flaky. The mouse move events came from global event filter
but the mouse press came from the Frame's mouse area. Sometimes the
QQuickWindow wouldn't deliver the press to the frame if it was done
outside, while the moves were changing the cursor shape since
we had a treshold

Now we guarantee that whenever the cursor changes the resize
will really happen
2021-03-02 14:40:42 +00:00
Sergio Martins
16b0770d72 qtquick: Fix minor qml warning 2021-03-02 14:31:15 +00:00
Sergio Martins
3fe44ee735 qtquick: Enable transparency for the floating window
You can now have windows with round corners
2021-03-02 12:19:51 +00:00
Sergio Martins
40d1a72300 qtquick: Fix QtQuick min-sizes not working when wrapped 2021-03-02 11:02:15 +00:00
Sergio Martins
4d428d42f6 Added DockWidgetBase::setMDIZ(int z)
Sets the Z order of the dockwidget within the MDI layout
2021-03-02 10:43:07 +00:00
Sergio Martins
7e9896263b Update .gitignore 2021-03-02 10:22:57 +00:00
Sergio Martins
cf0665c9b6 Added DockWidgetBase::setMDISize()
Only for dock widgets in MDI layout, where they are freely resizable
2021-03-02 10:21:29 +00:00
Sergio Martins
93c011c69d Added DockWidgetBase::setMDIPosition(QPoint pos)
Moves the window within the layout.
Only applies to MDI layouts.
2021-03-01 23:41:53 +00:00
Sergio Martins
f50c0b9d06 qtquick: Fix typo 2021-03-01 23:24:29 +00:00
Sergio Martins
6fc79d942b qtquick: add a way to disable the default mouse redirectors
So user can use his own
2021-03-01 23:23:04 +00:00
Sergio Martins
d01228d9ff Fixes cases where actualTitleBarChanged() wasn't emitted 2021-03-01 23:02:56 +00:00
Sergio Martins
b58d766d54 qtquick: Add Q_INVOKABLE to DockWidgetBase::setFloating() 2021-03-01 22:38:23 +00:00
Sergio Martins
79cbb96a38 Remove some unneeded qDebug 2021-03-01 22:34:15 +00:00
Sergio Martins
aabaeacb81 Improve a warning 2021-03-01 22:30:31 +00:00
Sergio Martins
3ab3e6d41b qtquick: Don't allow to have two redirectors for the same source
One event source can only have one redirector
2021-03-01 22:23:46 +00:00
Sergio Martins
f2f4afd0bb qtquick: Allow the user to customize the titlebar margins 2021-03-01 20:50:46 +00:00
Sergio Martins
0d62c43b9e qtquick: keep the original parent of the guest around
For debug purposes
2021-03-01 19:58:00 +00:00
Sergio Martins
1614d5702c Allow to call DockWidget::setWidget(nullptr)
In case you need to change ownership
2021-03-01 19:56:46 +00:00
Sergio Martins
610b9542e9 qtquick: Expose the event source for debug purposes 2021-03-01 17:13:54 +00:00
Sergio Martins
946f32a274 qtquick: debug++ 2021-03-01 17:12:09 +00:00
Sergio Martins
c7a6bc015e qtquick: Minor performance optimization
Only check the properties for mouse events
2021-03-01 16:54:32 +00:00
Sergio Martins
19fd1635e6 Remove userType() from public API for now
If added, will need to be through overloads, to not break source
compat. Will add them once there's a need.
2021-03-01 16:34:22 +00:00
Sergio Martins
74b034a195 Merge branch '1.3'
Fixes #172
2021-03-01 15:48:45 +00:00
David Faure
581139d099 SideBarButton: use color roles from the widget palette 2021-03-01 16:42:59 +01:00
Sergio Martins
e5492ea906 qtquick: Allow to override the mouse redirector 2021-03-01 14:50:14 +00:00
Sergio Martins
623133a0dc qtquick: Add DockWidgetQuick::actualTitleBar()
It's a pointer o either Frame::titleBar() or FloatingWindow::titleBar().
It's the same as Frame::actualTitleBar().
2021-03-01 14:35:14 +00:00
Sergio Martins
6baa8f2dbb qtquicl: Fix invisible tab bar eating mouse events 2021-03-01 14:19:39 +00:00
Sergio Martins
d2739d7218 qtquick: disable tabbar mouse area if tabbar hidden 2021-03-01 13:15:30 +00:00
Sergio Martins
5702101935 Add a Q_PROPERTY for Frame::actualTitleBar() 2021-03-01 12:50:25 +00:00
Sergio Martins
35d45a7523 qtquick: Fix some places which weren't forwarding userType 2021-02-28 20:44:19 +00:00
Sergio Martins
e37c1f0e8c qtquick: Pass the userType all the way to titleBarFilename() 2021-02-28 20:33:46 +00:00
Sergio Martins
ac0d35ef12 Fix clazy warning 2021-02-28 19:03:16 +00:00
Sergio Martins
15e63381a9 Added DockWidgetBase::setUserType(int)
This is an opaque value which KDDW won't try to interpret or do
anything with it other than forward it to the FrameworkWidgetFactory.

Which if specialized by the user he can have different title bars
for different said "dock widget types"
2021-02-28 18:57:35 +00:00
Sergio Martins
aa49ddacd5 qtquick: Allow the user to change the contents margin for Frame 2021-02-28 18:13:55 +00:00
Sergio Martins
f68e876bcb qtquick: Make the setWidget(QQuickItem) overload also fill parent 2021-02-28 16:05:44 +00:00
Sergio Martins
6b82ded399 qtquick: Allow derived FloatingWindow to override margins 2021-02-28 16:04:28 +00:00
Sergio Martins
000e67a09e qtquick: Register DockWidgetBase 2021-02-28 15:26:05 +00:00
Sergio Martins
1b6aa4c3d1 qtquick: Inject the dock registry into the context
Useful to call from QML if needed
2021-02-28 15:01:05 +00:00
Sergio Martins
8912a2c842 Remove debug 2021-02-28 14:50:12 +00:00
Sergio Martins
4de50db68c Added DockWidgetQuick::setWidget(QQuickItem*) overload
And expose it to QML
2021-02-28 14:48:09 +00:00
Sergio Martins
6b95e4aac9 Allow to add to MDI area and start as hidden
It will remember its position
2021-02-28 00:51:12 +00:00
Sergio Martins
c27658822e Add MainWindowMDI::addDockWidget() taking QPointF 2021-02-28 00:24:08 +00:00
Sergio Martins
8ec980c1e8 Add convenience DockRegistry::mdiMainWindowByName() 2021-02-28 00:20:33 +00:00
Sergio Martins
a952c5829a qtquick: Add MainWindowMDI.qml
A convenience wrapper
2021-02-27 23:48:15 +00:00
Sergio Martins
20e1b55a8a qtquick: make sure our QML types are registered early on 2021-02-27 15:52:31 +00:00
Allen Winter
a2605868db Merge branch '1.3' 2021-02-26 18:30:03 -05:00
Allen Winter
67be35b9f8 CMakeLists.txt - don't create the dummy docs target
it can conflict when used as a submodule
2021-02-26 18:29:10 -05:00
Allen Winter
b15eb1e62f Merge branch '1.3' 2021-02-26 17:09:08 -05:00
Allen Winter
d2e3ec0448 buildsystem - add a 'docs' target
only available with cmake -DKDDockWidgets_DOCS=true
2021-02-26 17:07:58 -05:00
Allen Winter
828c403fe5 Merge branch '1.3' 2021-02-26 16:40:18 -05:00
Allen Winter
53d3bc86ea conan - support build_with_qt6 2021-02-26 16:35:33 -05:00
Allen Winter
6ed8742cc1 buildsystem - install library with "-qt6" when built for Qt6 2021-02-26 16:35:08 -05:00
Sergio Martins
7bd047f6e6 qtquick: Make DockWidget.qml be a Rectangle
So user can change its color
2021-02-26 19:29:30 +00:00
Sergio Martins
482757b1cc qtquick: Don't hardcode "FloatingWindow.qml"
Allow the user to specify their impl
2021-02-26 19:21:42 +00:00
Sergio Martins
c143f832c6 qtquick: Don't hardcode "Frame.qml"
Allow the user to specify their impl
2021-02-26 19:12:53 +00:00
Sergio Martins
67a173ef53 qtquick: Don't hardcode "DockWidget.qml"
Allow the user to specify their impl
2021-02-26 18:57:31 +00:00
Sergio Martins
c2c37488a3 Merge branch '1.3' 2021-02-26 17:26:35 +00:00
Sergio Martins
8c1840b9cf Fix popups in overlayed dockwidgets not working 2021-02-26 17:26:04 +00:00
Sergio Martins
1088e37cd4 Improve warning 2021-02-26 17:00:22 +00:00
Sergio Martins
4d6c3dac3d qtquick: Warn there's no QQmlEngine set 2021-02-26 16:58:58 +00:00
Sergio Martins
adf29873ef Define KDDOCKWIDGETS_QTQUICK when linking against QtQuick KDDW
This is added when building, but also needs to be in the public
interface
2021-02-26 16:47:04 +00:00
Sergio Martins
772afe29ea Merge branch '1.3' 2021-02-26 15:45:46 +00:00
Sergio Martins
9c78953800 Fix tests on linux
Native title bar is specific to Windows
2021-02-26 15:38:00 +00:00
Sergio Martins
ff67b9bcbc Merge branch '1.3' into master 2021-02-26 15:20:32 +00:00
Sergio Martins
103de4f910 Update ChangeLog 2021-02-26 15:15:29 +00:00
Sergio Martins
7c3f06f98f When using a native title bar make floating window geometry smaller
When undocking a dock widget, the size of the dock widget should
be maintained

Fixes #174
2021-02-26 15:12:43 +00:00
Sergio Martins
6ac77a7662 Use the flags instead of the enum 2021-02-26 14:36:22 +00:00
Sergio Martins
610b85d01a Minor readability improv: replace bool with enum
CC issue #174
2021-02-26 14:32:55 +00:00
Sergio Martins
d4d222ebd3 Update ChangeLog 2021-02-25 22:01:54 +00:00
Sergio Martins
5cbed1d34b if native title bar + Qt::Tool, double-click should redock
When using the native Windows title bar with Qt::Tool, double-clicking
the title bar should redock the window.

Fixes #173
2021-02-25 21:59:43 +00:00
Sergio Martins
ba4a2eef5c Also cancel a drag if we receive a non-client double click 2021-02-25 21:58:12 +00:00
Sergio Martins
acdf03fc84 Merge branch '1.3' into master 2021-02-24 13:56:28 +00:00
Sergio Martins
a6f19e07c4 Fixed Flag_NativeTitleBar not hidding the client title bars
When restoring the layout the native title bar would be visible
Fixes #170
2021-02-24 13:52:44 +00:00
Sergio Martins
f07301bb1e Update ChangeLog 2021-02-24 12:10:38 +00:00
Sergio Martins
2116e3741b Improve restoring layout when RestoreOption_RelativeToMainWindow is used
Doesn't make much sense to apply a scalling factor to the floating window's
position. Instead we should apply the factor to the delta position between
the main window and the floating window

Fixes #171
2021-02-23 21:02:01 +00:00
Sergio Martins
1f11c732ed Remove unused function 2021-02-23 20:16:32 +00:00
Sergio Martins
e42bee27a4 Remove unused function 2021-02-23 20:14:25 +00:00
Sergio Martins
b36dcf1325 Fix macOS developer-build due to unused variable 2021-02-22 10:46:56 +00:00
Sergio Martins
421746c943 Margins now honour logical dpi and update when changing screen 2021-02-22 00:02:57 +00:00
Sergio Martins
f544ac58e6 Apply the logical DPI factor to the MainWindow contents margins 2021-02-21 23:42:11 +00:00
Sergio Martins
6047275cfa Add a localDpiFactor helper
As we don't do everything via QStyle. layout margins for example
still need a manual factor
2021-02-21 23:02:49 +00:00
Sergio Martins
630d832f50 Emit DockRegistry::windowChangedScreen()
We now have a signal we can connect to adapt sizes to logical
dpi changes
2021-02-21 19:45:35 +00:00
Sergio Martins
e0a34465d8 MDI: Raise dock widget when clicking on it 2021-02-21 16:26:16 +00:00
Sergio Martins
443a4049c3 qtquick|MDI: Also support raising non top-level windows
If we drag mdi windows around they'll raise now
2021-02-21 15:44:56 +00:00
Sergio Martins
54980e7585 Fix Qt6 build on Windows 2021-02-20 21:42:44 +00:00
Sergio Martins
14ff8bb26b Fix mingw build 2021-02-19 11:33:01 +00:00
Sergio Martins
615bb0ee3f Fix linux developer-build 2021-02-19 11:25:02 +00:00
Sergio Martins
986fd8c13b CustomFrame: Don't use QWindow::fromWinId()
as that creates a new QWindow
2021-02-18 18:43:43 +00:00
Sergio Martins
ec1cc27815 Added DockRegistry::windowForHandle() 2021-02-18 18:39:35 +00:00
Sergio Martins
3874e2d886 CustomFrame: Added a recursion guard 2021-02-18 18:32:14 +00:00
Sergio Martins
1afcf21529 CustomFrame: Same some cpu cycles in case we don't care about the events 2021-02-18 18:21:32 +00:00
Sergio Martins
4c0f3bd7cb Fix build with older compilers 2021-02-18 17:50:14 +00:00
Sergio Martins
9aafa2a662 CustomFrame: the native event handling now honours the features 2021-02-18 15:50:12 +00:00
Sergio Martins
029ba1202e CustomFrame: Add a NativeFeatures enum
Replaces the htCaptionRect. As we don't want to pass many arguments,
instead just pass a struct which describes which native features
to enable
2021-02-18 15:14:52 +00:00
Sergio Martins
b5043b2b87 CustomFrame: Move the features enum up 1 level 2021-02-18 14:40:20 +00:00
Sergio Martins
52ce80fd97 CustomFrame: Use an enum instead of bool return in our callback 2021-02-18 14:22:26 +00:00
Sergio Martins
cd19c5eb38 CustomFrame: Allow to specify a callback for QWindow opt-in
Which QWindows should get a custom frame is up to you. Just
return true in the callback.
2021-02-18 14:10:40 +00:00
Sergio Martins
0d101d779f Fix unused variable warning 2021-02-18 11:29:02 +00:00
Milian Wolff
1ff6f43eac Allow disabling the uninstall target
Prevents clashes when kddockwidgets is included as a submodule
into a project that already defines an uninstall target.
2021-02-17 20:47:49 +01:00
Sergio Martins
98d892f643 CustomFrame: Apply the drop-shadow code initially 2021-02-17 19:46:56 +00:00
Milian Wolff
cc80afff7d Fix compile on Linux 2021-02-17 20:36:37 +01:00
Milian Wolff
91ad74e3ad Relay status tip events to the parent of FloatingWindow
That allows the status bar of the main window - if available - to
show the event contents.
2021-02-17 20:36:37 +01:00
Sergio Martins
b128d25fbe Added convenience FloatingWindow::mainWindow() 2021-02-17 19:35:54 +00:00
Sergio Martins
8b23769ccc Remove some debug code 2021-02-17 18:17:48 +00:00
Sergio Martins
13f84ab518 Fix QtQuick build 2021-02-17 18:14:57 +00:00
Sergio Martins
feadc1ff9e Add a CustomFrameHelper class
So we can reuse the custom frame for arbitrary windows
2021-02-17 18:02:51 +00:00
Sergio Martins
f75c3a123e Merge branch '1.3' into master 2021-02-17 17:44:05 +00:00
Sergio Martins
12dfe49d9b Fix unused variable warning 2021-02-17 17:14:10 +00:00
Sergio Martins
545618964d Only apply the async mouse compensation when undocking
Not needed when moving regular windows
2021-02-17 17:01:03 +00:00
Sergio Martins
a6f3f21b70 Added Draggable::isWindow() 2021-02-17 16:54:15 +00:00
Sergio Martins
e6135f49fb Make WidgetResizeHandler::handleWindowsNativeEvent() more generic
Decoupled it from the KDDW specific parts. Can now be used
by non FloatingWindow widgets
2021-02-16 20:28:59 +00:00
Sergio Martins
cd3a1c869e Add missing include 2021-02-16 19:32:00 +00:00
Sergio Martins
8bb21b12aa Add a fwd header for WidgetResizeHandler_p.h too 2021-02-16 19:29:19 +00:00
Sergio Martins
f500791ba3 FloatingWindow: This setup code is windows only 2021-02-15 22:15:26 +00:00
Sergio Martins
4ebc4e9546 Fix Linux build 2021-02-15 22:09:27 +00:00
Sergio Martins
94ee2e55fd Move FloatingWindow::setupWindow() into a reusable function
So other QWindow can use this
2021-02-15 22:03:44 +00:00
Sergio Martins
e3c370eb6b Revert "Move FloatingWindow::setupWindow() into a reusable function"
This reverts commit 6fea5af585.
Need to fix the windows build first
2021-02-15 10:43:15 -08:00
Sergio Martins
6fea5af585 Move FloatingWindow::setupWindow() into a reusable function
So other QWindow can use this
2021-02-15 18:29:19 +00:00
Sergio Martins
e9e149d55e Remove unused include 2021-02-15 17:48:13 +00:00
Sergio Martins
bb5d98164f fix windows build 2021-02-15 09:45:55 -08:00
Sergio Martins
8941f41d1c Moved NCHITTESTEventFilter to WidgetResizeHandler
It's a better fit there.
2021-02-15 17:36:22 +00:00
Sergio Martins
d45e17b603 Minor styling: Remove one level of ifdefing 2021-02-15 17:32:05 +00:00
Sergio Martins
c2b3e6b36e Remove some unneeded debug info 2021-02-15 17:28:29 +00:00
Sergio Martins
b8405c01f1 Export WidgetResizeHandler 2021-02-15 17:25:49 +00:00
Sergio Martins
3d191da07e Remove the unity build option, there's already a CMake way
-DCMAKE_UNITY_BUILD should do the same thing.
Disabling it by default since the project is too small for the gains,
and because it screws up the compile_commands.json which I need
for vscode
2021-02-14 23:28:46 +00:00
Sergio Martins
aeb61993a4 Use QT6_DIR in the presets file
Removes the hardcoded "gcc"
2021-02-14 23:08:06 +00:00
Sergio Martins
3679e1da3a Pass a QStyleOption in Button::sizeHint()
Allows to use the logical dpi of the screen the widget is in
2021-02-14 22:41:02 +00:00
Sergio Martins
52aefd23f0 Improve comments 2021-02-14 22:40:23 +00:00
Sergio Martins
76a2b84925 Windows: Support scalable icons without requiring AA_EnableHighDpiScaling 2021-02-14 22:22:04 +00:00
Sergio Martins
a89b2c7ed1 Add a comment 2021-02-14 19:54:49 +00:00
Sergio Martins
5dd4eb2d20 Honour the logical DPI for sizing title bar buttons
Some Linux window managers manipulate the logical DPI instead of
the device pixel ratio.
2021-02-14 19:39:05 +00:00
Sergio Martins
600661a570 Minor: Move Button::paintEvent() into the .cpp file 2021-02-14 19:22:41 +00:00
Sergio Martins
92f6453d26 Minor refactoring
Adds scalingFactorIsSupported(dpr) to utils, to workaround a bug
on earlier Qt
2021-02-14 19:17:15 +00:00
Sergio Martins
8bf3aa9b4d Use sizeHint() for title bar buttons
In preparation for using style pixel metrics, which is more
HDPI friendly
2021-02-14 18:43:49 +00:00
Sergio Martins
c52db1945b Minor rename for readability 2021-02-14 17:52:36 +00:00
Sergio Martins
09a10cd791 Minor rename for readability 2021-02-14 17:42:35 +00:00
Sergio Martins
07fd577894 MDI|qtquick: Fix resize cursor not being shown
the docked widgets were resizable but the cursor didn't change
shape
2021-02-14 17:41:01 +00:00
Sergio Martins
ed900b8531 MDI|qtquick: Allow to resize the docked windows with mouse
Was already possible for QtWidgets. For QtQuick we weren't
receiving the mouse events in Frame.
2021-02-14 17:26:03 +00:00
Sergio Martins
fa010357b3 Add Frame::isMDIChanged() signal 2021-02-14 17:21:28 +00:00
Sergio Martins
da6ffc85ef Fix transparency of QtQuick MDI example 2021-02-14 16:33:02 +00:00
Sergio Martins
b7c23087e1 MDI|qtquick: Don't use private API in the example 2021-02-14 13:30:34 +00:00
Sergio Martins
656f482cad qtquick: fix build 2021-02-14 13:17:45 +00:00
Sergio Martins
5e03003b94 Add MDI to the ChangeLog 2021-02-14 13:05:34 +00:00
Sergio Martins
48b5b27d42 Introduce MainWindowMDI
User no longer needs to use private API
2021-02-14 13:02:48 +00:00
Sergio Martins
fccb815d5a Fix QtQuick build 2021-02-13 18:56:18 +00:00
Sergio Martins
544d3b026a MDI windows will now poput into floating mode
when dragged behond the edges
Please enter the commit message for your changes. Lines starting
2021-02-13 18:30:59 +00:00
Sergio Martins
15ff882919 MDI: Raise window when we're dragging it
Otherwise it's appearing under others, possibly
2021-02-13 16:29:32 +00:00
Sergio Martins
18d405bdfb MDI: Don't let windows escape its parent 2021-02-13 16:19:05 +00:00
Sergio Martins
4379e7c544 MDI|Item: Allow widget to resize itself outside of the layout domain
With a normal KDDW layout, we don't allow the user to arbitrarily
resize widgets, all the resizes are done via dragging Separator,
which interacts closly with the layout.

With MDI however there's no separators. The user is resizing the
widget and the layout doesn't know about it, and it's fine for MDI
2021-02-13 14:17:42 +00:00
Sergio Martins
51e2be9b7b MDI: Don't paint the titlebar over the frame 2021-02-13 14:06:24 +00:00
Sergio Martins
f2b21d04b3 mdi example: Show KDAB logo 2021-02-13 14:02:22 +00:00
Sergio Martins
a73f35af22 MDI: Implement internal move
You can now move internal windows around without them becoming
floating
2021-02-13 13:03:14 +00:00
Sergio Martins
7b3ef03924 Add Draggable::isMDI() 2021-02-13 12:16:28 +00:00
Sergio Martins
a4d03c1514 Add a mdi-example
Don't want the main example to be too complex
2021-02-13 12:01:01 +00:00
Allen Winter
eccdee0081 buildsystem - add an uninstall target
uses the KDE extra-cmake-module ECMUninstallTarget.cmake
2021-02-12 15:18:01 -05:00
Sergio Martins
48f3390d71 Delete resize handler when floating a MDI dock widget
When it's floating it will use native resizing, or the resize handler
from the floating window. Not its own.
2021-02-11 20:09:22 +00:00
Sergio Martins
df6f1e6c8f Added Frame::isMDI() 2021-02-11 20:00:54 +00:00
Sergio Martins
3c95081aee MDI: Support internal resize
You can now resize docked MDI widgets
2021-02-11 19:37:05 +00:00
Sergio Martins
329a980510 WidgetResizeHandler: Fix case if parent's target wasn't top-level
By luck parentGeometry was in global space. Force it to be in global
space, as the target might be more nested.

Will be used by MDI, where Frame's parent is the MDI layout
2021-02-11 19:35:40 +00:00
Sergio Martins
d8605e9ce3 Minor styling: Reduce nesting, process invariant early 2021-02-11 19:15:36 +00:00
Sergio Martins
7dcc6dc114 Frame now takes care of its own resize handler
No need to set it from outside
2021-02-11 18:56:45 +00:00
Sergio Martins
64db52fd17 Move the CursorPositions header to the enums header instead
Otherwise will have to include WidgetResizeHandler_p.h
where I don't want to. And make it an installed header etc.
2021-02-11 18:52:21 +00:00
Sergio Martins
c0957024ec Remove some unneeded qDebug 2021-02-10 22:25:45 +00:00
Sergio Martins
649571a2eb Minor styling for the MDI example 2021-02-10 20:16:19 +00:00
Sergio Martins
60adb0d50b qtquick: Add a MDI example
Instead of poluting the normal example
2021-02-10 20:06:50 +00:00
Sergio Martins
f751d7c24e TitleBar is now HDPI aware even without the Qt::AA_ properties set
It will just query the style, which is already HDPI aware.
2021-02-10 15:17:04 +00:00
Sergio Martins
5ec5ade62f qtquick: Add a MDI example 2021-02-10 00:19:29 +00:00
Sergio Martins
788c497cbb Don't crash if it's a MDI layout 2021-02-10 00:01:47 +00:00
Sergio Martins
f6c274b82f qtquick: Remove unneeded dependency to DockArea 2021-02-09 23:50:52 +00:00
Sergio Martins
5a6b4a6fdd Don't use transparent titlebar if docked MDI
As the dock widgets can overlap over eachother
2021-02-09 23:27:10 +00:00
Sergio Martins
ef212b8c0f Added TitleBar::isMDI() 2021-02-09 22:38:16 +00:00
Sergio Martins
aa2f46fd00 Added TitleBar::mainWindow() 2021-02-09 22:15:31 +00:00
Sergio Martins
a30808e6b7 Implement ItemFreeContainer::removeItem() 2021-02-09 20:17:59 +00:00
Sergio Martins
f23c55dfec Implement ItemFreeContainer::restore() 2021-02-09 19:56:12 +00:00
Sergio Martins
583d3188ca Implement ItemFreeContainer::clear() 2021-02-09 19:51:53 +00:00
Sergio Martins
ae81f64d07 MDI: Comment that these methods don't make sense for MDI 2021-02-09 19:47:51 +00:00
Sergio Martins
8dca2e346c Add experimental MDI support 2021-02-09 19:25:28 +00:00
Sergio Martins
b995986318 Decouple MainWindow a bit more from DropArea 2021-02-09 18:04:11 +00:00
Sergio Martins
0579b6dcd3 Add a basic MDI layout
Not fully functional yet, and no API for end user yet.
2021-02-09 17:44:49 +00:00
Sergio Martins
4d147c7ff3 Remove two drop area dependencies 2021-02-09 17:34:40 +00:00
Sergio Martins
baf5f088dc Remove more unneeded references to DropArea
reference its base class instead
2021-02-09 16:58:24 +00:00
Sergio Martins
2adaacb922 Remove unused AnimatedIndicators files 2021-02-09 16:54:23 +00:00
Sergio Martins
bc6870f4ee Move the serialize/deserialize functions into base class 2021-02-09 16:52:16 +00:00
Sergio Martins
c6abaf22c1 Decouple WindowBeingDragged from DropArea 2021-02-09 16:41:26 +00:00
Sergio Martins
e04e066177 Remove unneeded DropArea::numFrames() 2021-02-09 16:34:58 +00:00
Sergio Martins
6ad01f4994 Make Frame deal in LayoutWidget instead of its derived class DropArea 2021-02-09 16:32:42 +00:00
Sergio Martins
f6ad75e214 Rename Frame::setDropArea() to Frame::setLayoutWidget()
Made it private too.
2021-02-09 16:29:29 +00:00
Sergio Martins
a76864450e Remove unneeded Frame::dropArea() 2021-02-09 16:25:56 +00:00
Sergio Martins
3fe9994322 Remove some debug statements which are unneeded 2021-02-09 16:25:19 +00:00
Sergio Martins
3bcfcc40c0 Minor decoupling between DropArea and DockWidgetBase 2021-02-09 16:23:49 +00:00
Sergio Martins
92b54f949b More MultiSplitter decoupling 2021-02-09 16:17:49 +00:00
Sergio Martins
a73746fe8b tests: Use the MultiSplitter base class in a few places 2021-02-09 16:12:22 +00:00
Sergio Martins
da1fddffe1 Decouple MultiSplitter from MainWindow and FloatingWidget
They deal in its base class instead (LayoutWidget)
2021-02-09 16:05:56 +00:00
Sergio Martins
e3b0feb967 DockWidgetBase.cpp uses MultiSplitter's base class now 2021-02-09 15:54:47 +00:00
Sergio Martins
4e6811cfdc qtquick: Fix build 2021-02-09 15:47:54 +00:00
Sergio Martins
cc47e46e1f Move lots of methods from MultiSplitter to its base class
Should be almost done now
2021-02-09 15:45:22 +00:00
Sergio Martins
4246d99b93 DockRegistry now deals in LayoutWidget
Instead of its derived class MultiSplitter
2021-02-09 15:40:44 +00:00
Sergio Martins
ba38e3c1aa Move lots of methods from MultiSplitter to its base class 2021-02-09 15:38:30 +00:00
Sergio Martins
83aa64928d Remove duplicate connect 2021-02-09 15:22:12 +00:00
Sergio Martins
5fbff7211b Move lots of methods from MultiSplitter to its base class 2021-02-09 15:09:07 +00:00
Sergio Martins
eb67f19e3e Placeholders now receive LayoutWidget instead of the derived class 2021-02-09 14:41:31 +00:00
Sergio Martins
2130a31dfd Add another base class to MultiSplitter
MultiSplitter is the QWidget that holds the layout. But we also
want to support other types of layouts, such as MDI.
2021-02-09 12:37:08 +00:00
Sergio Martins
f3ce208ec1 Merge branch '1.3' 2021-02-09 10:52:55 +00:00
Sergio Martins
146478eb5e cmake: No need to build the fuzzer during development 2021-02-09 10:52:28 +00:00
Sergio Martins
05413d5a99 Open master for 1.4 2021-02-09 10:44:28 +00:00
Allen Winter
eac726fcb8 open for 1.3.1 2021-02-08 17:05:21 -05:00
Allen Winter
b25af85234 kddockwidgets.spec - OBS fix 2021-02-08 16:42:51 -05:00
Allen Winter
02167a0788 CMakeLists.txt - ECMGeneratePriFile needs CMake v3.12 or above 2021-02-08 16:15:25 -05:00
Allen Winter
54cce9aa41 OBS - update for 1.3.0 release 2021-02-08 15:35:43 -05:00
Allen Winter
73cdc7136c README.md - a bit of formatting, spelling fixes 2021-02-08 15:25:06 -05:00
Allen Winter
1567a2b847 docs/api/CMakeLists.txt - copy some files needed by the markdown
The markdown to html conversion by doxygen doesn't copy
some files referred to in the README.md
2021-02-08 15:24:11 -05:00
Allen Winter
9fe795f8dd README.md, CONTRIBUTORS.txt - thank you contributors
those who have signed the KDAB Copyright License Agreement
2021-02-08 12:29:06 -05:00
Allen Winter
0d6cfd183d .krazy - exclude the postfix checker 2021-02-08 12:28:45 -05:00
Allen Winter
bba36d041b minor for Krazy 2021-02-08 12:11:46 -05:00
Sergio Martins
710716f658 Update ChangeLog regarding v1.3.0 release date 2021-02-08 12:15:30 +00:00
Sergio Martins
b39798ac24 qtquick: Fix build 2021-02-08 11:22:25 +00:00
Sergio Martins
1f8fbb8518 Export the DockWidgetBase pimpl for unit-tests 2021-02-08 03:16:50 -08:00
Sergio Martins
51dc666181 Added DockWidget::setFloatingGeometry()
Closes #144
2021-02-07 17:26:01 +00:00
Sergio Martins
2a6e716e07 Fix typo in README 2021-02-07 17:04:09 +00:00
Allen Winter
01d68f30e4 docs/api/Doxyfile.cmake - add reimp alias 2021-02-07 07:41:08 -05:00
Allen Winter
1b73d01de8 src/DockWidgetQuick.h - minor doxygen fix 2021-02-07 07:36:55 -05:00
Allen Winter
a421cb0b01 various - include private header first, remove header dupes 2021-02-07 07:07:49 -05:00
Allen Winter
2177336d65 minor spelling 2021-02-07 06:56:49 -05:00
Allen Winter
059a8cc38f TabWidgetQuick_p.h,TabWidgetQuick.cpp - update copyright year 2021-02-07 06:50:03 -05:00
Sergio Martins
4c33cd6409 Sidebar overlays now maintain their size when toggled
Fixes #155
2021-02-06 22:55:14 +00:00
Sergio Martins
ea48d52447 refactor MainWindowBase::updateOverlayGeometry()
Receives a size instead of a bool now.
2021-02-06 22:30:21 +00:00
Sergio Martins
85f4be750a Remove bogus sizing 2021-02-06 21:43:05 +00:00
Sergio Martins
0ba37a347b Save last overlayed location
For issue #155
2021-02-06 20:12:39 +00:00
Sergio Martins
6ee1900331 Add a test for issue #155 2021-02-06 19:43:26 +00:00
Sergio Martins
44bf1ef322 qtquick: Move dummy QAction class into the pimpl header
No need for it to be in the public header
2021-02-06 18:20:44 +00:00
Sergio Martins
863691c313 Remove some private API from DockWidgetBase.h
Moved it into the pimpl
2021-02-06 16:32:13 +00:00
Sergio Martins
97d7cbf657 ChangeLog: Mention we have experimental QtQuick support 2021-02-06 15:56:13 +00:00
Allen Winter
3089535de6 CMakeLists.txt, conan/conanfile.py - this is version 1.3.0 2021-02-06 10:31:17 -05:00
Sergio Martins
36818093c6 Merge branch '1.2' 2021-02-06 15:20:08 +00:00
Allen Winter
e5857df439 conan/conanfile.py - upgrade version number 2021-02-06 10:09:32 -05:00
Allen Winter
42d25dc1a5 fwd_headers/kddockwidgets/KDDockWidgets.h - update copyright year 2021-02-06 10:07:55 -05:00
Sergio Martins
3b5e78bfba ChangeLog: Add release date of 1.2.1 2021-02-06 13:02:50 +00:00
Sergio Martins
639dce7c53 Remove the QtQuick presets from the 1.2 branch
It's for master only
2021-02-06 12:46:18 +00:00
Sergio Martins
882f54647c ChangeLog removed mention to 1.3 from the 1.2 branch 2021-02-06 12:43:56 +00:00
Sergio Martins
38443048b8 Merge branch '1.2' 2021-02-05 16:42:07 +00:00
Sergio Martins
621c3dbeca Also clear the overlay internals when floating an overlay
Fixes a crash.

Relates to #162
2021-02-05 16:32:41 +00:00
Sergio Martins
f1e4f7ecda Add another test 2021-02-05 14:34:12 +00:00
Sergio Martins
249d1b6790 Remove from the sidebar when floating a widget
Fixes #162
2021-02-05 12:35:03 +00:00
Sergio Martins
894e0fd03a Added test for issue #162 2021-02-05 11:49:52 +00:00
Allen Winter
ec1f009914 CLA - switch to a form-fillable PDF version 2021-02-04 16:26:34 -05:00
Allen Winter
697417b906 CLA - switch to a form-fillable PDF version 2021-02-04 16:17:53 -05:00
Sergio Martins
dadf57c184 Merge branch '1.2' 2021-02-04 19:59:29 +00:00
Sergio Martins
7caa83be19 Also hide overlay when clicking on empty space of a main window 2021-02-04 19:56:35 +00:00
Sergio Martins
e745d38b1e Improve the test a bit
clicking on the bottom is not very effective as that's where the
bottom sidebar is
2021-02-04 19:52:21 +00:00
Sergio Martins
6d389315bf Add test for issue #157 2021-02-04 19:31:31 +00:00
Sergio Martins
024d56505a Merge branch '1.2' 2021-02-04 13:30:35 +00:00
Sergio Martins
daec97ad66 Don't require QtX11Extras for WASM
Fixes #163
2021-02-04 13:30:04 +00:00
Sergio Martins
a98daead1d README: Improve paragraph about reporting bugs
Mention that sha1 and which operating system are important too
2021-02-03 13:34:33 +00:00
Sergio Martins
2c5cf77cf2 Fix build with cmake Visual Studio generator
Fixes #156
2021-02-02 23:02:04 +00:00
Sergio Martins
a6b9a82e9c Fix crash when hosting QQuickWidget
There's no need to check if invisible widgets are candidates
for docking. There's usually no downside either, except that
QQuickWidget is very sensitive with when its platform window
is created, so don't force the creation.

Will cherry-pick to 1.2 if no regression is found

Fixes #150
2021-01-31 13:05:20 +00:00
Sergio Martins
02cf9d1cd2 Make FloatingWindowWidget::m_vlayout protected
So users can override
2021-01-30 14:28:30 +00:00
Sergio Martins
cde814b216 Add a static FloatingWindow::s_windowFlagsOverride member
The user can now change which window flags FloatingWindow will get,
as his own risk.

Not doing it via public API such as FrameworkWidgetFactory as I don't
want to encourage or support this. The amount of combinations that
can go wrong is open ended.

Fixes #149
2021-01-30 13:59:42 +00:00
Sergio Martins
5f435cd8c7 minor comment: Don't mention deprecated flag 2021-01-30 13:40:07 +00:00
Sergio Martins
6ccd98ca01 Move DockWidgetBase::eventFilter() into the private class
Fixes issue #151 where the user is overriding eventFilter too
and getting into some edge cases.

Could be worked around in user code, but it's always good to have
less protected and private API in public classes.
2021-01-29 20:20:01 +00:00
Sergio Martins
799f2a81a7 Remember previous tab position when toggling float
This was already the case, but didn't work in case the tabs
were re-oredered by the user manually

Fixes #154
2021-01-29 19:15:23 +00:00
Sergio Martins
eb9fa6f567 Add unit-test for issue #154 2021-01-29 19:07:32 +00:00
Sergio Martins
9ebd595202 Remove stray debug 2021-01-29 18:32:23 +00:00
Sergio Martins
b50d301f44 Add DockWidget::tabIndex() 2021-01-29 18:32:10 +00:00
Sergio Martins
b01b8908ea qtquick: Fix build
That test is QtWidgets only
2021-01-29 18:23:37 +00:00
Sergio Martins
78c2cdddc4 minor: Make indexOfDockWidget() take a const dock widget
So it can be called from const places
2021-01-29 18:22:20 +00:00
Sergio Martins
1eaac74fb0 README: reword the getting involved section a bit
Explain that patches as images are not good when reporting bugs
2021-01-29 12:02:14 +00:00
Sergio Martins
ee759a5459 Don't trigger "Delete on Close" when sending to the sidebar 2021-01-28 22:40:41 +00:00
Sergio Martins
e9d19805d7 Add a force close to DockWidgetBase::Private
So we can add an argument there, without changing public API
2021-01-28 22:19:21 +00:00
Sergio Martins
1f74ba43f0 Add some includes to DockWidgetBase_p.h
It's not included directly, but let's be nice to IDEs.
2021-01-28 21:54:45 +00:00
Sergio Martins
bc1e686455 Create DockWidgetBase_p.h and move the pimpl there
So we can reuse the pimpl and not have to expose public API
for implementation details
2021-01-28 21:44:12 +00:00
Allen Winter
5cc0552c57 Merge pull request #153 from KDAB/fix-missing-source
Added missing layout_saver wrapper file in the source list
2021-01-28 10:01:38 -05:00
Renato Araujo Oliveira Filho
6c06bc6af8 Added missing layout_saver wrapper file in the source list 2021-01-28 11:30:25 -03:00
Sergio Martins
2193c131a8 Merge branch '1.2'
Should fix #152
2021-01-27 23:38:51 +00:00
Sergio Martins
9a38626af5 Allow more private includes to be used from installed dir 2021-01-27 23:31:59 +00:00
Sergio Martins
f0c243f6d1 Add KDDockWidgets.h fwd_header 2021-01-27 23:27:04 +00:00
Sergio Martins
f0441f1a08 Normalize some dock_exports.h includes
In case these files are ever included from an installed location
2021-01-27 23:00:32 +00:00
Sergio Martins
8a9d9f9fce Merge branch '1.2' 2021-01-27 22:55:36 +00:00
Sergio Martins
db06c496cf Remove dependencies to LayoutSaver_p.h
Only the impl needs to include it
2021-01-27 22:51:41 +00:00
Sergio Martins
2aa97e6565 Add a fwd header for DragController_p.h 2021-01-27 22:28:57 +00:00
Sergio Martins
68597cac22 install two more private includes 2021-01-27 22:27:46 +00:00
Sergio Martins
afa543ec79 Fix .clang-format
Was complaining about Cpp14 not being a valid enumerator
2021-01-27 22:07:26 +00:00
Sergio Martins
d1df6e910b Add a .clang-format file 2021-01-27 22:00:06 +00:00
Sergio Martins
74ae567ced vscode: Update workspace files
formats code you've changed now
2021-01-27 21:32:06 +00:00
Allen Winter
8d657c5b18 Merge branch '1.2' 2021-01-27 11:59:27 -05:00
Allen Winter
802bdc102f src/CMakeLists.txt - fix Windows library versioning
was broken for RelWithDebInfo builds
2021-01-27 11:55:51 -05:00
Sergio Martins
218d18793d Remove some unneeded horizontal line from MyCentralWidget
The difference is minimal and probably looks very bad with dark
and custom themes.

Let's minimize the amount of hardcoded styling.
2021-01-26 19:32:10 +00:00
Sergio Martins
723c29e33b Merge pull request #119 from JPatriceR/add-LayoutSaver-python-binding
Add binding for LayoutSaver class
2021-01-26 14:58:56 +00:00
Sergio Martins
2e248c6658 README: Add a paragraph about versioning and API/ABI compat 2021-01-26 14:36:30 +00:00
Sergio Martins
7fbd66d54c Merge branch '1.2' 2021-01-26 14:26:26 +00:00
Sergio Martins
4bede063ae Make DockWidgetBase::eventFilter() protected
The users might want to override it

Fixes #148 and restores source compat with v1.1
2021-01-26 14:25:30 +00:00
Sergio Martins
cbc5fec119 Added Config::setDisabledPaintEvents()
Makes our internal widgets not override QWidget::paintEvent(),
which gives full power to the user to stylesheets.

Was already possible, but required the user to override the
internal widgets via the widget factory

For issue #146
2021-01-24 10:51:58 +00:00
Sergio Martins
81927b088b Add CustomizableWidget enum 2021-01-24 10:15:02 +00:00
Sergio Martins
5556edd83b vscode: prefix the vscode workspaces with "code."
Easier to open on command line if they have the same prefix
2021-01-23 12:41:31 +00:00
Sergio Martins
2834532b5f example: Add a keyboard shortcut to toggle dock widgets visiblity 2021-01-21 16:22:43 +00:00
Sergio Martins
17638bc29f Fix regression with DockWidget::toggleAction()
Added unit-test too.
While we're processing the toggle action triggering isOpen() is
always false.
2021-01-21 16:16:29 +00:00
Jean-Patrice Laude
67b48cc417 Add binding for LayoutSaver class 2021-01-20 16:53:32 +01:00
Sergio Martins
451996016a Merge pull request #128 from CE-Programming/fix/pri
Fix issues with pri file.
2021-01-19 22:38:37 +00:00
Sergio Martins
52b9a9223d Update ChangeLog 2021-01-19 22:25:00 +00:00
Sergio Martins
d5b80b0bbd Fix offset when dragging too fast
On Windows, when we start a drag, we switch to native dragging in
mid-flight, so that AeroSnap still works

Fixes #121
2021-01-19 22:23:20 +00:00
Sergio Martins
ae14e49338 Add a comment, explains why we switch to native drag 2021-01-19 21:53:35 +00:00
Sergio Martins
c23bce9829 Minor coding style: Remove nested ifdef 2021-01-19 21:50:31 +00:00
Sergio Martins
40a549ca29 Fix double delete screwing with lastPositions()
Fixes #141
2021-01-18 23:37:17 +00:00
Sergio Martins
cac435a2a5 tests: Add missing cleanup 2021-01-18 22:52:54 +00:00
Sergio Martins
b81f50402a qtquick: Fix build 2021-01-18 20:42:41 +00:00
Sergio Martins
44587cb947 Don't require LayoutSaver_p.h to be installed
It's impl detail that doesn't need to be installed
2021-01-18 20:37:22 +00:00
Sergio Martins
54314273a0 Merge branch '1.2' 2021-01-18 19:40:18 +00:00
Sergio Martins
de068dc6ff Use less relative includes, some of them were wrong
The example now includes them, so we can detect build failures
if they get wrong again

Fixes #140
2021-01-18 18:57:15 +00:00
Allen Winter
64f537ad1e Merge branch '1.2' 2021-01-18 10:15:07 -05:00
Allen Winter
312c89f884 src/CMakeLists.txt - fix kddockwidgets_version.h install destination 2021-01-18 10:14:37 -05:00
Renato Araujo Oliveira Filho
d9fd7eb3a5 Fix python bindings build
Task-Id: KDABCI-692
2021-01-18 10:10:51 -05:00
Allen Winter
af9206f350 Merge pull request #142 from KDAB/python-build-fix
Fix python bindings build
2021-01-18 10:07:04 -05:00
Allen Winter
7c99bb084e Merge branch '1.2' 2021-01-18 10:05:27 -05:00
Allen Winter
1a2757b00a Generate and install kddockwidgets_version.h
defines a version string and other useful versioning macros

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

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

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

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

So, the index in Position::deserialize() should be a index to
LayoutSaver::Layout::floatingWindows, and not to DockRegistry::self()->floatingWindows()
since the later might be smaller.
2021-01-11 21:55:47 +00:00
Sergio Martins
d034722ba9 LayoutSaver: Add some utilities 2021-01-11 21:16:02 +00:00
Sergio Martins
e1e07c95ba Merge pull request #134 from KDAB/python6
Added PySide6 support
2021-01-11 19:35:53 +00:00
Jacob Young
b81c32b1c9 Fix issues with pri file. 2021-01-07 20:43:47 -05:00
154 changed files with 6342 additions and 2835 deletions

72
.clang-format Normal file
View File

@@ -0,0 +1,72 @@
---
BasedOnStyle: WebKit
Language: Cpp
Standard: Cpp11
IndentWidth: 4
SpacesBeforeTrailingComments: 1
TabWidth: 8
UseTab: Never
ContinuationIndentWidth: 4
MaxEmptyLinesToKeep: 3
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakConstructorInitializersBeforeComma: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
ForEachMacros:
- forever # avoids { wrapped to next line
- foreach
- Q_FOREACH
AccessModifierOffset: -4
ConstructorInitializerIndentWidth: 4
AlignEscapedNewlinesLeft: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortEnumsOnASingleLine: false # requires clang-format 11
AlignAfterOpenBracket: true
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackParameters: true
ColumnLimit: 100
Cpp11BracedListStyle: false
DerivePointerBinding: false
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: false
NamespaceIndentation: None
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 60
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerBindsToType: false
SpaceAfterTemplateKeyword: false
IndentFunctionDeclarationAfterType: false
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: true
SpacesInParentheses: false
...

6
.gitignore vendored
View File

@@ -58,3 +58,9 @@ kddockwidgets_minimal_example
/CMakeDoxygenDefaults.cmake
/Testing
/layout_tst*
/x64
/src/kddockwidgets_version.h
*.vcxproj*
*.sln
*.dir
.vscode

2
.krazy
View File

@@ -7,7 +7,7 @@ EXTRA kdabcopyright-reuse,kdabcontactus,fosslicense-reuse
#EXTRA defines,null
#exclude checks now being done by clazy or clang-tools
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects,dpointer,inline
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects,dpointer,inline,postfixop
#exclude more checks
EXCLUDE style

68
.vscode/launch.json vendored
View File

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

10
.vscode/settings.json vendored
View File

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

View File

@@ -19,6 +19,11 @@
# Build static versions of the libraries
# Default=false
#
# -DKDDockWidgets_UNINSTALL=[true|false]
# Setup the uninstall target.
# You may want to disable the uninstall target that's added by default when you are including
# KDDockWidgets as a submodule directly and have a custom uninstall target of your own.
#
# -DKDDockWidgets_TESTS=[true|false]
# Build the test harness.
# Currently does nothing unless you also set KDDockWidgets_DEVELOPER_MODE=True
@@ -29,7 +34,7 @@
# Default=true
#
# -DKDDockWidgets_DOCS=[true|false]
# Build the API documentation.
# Build the API documentation. Enables the 'docs' build target.
# Default=false
#
# -DKDDockWidgets_DEVELOPER_MODE=[true|false]
@@ -49,11 +54,6 @@
# -DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=[path]
# Set an alternative install path for Python bindings
# Default=CMAKE_INSTALL_PREFIX
#
# -DKDDockWidgets_UNITY_BUILD=[true|false]
# Build with CMake's UNITY_BUILD (requires CMake version 3.16 or higher)
# Default=true
#
cmake_minimum_required(VERSION 3.7)
if(POLICY CMP0020)
@@ -81,10 +81,10 @@ else()
endif()
set(${PROJECT_NAME}_VERSION_MAJOR 1)
set(${PROJECT_NAME}_VERSION_MINOR 2)
set(${PROJECT_NAME}_VERSION_PATCH 1)
set(${PROJECT_NAME}_VERSION_MINOR 4)
set(${PROJECT_NAME}_VERSION_PATCH 0)
set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
set(${PROJECT_NAME}_SOVERSION "1.3")
set(${PROJECT_NAME}_SOVERSION "1.4")
include(FeatureSummary)
@@ -92,15 +92,11 @@ option(${PROJECT_NAME}_QT6 "Build against Qt 6" OFF)
option(${PROJECT_NAME}_DEVELOPER_MODE "Developer Mode" OFF)
option(${PROJECT_NAME}_PYTHON_BINDINGS "Build python bindings" OFF)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
option(${PROJECT_NAME}_UNITY_BUILD "Build with CMake's UNITY_BUILD" ON)
endif()
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}_UNINSTALL "Enable the uninstall target" ON)
option(${PROJECT_NAME}_TESTS "Build the tests" OFF)
option(${PROJECT_NAME}_EXAMPLES "Build the examples" ON)
option(${PROJECT_NAME}_DOCS "Build the API documentation" OFF)
@@ -114,14 +110,16 @@ endif()
#option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
if (${PROJECT_NAME}_QT6)
if(${PROJECT_NAME}_QT6)
find_package(Qt6Widgets REQUIRED)
find_package(Qt6Test REQUIRED)
set(QT_MAJOR_VERSION 6)
set(KDDockWidgets_LIBRARY_QTID "-qt6")
else()
find_package(Qt5Widgets 5.9 REQUIRED)
find_package(Qt5Test 5.9 REQUIRED)
set(QT_MAJOR_VERSION 5)
set(KDDockWidgets_LIBRARY_QTID "")
endif()
set(CMAKE_AUTOMOC ON)
@@ -156,14 +154,21 @@ macro(set_compiler_flags targetName)
endmacro()
set(${PROJECT_NAME}_DEPS "widgets")
if(${PROJECT_NAME}_QTQUICK)
find_package(Qt${QT_MAJOR_VERSION}Quick)
find_package(Qt${QT_MAJOR_VERSION}QuickControls2)
add_definitions(-DKDDOCKWIDGETS_QTQUICK)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} quick quickcontrols2")
else()
add_definitions(-DKDDOCKWIDGETS_QTWIDGETS)
endif()
if(NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6)
set(${PROJECT_NAME}_DEPS "${${PROJECT_NAME}_DEPS} x11extras")
endif()
if(${PROJECT_NAME}_STATIC)
set(${PROJECT_NAME}_LIBRARY_MODE "STATIC")
else()
@@ -178,31 +183,38 @@ if(USE_DEFAULT_INSTALL_LOCATION)
endif()
endif()
# Generate .pri file for qmake users
include(ECMGeneratePriFile)
set(PROJECT_VERSION_STRING ${${PROJECT_NAME}_VERSION})
ecm_generate_pri_file(BASE_NAME KDDockWidgets
LIB_NAME kddockwidgets
FILENAME_VAR pri_filename
)
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
install(FILES LICENSE.txt README.md DESTINATION ${INSTALL_DOC_DIR})
install(DIRECTORY LICENSES DESTINATION ${INSTALL_DOC_DIR})
add_subdirectory(src)
if(${PROJECT_NAME}_PYTHON_BINDINGS)
add_subdirectory(python)
endif()
# Generate .pri file for qmake users
if(CMAKE_VERSION VERSION_GREATER "3.11.99" AND NOT CMAKE_CONFIGURATION_TYPES) # Not working with VS generator or older cmake versions
include(ECMGeneratePriFile)
set(PROJECT_VERSION_STRING ${${PROJECT_NAME}_VERSION})
ecm_generate_pri_file(BASE_NAME KDDockWidgets
LIB_NAME kddockwidgets
DEPS ${${PROJECT_NAME}_DEPS}
FILENAME_VAR pri_filename
INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}
)
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
else()
message(WARNING "Unable to generate the pri file for qmake users. Try updating CMake.")
endif()
install(FILES LICENSE.txt README.md DESTINATION ${INSTALL_DOC_DIR})
install(DIRECTORY LICENSES DESTINATION ${INSTALL_DOC_DIR})
if(${PROJECT_NAME}_EXAMPLES)
if (${PROJECT_NAME}_QTQUICK)
if(${PROJECT_NAME}_QTQUICK)
add_subdirectory(examples/qtquick)
else()
add_subdirectory(examples/dockwidgets)
add_subdirectory(examples/minimal)
set_compiler_flags(kddockwidgets_example)
set_compiler_flags(kddockwidgets_minimal_example)
add_subdirectory(examples/dockwidgets)
add_subdirectory(examples/minimal)
add_subdirectory(examples/minimal-mdi)
set_compiler_flags(kddockwidgets_example)
set_compiler_flags(kddockwidgets_minimal_example)
endif()
endif()
@@ -213,34 +225,34 @@ if(${PROJECT_NAME}_DEVELOPER_MODE)
add_subdirectory(tests)
# Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
if (NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (160 tests), so split it in more runs so we can use threads.
add_test(NAME tst_docks0 COMMAND tests_launcher 0 5)
add_test(NAME tst_docks1 COMMAND tests_launcher 1 5)
add_test(NAME tst_docks2 COMMAND tests_launcher 2 5)
add_test(NAME tst_docks3 COMMAND tests_launcher 3 5)
add_test(NAME tst_docks4 COMMAND tests_launcher 4 5)
add_test(NAME tst_docks5 COMMAND tests_launcher 5 5)
add_test(NAME tst_docks6 COMMAND tests_launcher 6 5)
add_test(NAME tst_docks7 COMMAND tests_launcher 7 5)
add_test(NAME tst_docks8 COMMAND tests_launcher 8 5)
add_test(NAME tst_docks9 COMMAND tests_launcher 9 5)
add_test(NAME tst_docks10 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks11 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks12 COMMAND tests_launcher 11 5)
add_test(NAME tst_docks13 COMMAND tests_launcher 12 5)
add_test(NAME tst_docks14 COMMAND tests_launcher 13 5)
add_test(NAME tst_docks15 COMMAND tests_launcher 14 5)
add_test(NAME tst_docks16 COMMAND tests_launcher 15 5)
add_test(NAME tst_docks17 COMMAND tests_launcher 16 5)
add_test(NAME tst_docks18 COMMAND tests_launcher 17 5)
add_test(NAME tst_docks19 COMMAND tests_launcher 18 5)
add_test(NAME tst_docks20 COMMAND tests_launcher 19 5)
add_test(NAME tst_docks21 COMMAND tests_launcher 20 5) # one more for rounding leftovers
if(NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (160 tests), so split it in more runs so we can use threads.
add_test(NAME tst_docks0 COMMAND tests_launcher 0 5)
add_test(NAME tst_docks1 COMMAND tests_launcher 1 5)
add_test(NAME tst_docks2 COMMAND tests_launcher 2 5)
add_test(NAME tst_docks3 COMMAND tests_launcher 3 5)
add_test(NAME tst_docks4 COMMAND tests_launcher 4 5)
add_test(NAME tst_docks5 COMMAND tests_launcher 5 5)
add_test(NAME tst_docks6 COMMAND tests_launcher 6 5)
add_test(NAME tst_docks7 COMMAND tests_launcher 7 5)
add_test(NAME tst_docks8 COMMAND tests_launcher 8 5)
add_test(NAME tst_docks9 COMMAND tests_launcher 9 5)
add_test(NAME tst_docks10 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks11 COMMAND tests_launcher 10 5)
add_test(NAME tst_docks12 COMMAND tests_launcher 11 5)
add_test(NAME tst_docks13 COMMAND tests_launcher 12 5)
add_test(NAME tst_docks14 COMMAND tests_launcher 13 5)
add_test(NAME tst_docks15 COMMAND tests_launcher 14 5)
add_test(NAME tst_docks16 COMMAND tests_launcher 15 5)
add_test(NAME tst_docks17 COMMAND tests_launcher 16 5)
add_test(NAME tst_docks18 COMMAND tests_launcher 17 5)
add_test(NAME tst_docks19 COMMAND tests_launcher 18 5)
add_test(NAME tst_docks20 COMMAND tests_launcher 19 5)
add_test(NAME tst_docks21 COMMAND tests_launcher 20 5) # one more for rounding leftovers
endif()
if (NOT ${PROJECT_NAME}_QTQUICK)
# tst_multisplitter depends on QWidget
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
if(NOT ${PROJECT_NAME}_QTQUICK)
# tst_multisplitter depends on QWidget
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
endif()
endif()
@@ -248,8 +260,14 @@ endif()
if(${PROJECT_NAME}_DOCS)
add_subdirectory(docs) # needs to go last, in case there are build source files
#don't create the dummy docs target as it can conflict when used as a submodule
#else()
# add_custom_target(docs
# COMMAND ${CMAKE_COMMAND} -E echo "Sorry, there is no docs target since KDDockWidgets_DOCS=OFF."
# "Re-run cmake with the -DKDDockWidgets_DOCS=True option if you want to generate the documentation.")
endif()
if (${PROJECT_NAME}_UNITY_BUILD)
set_target_properties(kddockwidgets PROPERTIES UNITY_BUILD ON)
if(${PROJECT_NAME}_UNINSTALL)
# Add uninstall target
include(ECMUninstallTarget)
endif()

View File

@@ -9,7 +9,9 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_DEVELOPER_MODE": "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'"
"ECM_ENABLE_SANITIZERS" : "'address;undefined'",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"KDDockWidgets_FUZZER" : "OFF"
}
},
{
@@ -31,7 +33,8 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-qtwidgets",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
}
},
{
@@ -41,7 +44,8 @@
"binaryDir": "${sourceDir}/build-qtquick",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QTQUICK": "ON"
"KDDockWidgets_QTQUICK": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
}
},
{
@@ -53,7 +57,9 @@
"CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_QTQUICK": "ON",
"KDDockWidgets_DEVELOPER_MODE": "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'"
"ECM_ENABLE_SANITIZERS" : "'address;undefined'",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"KDDockWidgets_FUZZER" : "OFF"
}
},
{
@@ -63,7 +69,8 @@
"binaryDir": "${sourceDir}/build-python",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_PYTHON_BINDINGS": "ON"
"KDDockWidgets_PYTHON_BINDINGS": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
}
},
{
@@ -73,7 +80,8 @@
"binaryDir": "${sourceDir}/build-static-qtwidgets",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_STATIC": "ON"
"KDDockWidgets_STATIC": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
}
},
{
@@ -84,7 +92,8 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_STATIC": "ON",
"KDDockWidgets_QTQUICK": "ON"
"KDDockWidgets_QTQUICK": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
}
},
{
@@ -94,10 +103,11 @@
"binaryDir": "${sourceDir}/build-qtwidgets6",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QT6": "ON"
"KDDockWidgets_QT6": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
},
"environment": {
"PATH": "$env{HOME}/Qt/6.0.0/gcc_64/bin:$penv{PATH}"
"PATH": "$env{QT6_DIR}/bin:$penv{PATH}"
}
},
{
@@ -109,10 +119,12 @@
"CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_QT6": "ON",
"KDDockWidgets_DEVELOPER_MODE": "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'"
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON",
"ECM_ENABLE_SANITIZERS" : "'address;undefined'",
"KDDockWidgets_FUZZER" : "OFF"
},
"environment": {
"PATH": "$env{HOME}/Qt/6.0.0/gcc_64/bin:$penv{PATH}"
"PATH": "$env{QT6_DIR}/bin:$penv{PATH}"
}
},
{
@@ -123,10 +135,11 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QTQUICK": "ON",
"KDDockWidgets_QT6": "ON"
"KDDockWidgets_QT6": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS" : "ON"
},
"environment": {
"PATH": "$env{HOME}/Qt/6.0.0/gcc_64/bin:$penv{PATH}"
"PATH": "$env{QT6_DIR}/bin:$penv{PATH}"
}
}
]

3
CONTRIBUTORS.txt Normal file
View File

@@ -0,0 +1,3 @@
Christian Riggenbach <criggenbach@magahugu.ch>
Jacob Young <amazingjacob@gmail.com>
Matthew Waltz <matthewwaltzis@gmail.com>

View File

@@ -1,12 +1,48 @@
* v1.3.0 (unreleased)
- [TODO] QtQuick support
* v1.4.0 (unreleased)
- Experimental MDI support. By using MainWindowMDI you can now have "docked" dock widgets
which are free to be arbitrarily positioned and even overlapped inside the main window.
- TitleBar's height is now controlled by sizeHint() instead of hardcoded.
If you haven't overridden TitleBar or just inheriting from TitleBarWidget then there's nothing to do,
as TitleBarWidget already reimplements sizeHint. If you're inheriting directly from TitleBar then make
sure to provide a sizeHint.
* v1.3.1 (unreleased)
- Improve restoring layout when RestoreOption_RelativeToMainWindow is used (#171)
- Improved dragging windows across screens in mixed hdpi setups
- Fixed Flag_NativeTitleBar not hidding the client title bars when restoring (#170)
- Double clicking a native title bar of a Qt::Tool window will now redock the window (#173)
- Size of FloatingWindow now accounts for the fact that it's using Flag_NativeTitleBar and
resizes its content accordingly (#174)
- Fixed popups on overlayed dock widgets not working
* v1.3.0 (8 February 2021)
- Experimental QtQuick support (#49)
- Added static DockWidgetBase::byName() (#126)
- The enum KDDockWidgets::AddingOption has been deprecated, use
KDDockWidgets::InitialVisibilityOption instead
- You can now pass a preferred initial size to MainWindow::addDockWidget() (#95)
- Added DockWidgetBase::Option_DeleteOnClose
- Added Config::Flag_CloseOnlyCurrentTab
- PySide6 support
- Layout restorer now restores maximzied/minimized state too (#81)
- Fixed dock indicators sometimes not appearing on Windows (#103)
- Fixed Flag_NativeTitleBar not working
- Fixed drag offset when dragging too fast with mouse
- Fixed bug where last tab index position wouldn't be remembered in case user
had manually reordered tabs (#154)
- Fixed crash when hosting a QQuickWidget (#150)
- Fixed CMake Visual Studio generator not working
- Sidebar overlays now maintain their size when toggled (#155)
- Added DockWidget::setFloatingGeometry() (#144)
* v1.2.1 (unreleased)
* v1.2.1 (6 February 2021)
- Support for resizing dock widgets when they are in overlay/popup mode (autohide/sidebar feature)
- Fixed title bar close button enabled state not being restored with Layout saver (#137)
- Installs a version header (kddockwidgets_version.h) that defines a version string and other useful versioning macros (#138)
- DockWidgetBase::eventFilter() is protected instead of private (regression vs v1.1) (#148)
It's recommended that you rebuild your application when updating KDDW, as MSVC encodes private/protected in the name mangling.
- Fixed WASM build on Windows (#163)
- Fixed sidebar overlay not getting hidden when clicking on the main window docking area (#157)
* v1.2.0 (17 December 2020)
- Wayland support

View File

@@ -29,7 +29,7 @@ however that this demo isn't fully featured, as it's running on Qt for WebAssemb
Features
========
- Provide advanced docking that QDockWidgets doesn't support
- Provide advanced docking that QDockWidget doesn't support
- Native window resize on Windows (allowing for Aero-snap even with custom title bar decorations)
- Arrow drop indicators for great drop precision
- Allow for totally different, user provided, drop indicator types
@@ -46,13 +46,13 @@ Features
- Customize title bars
- Customize window frames
- Custom widget separators
- Crossplatform (macOS, Linux, Windows, WebAssembly, Wayland, X11/XCB, EGLFS are working)
- Cross-platform (macOS, Linux, Windows, WebAssembly, Wayland, X11/XCB, EGLFS are working)
See README-Wayland.md and README-WASM.md for platform specific information.
- Layouting engine honouring min/max size constraints and some size policies
- PySide2 bindings
- Clean codebase
- Not mixing GUI with state with logic with animations
- Great test coverage, even the gui and DnD operations are tested. 200 tests currently.
- Great test coverage, even the GUI and DnD operations are tested. 200 tests currently.
- Fuzzer for doing random testing and finding bugs
- Lazy separator resize
- Reordering tabs with mouse
@@ -75,10 +75,10 @@ Roadmap
Trying out the examples
=======================
A full demo lives in `examples/dockwidgets/`, it showcasts most of the features.
A simpler example lives in `examples/minimal/`, which might be more indicated
to learn the API, as it's less overwelming than the full demo.
A full demo that showcases most of the features lives in [examples/dockwidgets](examples/dockwidgets).
A simpler example can be found in [examples/minimal](examples/minimal),
which might be more indicated to learn the API, as it's less overwhelming than the full demo.
Open a terminal capable of building Qt5 applications.
Make sure you have cmake, ninja, compiler, Qt, etc in PATH.
@@ -93,23 +93,26 @@ $ cmake --build . --target install
```
Now build and run the example:
```
$ cd path/to/kddockwidgets/examples/dockwidgets/
$ cmake -G Ninja -DCMAKE_PREFIX_PATH=/path/where/to/install
$ cmake --build .
$ ./kddockwidgets_example
```
The installation directory defaults to `c:\KDAB\KDDockWidgets-<version>` on Windows
and `/usr/local/KDAB/KDDockWidgets-<version>` on non-Windows. You can change this
location by passing the option `-DCMAKE_INSTALL_PREFIX=/install/path` to cmake.
and `/usr/local/KDAB/KDDockWidgets-<version>` on non-Windows.
You can change the installation location by passing the option `-DCMAKE_INSTALL_PREFIX=/install/path` to cmake.
Using
=====
From your CMake project, add
```
find_package(KDDockWidgets CONFIG)
```
and link to the imported target `KDAB::kddockwidgets`.
That's all you need to do (the imported target also brings in the include directories)
@@ -120,7 +123,7 @@ on where you installed KDDockWidgets.
Python Bindings
================
Make sure you have pyside2, shiboken2 and shiboken2-generator installed.
Make sure you have PySide2, shiboken2 and shiboken2-generator installed.
As this time, you cannot get shiboken2-generator because the wheels are not on PyPi.
To use the wheels do this:
@@ -133,8 +136,8 @@ To use the wheels do this:
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.
Once QtForPython is installed you are ready to generate the PySide2 bindings
for KDDockWidgets.
Next pass `-DKDDockWidgets_PYTHON_BINDINGS=ON` to CMake, followed by the
make command.
@@ -146,6 +149,7 @@ by passing `-DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=/usr/lib/python3.8/si
to CMake (adjust to the python path on your system).
To run the KDDW python example
```
$ export PYTHONPATH=/kddw/install/path # Only if needed
$ cd python/examples/
@@ -153,6 +157,20 @@ $ rcc -g python -o rc_assets.py ../../examples/dockwidgets/resources_example.qrc
$ python3 main.py
```
Versioning
==========
New features go to master while the stable branch only accepts non-intrusive bug fixes.
We'll try to remain source-compatible across versions. API will get a deprecation
notice before being removed in the next version. Note that this source-compatibility
effort is only for the public API. Private API (headers ending in _p.h) might change so you
shouldn't depend on them. Private API is only exposed so more advanced users can
override, for example `paintEvent()`, and not so they can change internal business logic.
We don't promise or test binary compatibility. It's advised that you recompile
your application whenever updating KDDW.
Supported Qt versions and toolchains
=====================================
@@ -174,14 +192,26 @@ Contact KDAB at <info@kdab.com> to inquire about commercial licensing.
Get Involved
============
Please submit your issue reports to our GitHub space at
https://github.com/KDAB/KDDockWidgets
When reporting bugs please make it easy for the maintainer to reproduce it. Use `examples/minimal/` or
`examples/dockwidgets/` for reproducing the problem. If you did modifications to the example in order to
reproduce then please attach the *patch* and not a picture of your changes. You can get a patch by doing
`git diff > repro.diff` at the repo root.
Also state which KDDW sha1, branch or version you're using, and which operating system.
KDAB will happily accept external contributions; however, **all** contributions require a
signed [Copyright Assignment Agreement](docs/KDDockWidgets-CopyrightAssignmentForm.docx).
signed [Copyright Assignment Agreement](docs/KDDockWidgets-CopyrightAssignmentForm.pdf).
This is needed so we can continue to dual-license it.
Contact info@kdab.com for more information.
Please submit your contributions or issue reports from our GitHub space at
https://github.com/KDAB/KDDockWidgets
Thanks to our [contributors](CONTRIBUTORS.txt).
About KDAB
==========

View File

@@ -160,7 +160,6 @@ function(ECM_GENERATE_PRI_FILE)
else()
set(PRI_TARGET_LIBS "${BASEPATH}/${EGPF_LIB_INSTALL_DIR}")
endif()
set(PRI_TARGET_DEFINES "")
set(PRI_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/qt_${PRI_TARGET_BASENAME}.pri)
if (EGPF_FILENAME_VAR)
@@ -168,6 +167,8 @@ function(ECM_GENERATE_PRI_FILE)
endif()
set(PRI_TARGET_MODULE_CONFIG "")
set(PRI_TARGET_DEFINES "")
set(PRI_TARGET_POSTFIX "")
# backward compat: it was not obvious LIB_NAME needs to be a target name,
# and some projects where the target name was not the actual library output name
# passed the output name for LIB_NAME, so .name & .module prperties are correctly set.
@@ -177,6 +178,10 @@ function(ECM_GENERATE_PRI_FILE)
if (target_type STREQUAL "STATIC_LIBRARY")
set(PRI_TARGET_MODULE_CONFIG "staticlib")
endif()
get_target_property(target_defs ${EGPF_LIB_NAME} INTERFACE_COMPILE_DEFINITIONS)
list(FILTER target_defs EXCLUDE REGEX ^QT_)
string(JOIN " " PRI_TARGET_DEFINES "${target_defs}")
set(PRI_TARGET_POSTFIX "$<TARGET_PROPERTY:${EGPF_LIB_NAME},$<UPPER_CASE:$<CONFIG>$<$<CONFIG:>:Debug>>_POSTFIX>")
endif()
file(GENERATE
@@ -187,8 +192,8 @@ QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR}
QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR}
QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH}
QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME}
QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME}
QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES}
QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME}${PRI_TARGET_POSTFIX}
QT.${PRI_TARGET_BASENAME}.DEFINES = ${PRI_TARGET_DEFINES}
QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES}
QT.${PRI_TARGET_BASENAME}.private_includes =
QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS}

View File

@@ -0,0 +1,202 @@
#.rst:
# ECMSetupVersion
# ---------------
#
# Handle library version information.
#
# ::
#
# ecm_setup_version(<version>
# VARIABLE_PREFIX <prefix>
# [SOVERSION <soversion>]
# [VERSION_HEADER <filename>]
# [PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
#
# This parses a version string and sets up a standard set of version variables.
# It can optionally also create a C version header file and a CMake package
# version file to install along with the library.
#
# If the ``<version>`` argument is of the form ``<major>.<minor>.<patch>``
# (or ``<major>.<minor>.<patch>.<tweak>``), The following CMake variables are
# set::
#
# <prefix>_VERSION_MAJOR - <major>
# <prefix>_VERSION_MINOR - <minor>
# <prefix>_VERSION_PATCH - <patch>
# <prefix>_VERSION - <version>
# <prefix>_VERSION_STRING - <version> (for compatibility: use <prefix>_VERSION instead)
# <prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
#
# If CMake policy CMP0048 is not NEW, the following CMake variables will also
# be set::
#
# PROJECT_VERSION_MAJOR - <major>
# PROJECT_VERSION_MINOR - <minor>
# PROJECT_VERSION_PATCH - <patch>
# PROJECT_VERSION - <version>
# PROJECT_VERSION_STRING - <version> (for compatibility: use PROJECT_VERSION instead)
#
# If the VERSION_HEADER option is used, a simple C header is generated with the
# given filename. If filename is a relative path, it is interpreted as relative
# to CMAKE_CURRENT_BINARY_DIR. The generated header contains the following
# macros::
#
# <prefix>_VERSION_MAJOR - <major> as an integer
# <prefix>_VERSION_MINOR - <minor> as an integer
# <prefix>_VERSION_PATCH - <patch> as an integer
# <prefix>_VERSION_STRING - <version> as a C string
# <prefix>_VERSION - the version as an integer
#
# ``<prefix>_VERSION`` has ``<patch>`` in the bottom 8 bits, ``<minor>`` in the
# next 8 bits and ``<major>`` in the remaining bits. Note that ``<patch>`` and
# ``<minor>`` must be less than 256.
#
# If the PACKAGE_VERSION_FILE option is used, a simple CMake package version
# file is created using the write_basic_package_version_file() macro provided by
# CMake. It should be installed in the same location as the Config.cmake file of
# the library so that it can be found by find_package(). If the filename is a
# relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The
# optional COMPATIBILITY option is forwarded to
# write_basic_package_version_file(), and defaults to AnyNewerVersion.
#
# If CMake policy CMP0048 is NEW, an alternative form of the command is
# available::
#
# ecm_setup_version(PROJECT
# [VARIABLE_PREFIX <prefix>]
# [SOVERSION <soversion>]
# [VERSION_HEADER <filename>]
# [PACKAGE_VERSION_FILE <filename>] )
#
# This will use the version information set by the project() command.
# VARIABLE_PREFIX defaults to the project name. Note that PROJECT must be the
# first argument. In all other respects, it behaves like the other form of the
# command.
#
# Since pre-1.0.0.
#
# COMPATIBILITY option available since 1.6.0.
#=============================================================================
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2012 Alexander Neundorf <neundorf@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
include(CMakePackageConfigHelpers)
# save the location of the header template while CMAKE_CURRENT_LIST_DIR
# has the value we want
set(_ECM_SETUP_VERSION_HEADER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/ECMVersionHeader.h.in")
function(ecm_setup_version _version)
set(options )
set(oneValueArgs VARIABLE_PREFIX SOVERSION VERSION_HEADER PACKAGE_VERSION_FILE COMPATIBILITY)
set(multiValueArgs )
cmake_parse_arguments(ESV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(ESV_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_VERSION(): \"${ESV_UNPARSED_ARGUMENTS}\"")
endif()
set(project_manages_version FALSE)
set(use_project_version FALSE)
# CMP0048 only exists in CMake 3.0.0 and later
if(CMAKE_VERSION VERSION_LESS 3.0.0)
set(project_version_policy "OLD")
else()
cmake_policy(GET CMP0048 project_version_policy)
endif()
if(project_version_policy STREQUAL "NEW")
set(project_manages_version TRUE)
if(_version STREQUAL "PROJECT")
set(use_project_version TRUE)
endif()
elseif(_version STREQUAL "PROJECT")
message(FATAL_ERROR "ecm_setup_version given PROJECT argument, but CMP0048 is not NEW")
endif()
set(should_set_prefixed_vars TRUE)
if(NOT ESV_VARIABLE_PREFIX)
if(use_project_version)
set(ESV_VARIABLE_PREFIX "${PROJECT_NAME}")
set(should_set_prefixed_vars FALSE)
else()
message(FATAL_ERROR "Required argument PREFIX missing in ECM_SETUP_VERSION() call")
endif()
endif()
if(use_project_version)
set(_version "${PROJECT_VERSION}")
set(_major "${PROJECT_VERSION_MAJOR}")
set(_minor "${PROJECT_VERSION_MINOR}")
set(_patch "${PROJECT_VERSION_PATCH}")
else()
string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}")
string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}")
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" _patch "${_version}")
endif()
if(NOT ESV_SOVERSION)
set(ESV_SOVERSION ${_major})
endif()
if(should_set_prefixed_vars)
set(${ESV_VARIABLE_PREFIX}_VERSION "${_version}")
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR ${_major})
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR ${_minor})
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH ${_patch})
endif()
set(${ESV_VARIABLE_PREFIX}_SOVERSION ${ESV_SOVERSION})
if(NOT project_manages_version)
set(PROJECT_VERSION "${_version}")
set(PROJECT_VERSION_MAJOR "${_major}")
set(PROJECT_VERSION_MINOR "${_minor}")
set(PROJECT_VERSION_PATCH "${_patch}")
endif()
# compat
set(PROJECT_VERSION_STRING "${PROJECT_VERSION}")
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}")
if(ESV_VERSION_HEADER)
set(HEADER_PREFIX "${ESV_VARIABLE_PREFIX}")
set(HEADER_VERSION "${_version}")
set(HEADER_VERSION_MAJOR "${_major}")
set(HEADER_VERSION_MINOR "${_minor}")
set(HEADER_VERSION_PATCH "${_patch}")
configure_file("${_ECM_SETUP_VERSION_HEADER_TEMPLATE}" "${ESV_VERSION_HEADER}")
endif()
if(ESV_PACKAGE_VERSION_FILE)
if(NOT ESV_COMPATIBILITY)
set(ESV_COMPATIBILITY AnyNewerVersion)
endif()
write_basic_package_version_file("${ESV_PACKAGE_VERSION_FILE}" VERSION ${_version} COMPATIBILITY ${ESV_COMPATIBILITY})
endif()
if(should_set_prefixed_vars)
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR "${${ESV_VARIABLE_PREFIX}_VERSION_MAJOR}" PARENT_SCOPE)
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR "${${ESV_VARIABLE_PREFIX}_VERSION_MINOR}" PARENT_SCOPE)
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH "${${ESV_VARIABLE_PREFIX}_VERSION_PATCH}" PARENT_SCOPE)
set(${ESV_VARIABLE_PREFIX}_VERSION "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
endif()
# always set the soversion
set(${ESV_VARIABLE_PREFIX}_SOVERSION "${${ESV_VARIABLE_PREFIX}_SOVERSION}" PARENT_SCOPE)
if(NOT project_manages_version)
set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
endif()
# always set the compatibility variables
set(PROJECT_VERSION_STRING "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,50 @@
#.rst:
# ECMUninstallTarget
# ------------------
#
# Add an ``uninstall`` target.
#
# By including this module, an ``uninstall`` target will be added to your CMake
# project. This will remove all files installed (or updated) by a previous
# invocation of the ``install`` target. It will not remove files created or
# modified by an ``install(SCRIPT)`` or ``install(CODE)`` command; you should
# create a custom uninstallation target for these and use ``add_dependency`` to
# make the ``uninstall`` target depend on it:
#
# .. code-block:: cmake
#
# include(ECMUninstallTarget)
# install(SCRIPT install-foo.cmake)
# add_custom_target(uninstall_foo COMMAND ${CMAKE_COMMAND} -P uninstall-foo.cmake)
# add_dependency(uninstall uninstall_foo)
#
# The target will fail if the ``install`` target has not yet been run (so it is
# not possible to run CMake on the project and then immediately run the
# ``uninstall`` target).
#
# .. warning::
#
# CMake deliberately does not provide an ``uninstall`` target by default on
# the basis that such a target has the potential to remove important files
# from a user's computer. Use with caution.
#
# Since 1.7.0.
#=============================================================================
# SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
if (NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_LIST_DIR}/ecm_uninstall.cmake.in"
"${CMAKE_BINARY_DIR}/ecm_uninstall.cmake"
IMMEDIATE
@ONLY
)
add_custom_target(uninstall
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/ecm_uninstall.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
endif()

View File

@@ -0,0 +1,17 @@
// This file was generated by ecm_setup_version(): DO NOT EDIT!
#ifndef @HEADER_PREFIX@_VERSION_H
#define @HEADER_PREFIX@_VERSION_H
#define @HEADER_PREFIX@_VERSION_STRING "@HEADER_VERSION@"
#define @HEADER_PREFIX@_VERSION_MAJOR @HEADER_VERSION_MAJOR@
#define @HEADER_PREFIX@_VERSION_MINOR @HEADER_VERSION_MINOR@
#define @HEADER_PREFIX@_VERSION_PATCH @HEADER_VERSION_PATCH@
#define @HEADER_PREFIX@_VERSION @HEADER_PREFIX@_VERSION_CHECK(@HEADER_PREFIX@_VERSION_MAJOR, @HEADER_PREFIX@_VERSION_MINOR, @HEADER_PREFIX@_VERSION_PATCH)
/*
for example: @HEADER_PREFIX@_VERSION >= @HEADER_PREFIX@_VERSION_CHECK(1, 2, 2))
*/
#define @HEADER_PREFIX@_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
#endif

View File

@@ -0,0 +1,21 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif()
else()
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()

View File

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

View File

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

View File

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

View File

@@ -13,6 +13,9 @@ Configuration options:
* build_examples
Build the examples. Default=True
* builde_python_bindings
* build_python_bindings
Build/Generate python bindings (always false for Debug or static builds). Default=False
* build_for_qt6
Build against Qt6 rather than Qt5. Default=false (Qt5 will be used even if Qt6 is available)
(Make sure the Qt6 bin directory is found in your execute PATH)

View File

@@ -11,7 +11,7 @@ from conans import ConanFile, CMake, tools
class KDDockWidgetsConan(ConanFile):
name = "kddockwidgets"
version = "1.2.0"
version = "1.3.1"
default_user = "kdab"
default_channel = "stable"
license = ("https://raw.githubusercontent.com/KDAB/KDDockWidgets/master/LICENSES/GPL-2.0-only.txt",
@@ -28,6 +28,7 @@ class KDDockWidgetsConan(ConanFile):
"build_examples": [True, False],
"build_tests": [True, False],
"build_python_bindings": [True, False],
"build_for_qt6": [True, False],
}
default_options = {
@@ -36,6 +37,7 @@ class KDDockWidgetsConan(ConanFile):
"build_examples": True,
"build_tests": False,
"build_python_bindings": False,
"build_for_qt6": False,
}
def requirements(self):
@@ -52,6 +54,7 @@ class KDDockWidgetsConan(ConanFile):
self.cmake.definitions["KDDockWidgets_EXAMPLES"] = self.options.build_examples
self.cmake.definitions["KDDockWidgets_TESTS"] = self.options.build_tests
self.cmake.definitions["KDDockWidgets_PYTHON_BINDINGS"] = self.options.build_python_bindings
self.cmake.definitions["KDDockWidgets_QT6"] = self.options.build_for_qt6
self.cmake.configure()
self.cmake.build()

View File

@@ -1,3 +1,9 @@
kddockwidgets (1.3.0) release candidate; urgency=high
* 1.3.0 final
-- Allen Winter <allen.winter@kdab.com> Mon, 08 Feb 2021 15:00:00 -0500
kddockwidgets (1.2.0) release candidate; urgency=high
* 1.2.0 final

View File

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

Binary file not shown.

View File

@@ -32,9 +32,17 @@ add_custom_command(
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
#handle a bug in doxygen where image files referred to in markdown are not copied the output
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../../screencap.gif ${DOXYGEN_OUTPUT_DIR}/html
#copy some files by-hand that are referred to by the markdown README
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOXYGEN_OUTPUT_DIR}/html/LICENSES
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/LICENSES/GPL-2.0-only.txt ${DOXYGEN_OUTPUT_DIR}/html/LICENSES
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/LICENSES/GPL-3.0-only.txt ${DOXYGEN_OUTPUT_DIR}/html/LICENSES
COMMAND ${CMAKE_COMMAND} -E make_directory ${DOXYGEN_OUTPUT_DIR}/html/docs
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/docs/KDDockWidgets-CopyrightAssignmentForm.pdf ${DOXYGEN_OUTPUT_DIR}/html/docs
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/examples ${DOXYGEN_OUTPUT_DIR}/html/examples
DEPENDS ${_dox_deps} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(kddockwidgets-api.qch ALL DEPENDS ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch)
add_custom_target(docs DEPENDS kddockwidgets-api.qch)
install(FILES ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch DESTINATION ${INSTALL_DOC_DIR})

View File

@@ -261,7 +261,7 @@ TAB_SIZE = 8
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
ALIASES =
ALIASES = "reimp=Reimplemented for internal purposes.\n"
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For

View File

@@ -12,6 +12,11 @@
#include "MyFrameworkWidgetFactory.h"
#include <kddockwidgets/FrameworkWidgetFactory.h>
#include <kddockwidgets/private/TabWidget_p.h>
#include <kddockwidgets/private/widgets/FrameWidget_p.h>
#include <kddockwidgets/private/widgets/TabBarWidget_p.h>
#include <kddockwidgets/private/widgets/TabWidgetWidget_p.h>
#include <kddockwidgets/private/widgets/TitleBarWidget_p.h>
#include <kddockwidgets/private/multisplitter/Separator_qwidget.h>

View File

@@ -52,13 +52,14 @@ static MyWidget *newMyWidget()
MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
bool maxSizeForDockWidget8,
bool maxSizeForDockWidget8, bool dockwidget5DoesntCloseBeforeRestore,
const QString &affinityName, QWidget *parent)
: MainWindow(uniqueName, options, parent)
, m_dockWidget0IsNonClosable(dockWidget0IsNonClosable)
, m_dockWidget9IsNonDockable(nonDockableDockWidget9)
, m_restoreIsRelative(restoreIsRelative)
, m_maxSizeForDockWidget8(maxSizeForDockWidget8)
, m_dockwidget5DoesntCloseBeforeRestore(dockwidget5DoesntCloseBeforeRestore)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
qsrand(time(nullptr));
@@ -133,7 +134,6 @@ void MyMainWindow::createDockWidgets()
for (int i = 0; i < numDockWidgets; i++)
m_dockwidgets << newDockWidget();
// MainWindow::addDockWidget() attaches a dock widget to the main window:
addDockWidget(m_dockwidgets.at(0), KDDockWidgets::Location_OnTop);
@@ -164,13 +164,18 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
// Passing options is optional, we just want to illustrate Option_NotClosable here
KDDockWidgets::DockWidget::Options options = KDDockWidgets::DockWidget::Option_None;
KDDockWidgets::DockWidget::LayoutSaverOptions layoutSaverOptions = KDDockWidgets::DockWidget::LayoutSaverOption::None;
if (count == 0 && m_dockWidget0IsNonClosable)
options |= KDDockWidgets::DockWidget::Option_NotClosable;
if (count == 9 && m_dockWidget9IsNonDockable)
options |= KDDockWidgets::DockWidget::Option_NotDockable;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options);
if (count == 5 && m_dockwidget5DoesntCloseBeforeRestore)
layoutSaverOptions |= KDDockWidgets::DockWidget::LayoutSaverOption::Skip;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options, layoutSaverOptions);
dock->setAffinities(affinities()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
if (count == 1)
@@ -192,6 +197,7 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
dock->resize(600, 600);
m_toggleMenu->addAction(dock->toggleAction());
dock->toggleAction()->setShortcut(QStringLiteral("ctrl+%1").arg(count));
count++;
return dock;

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2021 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.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_minimal_mdi_example)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
set(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../dockwidgets/resources_example.qrc)
add_executable(kddockwidgets_minimal_mdi_example
main.cpp
../dockwidgets/MyWidget.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_minimal_mdi_example
PRIVATE
KDAB::kddockwidgets
)

View File

@@ -0,0 +1,116 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyWidget.h"
#include <QPainter>
#include <QDebug>
#include <QFile>
#include <QLineEdit>
static QHash<QString, QImage> s_images; /// clazy:exclude=non-pod-global-static
MyWidget::MyWidget(const QString &backgroundFile, const QString &logoFile, QWidget *parent)
: QWidget(parent)
{
if (!backgroundFile.isEmpty()) {
auto it = s_images.find(backgroundFile);
if (it == s_images.end())
it = s_images.insert(backgroundFile, QImage(backgroundFile));
m_background = it.value();
}
if (!logoFile.isEmpty()) {
auto it = s_images.find(logoFile);
if (it == s_images.end())
it = s_images.insert(logoFile, QImage(logoFile));
m_logo = it.value();
}
setFocusPolicy(Qt::StrongFocus);
#if 0
// Uncomment to show focus propagation working
new QLineEdit(this);
auto l2 = new QLineEdit(this);
l2->move(0, 100);
setFocusProxy(l2);
#endif
}
MyWidget::~MyWidget()
{
}
void MyWidget::drawLogo(QPainter &p)
{
if (m_logo.isNull())
return;
const qreal ratio = m_logo.height() / (m_logo.width() * 1.0);
const int maxWidth = int(0.80 * size().width());
const int maxHeight = int(0.80 * size().height());
const int proposedHeight = int(maxWidth * ratio);
const int width = proposedHeight <= maxHeight ? maxWidth
: int(maxHeight / ratio);
const int height = int(width * ratio);
QRect targetLogoRect(0,0, width, height);
targetLogoRect.moveCenter(rect().center() + QPoint(0, -int(size().height() * 0.00)));
p.drawImage(targetLogoRect, m_logo, m_logo.rect());
}
MyWidget1::MyWidget1(MyWidget::QWidget *parent)
: MyWidget(QStringLiteral(":/assets/triangles.png"), QStringLiteral(":/assets/KDAB_bubble_white.png"), parent)
{
}
void MyWidget1::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), QColor(0xCC, 0xCC, 0xCC));
p.drawImage(m_background.rect(), m_background, m_background.rect());
drawLogo(p);
}
MyWidget2::MyWidget2(MyWidget::QWidget *parent)
: MyWidget(QString(), QStringLiteral(":/assets/KDAB_bubble_blue.png"), parent)
{
}
void MyWidget2::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), Qt::white);
drawLogo(p);
}
MyWidget3::MyWidget3(MyWidget::QWidget *parent)
: MyWidget(QStringLiteral(":/assets/base.png"), QStringLiteral(":/assets/KDAB_bubble_fulcolor.png"), parent)
, m_triangle(QImage(QStringLiteral(":/assets/tri.png")))
{
}
void MyWidget3::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), QColor(0xD5, 0xD5, 0xD5));
p.drawImage(m_background.rect(), m_background, m_background.rect());
const QRect targetRect = QRect({ width() - m_triangle.width(), height() - m_triangle.height() }, m_triangle.size());
p.drawImage(targetRect, m_triangle, m_triangle.rect());
drawLogo(p);
}

View File

@@ -0,0 +1,65 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef EXAMPLEDOCKABLEWIDGET_H
#define EXAMPLEDOCKABLEWIDGET_H
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
class QPainter;
QT_END_NAMESPACE
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget() = default;
explicit MyWidget(const QString &backgroundFile, const QString &logoFile, QWidget *parent = nullptr);
~MyWidget();
protected:
void drawLogo(QPainter &);
QImage m_background;
QImage m_logo;
};
class MyWidget1 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget1(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent*) override;
};
class MyWidget2 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget2(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent*) override;
};
class MyWidget3 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget3(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent*) override;
QImage m_triangle;
};
#endif

View File

@@ -0,0 +1,62 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyWidget.h"
#include <kddockwidgets/DockWidget.h>
#include <kddockwidgets/MainWindowMDI.h>
#include <QStyleFactory>
#include <QApplication>
// clazy:excludeall=qstring-allocations
int main(int argc, char **argv)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication app(argc, 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::MainWindowMDI mainWindow(QStringLiteral("MyMainWindow"));
mainWindow.setWindowTitle("Main Window");
mainWindow.resize(1200, 1200);
mainWindow.show();
// # 2. Create a dock widget, it needs a unique name
auto dock1 = new KDDockWidgets::DockWidget(QStringLiteral("MyDock1"));
auto widget1 = new MyWidget1();
dock1->setWidget(widget1);
auto dock2 = new KDDockWidgets::DockWidget(QStringLiteral("MyDock2"));
auto widget2 = new MyWidget2();
dock2->setWidget(widget2);
auto dock3 = new KDDockWidgets::DockWidget(QStringLiteral("MyDock3"));
auto widget3 = new MyWidget3();
dock3->setWidget(widget3);
// # 3. Dock them
mainWindow.addDockWidget(dock1, QPoint(10, 10));
mainWindow.addDockWidget(dock2, QPoint(50, 50));
mainWindow.addDockWidget(dock3, QPoint(90, 90));
return app.exec();
}

View File

@@ -10,6 +10,8 @@
add_subdirectory(customtitlebar)
add_subdirectory(dockwidgets)
add_subdirectory(mdi)
set_compiler_flags(kddockwidgets_example_quick)
set_compiler_flags(kddockwidgets_example_mdi_quick)
set_compiler_flags(kddockwidgets_customtitlebar_quick)

View File

@@ -0,0 +1,18 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.9
Rectangle {
id: root
color: "green"
anchors.fill: parent
}

View File

@@ -0,0 +1,37 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2021 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.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_example_mdi_quick)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
set(RESOURCES_EXAMPLE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/resources_qtquick_mdi_example.qrc
${CMAKE_CURRENT_SOURCE_DIR}/../../dockwidgets/resources_example.qrc)
add_executable(kddockwidgets_example_mdi_quick
main.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_example_mdi_quick
PRIVATE
KDAB::kddockwidgets
)

View File

@@ -0,0 +1,37 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.9
Rectangle {
color: "white"
anchors.fill: parent
property alias background: background.source
property alias logo: logo.source
Image {
id: background
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
Image {
id: logo
fillMode: Image.PreserveAspectFit
anchors {
fill: parent
margins: 50
}
}
}
}

View File

@@ -0,0 +1,44 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.9
import QtQuick.Controls 2.12
import com.kdab.dockwidgets 1.0 as KDDW
Guest {
anchors.fill: parent
background: "qrc:/assets/triangles.png"
logo: "qrc:/assets/KDAB_bubble_white.png"
KDDW.DockWidget {
id: another
uniqueName: "another1"
source: ":/Another.qml"
}
Button {
text: "Toggle Another"
anchors {
bottom: parent.bottom
left: parent.left
margins: 5
}
onClicked: {
if (another.dockWidget.visible) {
another.dockWidget.close();
} else {
another.dockWidget.show();
}
}
}
}

View File

@@ -0,0 +1,17 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.9
Guest {
anchors.fill: parent
logo: "qrc:/assets/KDAB_bubble_blue.png"
}

View File

@@ -0,0 +1,18 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.9
Guest {
anchors.fill: parent
background: "qrc:/assets/base.png"
logo: "qrc:/assets/KDAB_bubble_fulcolor.png"
}

View File

@@ -0,0 +1,64 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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 <kddockwidgets/Config.h>
#include <kddockwidgets/DockWidgetQuick.h>
#include <kddockwidgets/private/DockRegistry_p.h>
#include <kddockwidgets/FrameworkWidgetFactory.h>
#include <kddockwidgets/MainWindowMDI.h>
#include <QQuickView>
#include <QGuiApplication>
#include <QCommandLineParser>
// Foro my own debugging, until we have better API
#include "../../src/private/MDILayoutWidget_p.h"
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
QGuiApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("KDDockWidgets example application");
parser.addHelpOption();
QQuickView view;
view.setObjectName("MainWindow QQuickView");
KDDockWidgets::Config::self().setQmlEngine(view.engine());
view.resize(1000, 1000);
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(400, 400));
auto dw2 = new KDDockWidgets::DockWidgetQuick("Dock #2");
dw2->setWidget(QStringLiteral("qrc:/Guest2.qml"));
dw2->resize(QSize(400, 400));
auto dw3 = new KDDockWidgets::DockWidgetQuick("Dock #3");
dw3->setWidget(QStringLiteral("qrc:/Guest3.qml"));
auto mainWindow = static_cast<KDDockWidgets::MainWindowMDI*>(KDDockWidgets::DockRegistry::self()->mainwindows().constFirst());
mainWindow->addDockWidget(dw1, QPoint(10, 10));
mainWindow->addDockWidget(dw2, QPoint(50, 50));
mainWindow->addDockWidget(dw3, QPoint(90, 90));
return app.exec();
}

View File

@@ -0,0 +1,19 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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.
*/
import QtQuick 2.6
import com.kdab.dockwidgets 1.0 as KDDW
import "qrc:/kddockwidgets/private/quick/qml/" // TODO: Improve the public API
MainWindowMDI {
id: root
uniqueName: "MyWindow1"
}

View File

@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Guest1.qml</file>
<file>Guest2.qml</file>
<file>Guest3.qml</file>
<file>Guest.qml</file>
<file>Another.qml</file>
</qresource>
</RCC>

View File

@@ -1,10 +1,10 @@
Format: 1.0
Source: kddockwidgets
Version: 1.2.0-1
Version: 1.3.0-1
Binary: kddockwidgets
Maintainer: Allen Winter <allen.winter@kdab.com>
Architecture: any
Build-Depends: debhelper (>=9), cdbs, cmake, qt5-default, qtbase5-dev, libqt5x11extras5-dev
Files:
00000000000000000000000000000000 00000 kddockwidgets-1.2.0.tar.gz
00000000000000000000000000000000 00000 kddockwidgets-1.3.0.tar.gz

View File

@@ -1,5 +1,5 @@
Name: kddockwidgets
Version: 1.2.0
Version: 1.3.0
Release: 1
Summary: KDAB's Dock Widget Framework for Qt
Source0: %{name}-%{version}.tar.gz
@@ -79,7 +79,15 @@ cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
%files devel
%defattr(-,root,root,-)
%if 0%{?sle_version} >= 150200 && 0%{?is_opensuse}
%{_libdir}/qt5/mkspecs/modules/*
%endif
%if 0%{?suse_version} > 1500
%{_libdir}/qt5/mkspecs/modules/*
%endif
%if 0%{?fedora} > 28
%{_libdir}/qt5/mkspecs/modules/*
%endif
%dir %{_includedir}/kddockwidgets
%{_includedir}/kddockwidgets/*
%dir %{_libdir}/cmake/KDDockWidgets
@@ -87,6 +95,8 @@ cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
%{_libdir}/libkddockwidgets.so
%changelog
* Mon Feb 08 2021 Allen Winter <allen.winter@kdab.com> 1.3.0
1.3.0 final
* Thu Dec 17 2020 Allen Winter <allen.winter@kdab.com> 1.2.0
1.2.0 final
* Fri Dec 11 2020 Allen Winter <allen.winter@kdab.com> 1.1.1

View File

@@ -20,6 +20,8 @@ set(PyKDDockWidgets_SRC
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindowbase_wrapper.h
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindow_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_mainwindow_wrapper.h
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_layoutsaver_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_layoutsaver_wrapper.h
# global module wrapper
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_module_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgets/kddockwidgets_python.h
@@ -56,6 +58,7 @@ set(PyKDDockWidgets_DEPENDS
${CMAKE_SOURCE_DIR}/src/DockWidget.h
${CMAKE_SOURCE_DIR}/src/MainWindowBase.h
${CMAKE_SOURCE_DIR}/src/MainWindow.h
${CMAKE_SOURCE_DIR}/src/LayoutSaver.h
)
create_python_bindings(
@@ -75,4 +78,4 @@ create_python_bindings(
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY)
# install
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)

View File

@@ -25,3 +25,4 @@
#include <kddockwidgets/MainWindow.h>
#include <kddockwidgets/DockWidgetBase.h>
#include <kddockwidgets/DockWidget.h>
#include <kddockwidgets/LayoutSaver.h>

View File

@@ -45,5 +45,6 @@
</object-type>
<object-type name="DockWidget" />
<object-type name="LayoutSaver" />
</namespace-type>
</typesystem>

View File

@@ -33,9 +33,15 @@ set(DOCKSLIBS_SRCS
DockWidgetBase.h
MainWindowBase.cpp
MainWindowBase.h
MainWindowMDI.cpp
MainWindowMDI.h
LayoutSaver.cpp
LayoutSaver.h
LayoutSaver_p.h
private/LayoutWidget.cpp
private/LayoutWidget_p.h
private/MDILayoutWidget.cpp
private/MDILayoutWidget_p.h
private/MultiSplitter.cpp
private/MultiSplitter_p.h
private/Position.cpp
@@ -77,6 +83,8 @@ set(DOCKSLIBS_SRCS
private/multisplitter/Item.cpp
private/multisplitter/Item_p.h
private/multisplitter/ItemFreeContainer.cpp
private/multisplitter/ItemFreeContainer_p.h
private/multisplitter/Logging.cpp
private/multisplitter/Logging_p.h
private/multisplitter/MultiSplitterConfig.cpp
@@ -97,18 +105,21 @@ set(DOCKS_INSTALLABLE_INCLUDES
FocusScope.h
QWidgetAdapter.h
LayoutSaver.h
LayoutSaver_p.h
)
set(DOCKS_INSTALLABLE_PRIVATE_INCLUDES
private/DragController_p.h
private/Draggable_p.h
private/DropArea_p.h
private/DropIndicatorOverlayInterface_p.h
private/FloatingWindow_p.h
private/Frame_p.h
private/MultiSplitter_p.h
private/LayoutWidget_p.h
private/SideBar_p.h
private/TitleBar_p.h
private/WindowBeingDragged_p.h
private/WidgetResizeHandler_p.h
private/DockRegistry_p.h
private/TabWidget_p.h
)
@@ -192,6 +203,7 @@ else()
set(DOCKS_INSTALLABLE_INCLUDES
${DOCKS_INSTALLABLE_INCLUDES}
MainWindow.h
MainWindowMDI.h
MainWindowBase.h
DockWidget.h
)
@@ -207,6 +219,7 @@ set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/kddockwidgets_resources.qrc)
add_library(kddockwidgets ${KDDockWidgets_LIBRARY_MODE} ${DOCKSLIBS_SRCS} ${DOCKS_INSTALLABLE_INCLUDES} ${RESOURCES} ${RESOURCES_QUICK})
add_library(KDAB::kddockwidgets ALIAS kddockwidgets)
set_target_properties(kddockwidgets PROPERTIES OUTPUT_NAME "kddockwidgets${KDDockWidgets_LIBRARY_QTID}")
set_compiler_flags(kddockwidgets)
target_include_directories(kddockwidgets
@@ -232,6 +245,10 @@ else()
target_compile_definitions(kddockwidgets PRIVATE BUILDING_DOCKS_LIBRARY)
endif()
if (KDDockWidgets_QTQUICK)
target_compile_definitions(kddockwidgets PUBLIC KDDOCKWIDGETS_QTQUICK)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR IS_CLANG_BUILD)
target_compile_options(kddockwidgets PRIVATE -Wshadow -fvisibility=hidden)
@@ -265,13 +282,19 @@ set_target_properties(kddockwidgets PROPERTIES
#version libraries on Windows
if(WIN32)
set(postfix ${${PROJECT_NAME}_VERSION_MAJOR})
set(CMAKE_RELEASE_POSTFIX ${postfix})
set_target_properties(kddockwidgets PROPERTIES RELEASE_POSTFIX ${CMAKE_RELEASE_POSTFIX})
#append 'd' to debug libraries
string(CONCAT postfix ${postfix} "d")
set(CMAKE_DEBUG_POSTFIX ${postfix})
set_target_properties(kddockwidgets PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
if (CMAKE_BUILD_TYPE)
set(postfix ${${PROJECT_NAME}_VERSION_MAJOR})
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
if(${UPPER_BUILD_TYPE} MATCHES "^DEBUG")
string(CONCAT postfix ${postfix} "d")
set_target_properties(kddockwidgets PROPERTIES DEBUG_POSTFIX ${postfix})
else()
set_target_properties(kddockwidgets PROPERTIES ${UPPER_BUILD_TYPE}_POSTFIX ${postfix})
endif()
elseif(CMAKE_CONFIGURATION_TYPES)
# Visual Studio generator
set_target_properties(kddockwidgets PROPERTIES DEBUG_POSTFIX d)
endif()
endif()
install(TARGETS kddockwidgets
@@ -295,12 +318,16 @@ install(FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION include/k
install(FILES private/indicators/ClassicIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
install(FILES private/indicators/SegmentedIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
KDDockWidgetsConfigVersion.cmake
VERSION ${${PROJECT_NAME}_VERSION}
COMPATIBILITY AnyNewerVersion
)
# Generate library version files
include(ECMSetupVersion)
ecm_setup_version(
${${PROJECT_NAME}_VERSION}
VARIABLE_PREFIX KDDOCKWIDGETS
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kddockwidgets_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDDockWidgetsConfigVersion.cmake"
SOVERSION ${${PROJECT_NAME}_SOVERSION}
COMPATIBILITY AnyNewerVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kddockwidgets_version.h" DESTINATION include/kddockwidgets)
install(EXPORT kddockwidgetsTargets
FILE KDDockWidgetsTargets.cmake

View File

@@ -22,6 +22,7 @@
#include "DockRegistry_p.h"
#include "FrameworkWidgetFactory.h"
#include "Utils_p.h"
#include "DragController_p.h"
#include <QDebug>
#include <QOperatingSystemVersion>
@@ -56,7 +57,9 @@ public:
FrameworkWidgetFactory *m_frameworkWidgetFactory = nullptr;
Flags m_flags = Flag_Default;
InternalFlags m_internalFlags = InternalFlag_None;
CustomizableWidgets m_disabledPaintEvents = CustomizableWidget_None;
qreal m_draggedWindowOpacity = Q_QNAN;
int m_mdiPopupThreshold = 250;
};
Config::Config()
@@ -226,6 +229,9 @@ void Config::setQmlEngine(QQmlEngine *qmlEngine)
return;
}
auto dr = DockRegistry::self(); // make sure our QML types are registered
qmlEngine->rootContext()->setContextProperty(QStringLiteral("_kddwDockRegistry"), dr);
qmlEngine->rootContext()->setContextProperty(QStringLiteral("_kddwDragController"), DragController::instance());
d->m_qmlEngine = qmlEngine;
QQmlContext *context = qmlEngine->rootContext();
@@ -234,6 +240,9 @@ void Config::setQmlEngine(QQmlEngine *qmlEngine)
QQmlEngine *Config::qmlEngine() const
{
if (!d->m_qmlEngine)
qWarning() << "Please call KDDockWidgets::Config::self()->setQmlEngine(engine)";
return d->m_qmlEngine;
}
#endif
@@ -241,8 +250,8 @@ QQmlEngine *Config::qmlEngine() const
void Config::Private::fixFlags()
{
#if defined(Q_OS_WIN)
if (QOperatingSystemVersion::current().majorVersion() < 10) {
// Aero-snap requires Windows 10
if (QOperatingSystemVersion::current().majorVersion() < 8) {
// Untested on Windows 7. Windows 8 doesn't have aerosnap but at least it can get native resizing
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
} else {
// Unconditional now
@@ -292,4 +301,23 @@ void Config::Private::fixFlags()
}
}
void Config::setDisabledPaintEvents(CustomizableWidgets widgets)
{
d->m_disabledPaintEvents = widgets;
}
Config::CustomizableWidgets Config::disabledPaintEvents() const
{
return d->m_disabledPaintEvents;
}
void Config::setMDIPopupThreshold(int threshold)
{
d->m_mdiPopupThreshold = threshold;
}
int Config::mdiPopupThreshold() const
{
return d->m_mdiPopupThreshold;
}
}

View File

@@ -63,7 +63,7 @@ public:
///@brief Flag enum to tune certain behaviours, the defaults are Flag_Default
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_NativeTitleBar = 1, ///< Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise.
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.
@@ -75,15 +75,29 @@ public:
Flag_TitleBarIsFocusable = 512, ///< You can click the title bar and it will focus the last focused widget in the focus scope. If no previously focused widget then it focuses the user's dock widget guest, which should accept focus or use a focus proxy.
Flag_LazyResize = 1024, ///< The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
Flag_DontUseUtilityFloatingWindows = 0x1000,
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_KeepAboveIfNotUtilityWindow = 0x10000, ///< Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows, you might still want them to keep above and not minimize when you focus the main window.
Flag_CloseOnlyCurrentTab = 0x20000, ///< The TitleBar's close button will only close the current tab, instead of all of them
Flag_Default = Flag_AeroSnapWithClientDecos ///< The defaults
};
Q_DECLARE_FLAGS(Flags, Flag)
///@brief List of customizable widgets
enum CustomizableWidget {
CustomizableWidget_None = 0, ///< None
CustomizableWidget_TitleBar, ///< The title bar
CustomizableWidget_DockWidget, ///< The dock widget
CustomizableWidget_Frame, ///< The container for a group of 1 or more dockwidgets which are tabbed together
CustomizableWidget_TabBar, ///< The tab bar, child of Frame, which contains 1 or more dock widgets
CustomizableWidget_TabWidget, ///< The tab widget which relates to the tab bar
CustomizableWidget_FloatingWindow, ///< A top-level window. The container for 1 or more Frame nested side by side
CustomizableWidget_Separator ///< The draggable separator between dock widgets in a layout
};
Q_DECLARE_FLAGS(CustomizableWidgets, CustomizableWidget)
///@internal
///Internal flags for addtional tunning.
///@warning Not for public consumption, support will be limited.
@@ -206,6 +220,13 @@ public:
void setAbsoluteWidgetMaxSize(QSize size);
QSize absoluteWidgetMaxSize() const;
///@brief Disables our internal widget's paint events
/// By default, KDDockWidget's internal widgets reimplement paintEvent(). Disabling them
/// (which makes the base-class, QWidget::paintEvent() be called instead) can be useful if you want to style
// via CSS stylesheets.
void setDisabledPaintEvents(CustomizableWidgets);
Config::CustomizableWidgets disabledPaintEvents() const;
///@internal
///@brief returns the internal flags.
///@warning Not for public consumption, support will be limited.
@@ -216,6 +237,12 @@ public:
///@warning Not for public consumption, support will be limited.
void setInternalFlags(InternalFlags flags);
/// @brief Sets the MDI popup threshold. When the layout is MDI and you drag a dock widget
/// X pixels behond the window's edge, it will float the dock widget.
/// by default this value is 250px. Use -1 to disable
void setMDIPopupThreshold(int);
int mdiPopupThreshold() const;
#ifdef KDDOCKWIDGETS_QTQUICK
///@brief Sets the QQmlEngine to use. Applicable only when using QtQuick.
void setQmlEngine(QQmlEngine *);

View File

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

View File

@@ -10,18 +10,20 @@
*/
#include "DockWidgetBase.h"
#include "Frame_p.h"
#include "FloatingWindow_p.h"
#include "Logging_p.h"
#include "Utils_p.h"
#include "DockRegistry_p.h"
#include "DropArea_p.h"
#include "Config.h"
#include "TitleBar_p.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "private/Position_p.h"
#include "WindowBeingDragged_p.h"
#include "LayoutSaver_p.h"
#include "Logging_p.h"
#include "MDILayoutWidget_p.h"
#include "SideBar_p.h"
#include "TitleBar_p.h"
#include "Utils_p.h"
#include "WindowBeingDragged_p.h"
#include "private/Position_p.h"
#include <QEvent>
#include <QCloseEvent>
@@ -37,107 +39,10 @@
using namespace KDDockWidgets;
class DockWidgetBase::Private
{
public:
Private(const QString &dockName, DockWidgetBase::Options options_, DockWidgetBase *qq)
: name(dockName)
, title(dockName)
, q(qq)
, options(options_)
, toggleAction(new QAction(q))
, floatAction(new QAction(q))
{
q->connect(toggleAction, &QAction::toggled, q, [this] (bool enabled) {
if (!m_updatingToggleAction) { // guard against recursiveness
toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock widget is inserted into a tab widget it might get hide events, ignore those. The Dock Widget is open.
toggle(enabled);
toggleAction->blockSignals(false);
}
});
q->connect(floatAction, &QAction::toggled, q, [this] (bool checked) {
if (!m_updatingFloatAction) { // guard against recursiveness
q->setFloating(checked);
}
Q_EMIT q->isFloatingChanged(checked);
});
toggleAction->setCheckable(true);
floatAction->setCheckable(true);
}
void init()
{
updateTitle();
}
FloatingWindow *floatingWindow() const
{
return qobject_cast<FloatingWindow*>(q->window());
}
MainWindowBase *mainWindow() const
{
if (q->isWindow())
return nullptr;
// Note: Don't simply use window(), as the MainWindow might be embedded into something else
QWidgetOrQuick *p = q->parentWidget();
while (p) {
if (auto window = qobject_cast<MainWindowBase*>(p))
return window;
if (p->isWindow())
return nullptr;
p = p->parentWidget();
}
return nullptr;
}
QPoint defaultCenterPosForFloating();
void updateTitle();
void toggle(bool enabled);
void updateToggleAction();
void updateFloatAction();
void onDockWidgetShown();
void onDockWidgetHidden();
void show();
void close();
bool restoreToPreviousPosition();
void maybeRestoreToPreviousPosition();
int currentTabIndex() const;
/**
* Before floating a dock widget we save its position. So it can be restored when calling
* DockWidget::setFloating(false)
*/
void saveTabIndex();
const QString name;
QStringList affinities;
QString title;
QIcon titleBarIcon;
QIcon tabBarIcon;
QWidgetOrQuick *widget = nullptr;
DockWidgetBase *const q;
DockWidgetBase::Options options;
QAction *const toggleAction;
QAction *const floatAction;
LastPositions m_lastPositions;
bool m_updatingToggleAction = false;
bool m_updatingFloatAction = false;
bool m_isForceClosing = false;
QSize m_lastOverlayedSize = QSize(0, 0);
};
DockWidgetBase::DockWidgetBase(const QString &name, Options options)
DockWidgetBase::DockWidgetBase(const QString &name, Options options,
LayoutSaverOptions layoutSaverOptions)
: QWidgetAdapter(nullptr, Qt::Tool)
, d(new Private(name, options, this))
, d(new Private(name, options, layoutSaverOptions, this))
{
d->init();
DockRegistry::self()->registerDockWidget(this);
@@ -146,7 +51,6 @@ DockWidgetBase::DockWidgetBase(const QString &name, Options options)
qWarning() << Q_FUNC_INFO << "Name can't be null";
setAttribute(Qt::WA_PendingMoveEvent, false);
qApp->installEventFilter(this);
}
DockWidgetBase::~DockWidgetBase()
@@ -179,7 +83,7 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, InitialOption opt
return;
}
Frame *frame = this->frame();
Frame *frame = d->frame();
if (frame) {
if (frame->containsDockWidget(other)) {
@@ -189,8 +93,8 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, InitialOption opt
} else {
if (isWindow()) {
// Doesn't have a frame yet
morphIntoFloatingWindow();
frame = this->frame();
d->morphIntoFloatingWindow();
frame = d->frame();
} else {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "null frame";
@@ -226,10 +130,10 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other,
}
if (isWindow())
morphIntoFloatingWindow();
d->morphIntoFloatingWindow();
if (auto fw = floatingWindow()) {
fw->dropArea()->addDockWidget(other, location, relativeTo, initialOption);
fw->addDockWidget(other, location, relativeTo, initialOption);
} else {
qWarning() << Q_FUNC_INFO << "Couldn't find floating nested window";
}
@@ -237,7 +141,6 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other,
void DockWidgetBase::setWidget(QWidgetOrQuick *w)
{
Q_ASSERT(w);
if (w == d->widget)
return;
@@ -251,7 +154,6 @@ void DockWidgetBase::setWidget(QWidgetOrQuick *w)
setSizePolicy(w->sizePolicy());
Q_EMIT widgetChanged(w);
setWindowTitle(uniqueName());
}
QWidgetOrQuick *DockWidgetBase::widget() const
@@ -284,7 +186,7 @@ bool DockWidgetBase::setFloating(bool floats)
if (floats) {
d->saveTabIndex();
if (isTabbed()) {
auto frame = this->frame();
auto frame = d->frame();
if (!frame) {
qWarning() << "DockWidget::setFloating: Tabbed but no frame exists"
<< this;
@@ -294,17 +196,17 @@ bool DockWidgetBase::setFloating(bool floats)
frame->detachTab(this);
} else {
frame()->titleBar()->makeWindow();
d->frame()->titleBar()->makeWindow();
}
auto lastGeo = lastPositions().lastFloatingGeometry();
auto lastGeo = d->lastPositions().lastFloatingGeometry();
if (lastGeo.isValid()) {
if (auto fw = floatingWindow())
fw->setSuggestedGeometry(lastGeo, /*preserveCenter=*/true);
fw->setSuggestedGeometry(lastGeo, SuggestedGeometryHint_PreserveCenter);
}
return true;
} else {
saveLastFloatingGeometry();
d->saveLastFloatingGeometry();
return d->restoreToPreviousPosition();
}
}
@@ -338,11 +240,25 @@ void DockWidgetBase::setTitle(const QString &title)
}
}
QRect DockWidgetBase::frameGeometry() const
{
if (Frame *f = d->frame())
return f->QWidgetAdapter::geometry();
// Means the dock widget isn't visible. Just fallback to its own geometry
return QWidgetAdapter::geometry();
}
DockWidgetBase::Options DockWidgetBase::options() const
{
return d->options;
}
DockWidgetBase::LayoutSaverOptions DockWidgetBase::layoutSaverOptions() const
{
return d->layoutSaverOptions;
}
void DockWidgetBase::setOptions(Options options)
{
if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) {
@@ -354,13 +270,13 @@ void DockWidgetBase::setOptions(Options options)
d->options = options;
Q_EMIT optionsChanged(options);
if (auto tb = titleBar())
tb->updateCloseButton();
tb->updateButtons();
}
}
bool DockWidgetBase::isTabbed() const
{
if (Frame *frame = this->frame()) {
if (Frame *frame = d->frame()) {
return frame->alwaysShowsTabs() || frame->dockWidgetCount() > 1;
} else {
if (!isFloating())
@@ -371,7 +287,7 @@ bool DockWidgetBase::isTabbed() const
bool DockWidgetBase::isCurrentTab() const
{
if (Frame *frame = this->frame()) {
if (Frame *frame = d->frame()) {
return frame->currentIndex() == frame->indexOfDockWidget(const_cast<DockWidgetBase*>(this));
} else {
return true;
@@ -380,10 +296,18 @@ bool DockWidgetBase::isCurrentTab() const
void DockWidgetBase::setAsCurrentTab()
{
if (Frame *frame = this->frame())
if (Frame *frame = d->frame())
frame->setCurrentDockWidget(this);
}
int DockWidgetBase::tabIndex() const
{
if (Frame *frame = d->frame())
return frame->indexOfDockWidget(this);
return 0;
}
void DockWidgetBase::setIcon(const QIcon &icon, IconPlaces places)
{
if (places & IconPlace::TitleBar)
@@ -414,13 +338,12 @@ QIcon DockWidgetBase::icon(IconPlace place) const
void DockWidgetBase::forceClose()
{
QScopedValueRollback<bool> rollback(d->m_isForceClosing, true);
d->close();
d->forceClose();
}
TitleBar *DockWidgetBase::titleBar() const
{
if (Frame *f = frame())
if (Frame *f = d->frame())
return f->actualTitleBar();
return nullptr;
@@ -441,7 +364,7 @@ void DockWidgetBase::show()
if (isWindow() && (d->m_lastPositions.wasFloating() || !d->m_lastPositions.isValid())) {
// Create the FloatingWindow already, instead of waiting for the show event.
// This reduces flickering on some platforms
morphIntoFloatingWindow();
d->morphIntoFloatingWindow();
} else {
QWidgetOrQuick::show();
}
@@ -477,7 +400,7 @@ MainWindowBase* DockWidgetBase::mainWindow() const
bool DockWidgetBase::isFocused() const
{
auto f = this->frame();
auto f = d->frame();
return f && f->isFocused() && isCurrentTab();
}
@@ -523,6 +446,11 @@ SideBarLocation DockWidgetBase::sideBarLocation() const
return DockRegistry::self()->sideBarLocationForDockWidget(this);
}
bool DockWidgetBase::isInSideBar() const
{
return sideBarLocation() != SideBarLocation::None;
}
bool DockWidgetBase::hasPreviousDockedLocation() const
{
return d->m_lastPositions.isValid();
@@ -538,25 +466,40 @@ DockWidgetBase *DockWidgetBase::byName(const QString &uniqueName)
return DockRegistry::self()->dockByName(uniqueName);
}
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
bool DockWidgetBase::skipsRestore() const
{
return d->layoutSaverOptions & LayoutSaverOption::Skip;
}
void DockWidgetBase::setFloatingGeometry(QRect geometry)
{
if (isOpen() && isFloating()) {
window()->setGeometry(geometry);
} else {
d->m_lastPositions.setLastFloatingGeometry(geometry);
}
}
FloatingWindow *DockWidgetBase::Private::morphIntoFloatingWindow()
{
if (auto fw = floatingWindow())
return fw; // Nothing to do
if (isWindow()) {
QRect geo = d->m_lastPositions.lastFloatingGeometry();
if (q->isWindow()) {
QRect geo = m_lastPositions.lastFloatingGeometry();
if (geo.isNull()) {
geo = geometry();
geo = q->geometry();
if (!testAttribute(Qt::WA_PendingMoveEvent)) { // If user already moved it, we don't interfere
const QPoint center = d->defaultCenterPosForFloating();
if (!q->testAttribute(Qt::WA_PendingMoveEvent)) { // If user already moved it, we don't
// interfere
const QPoint center = defaultCenterPosForFloating();
if (!center.isNull())
geo.moveCenter(center);
}
}
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(this);
frame->addWidget(q);
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(frame);
floatingWindow->setSuggestedGeometry(geo);
floatingWindow->show();
@@ -567,60 +510,23 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
}
}
void DockWidgetBase::maybeMorphIntoFloatingWindow()
void DockWidgetBase::Private::maybeMorphIntoFloatingWindow()
{
if (isWindow() && isVisible())
if (q->isWindow() && q->isVisible())
morphIntoFloatingWindow();
}
Frame *DockWidgetBase::frame() const
MDILayoutWidget *DockWidgetBase::Private::mdiLayout() const
{
QWidgetOrQuick *p = parentWidget();
while (p) {
if (auto frame = qobject_cast<Frame *>(p))
return frame;
p = p->parentWidget();
}
if (auto mw = mainWindow())
return mw->mdiLayoutWidget();
return nullptr;
}
FloatingWindow *DockWidgetBase::floatingWindow() const
DockWidgetBase::Private *DockWidgetBase::dptr() const
{
return d->floatingWindow();
}
void DockWidgetBase::addPlaceholderItem(Layouting::Item *item)
{
Q_ASSERT(item);
d->m_lastPositions.addPosition(item);
}
LastPositions& DockWidgetBase::lastPositions() const
{
return d->m_lastPositions;
}
void DockWidgetBase::saveLastFloatingGeometry()
{
if (isFloating() && isVisible()) {
// It's getting docked, save last floating position
lastPositions().setLastFloatingGeometry(window()->geometry());
}
}
void DockWidgetBase::updateFloatAction()
{
d->updateFloatAction();
}
bool DockWidgetBase::eventFilter(QObject *watched, QEvent *event)
{
const bool isWindowActivate = event->type() == QEvent::WindowActivate;
const bool isWindowDeactivate = event->type() == QEvent::WindowDeactivate;
if ((isWindowActivate || isWindowDeactivate) && watched == window())
Q_EMIT windowActiveAboutToChange(isWindowActivate);
return QWidgetAdapter::eventFilter(watched, event);
return d;
}
QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
@@ -634,21 +540,36 @@ QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
return mw->geometry().center();
}
bool DockWidgetBase::Private::eventFilter(QObject *watched, QEvent *event)
{
const bool isWindowActivate = event->type() == QEvent::WindowActivate;
const bool isWindowDeactivate = event->type() == QEvent::WindowDeactivate;
if ((isWindowActivate || isWindowDeactivate) && watched == q->window())
Q_EMIT q->windowActiveAboutToChange(isWindowActivate);
return QObject::eventFilter(watched, event);
}
void DockWidgetBase::Private::updateTitle()
{
if (q->isFloating())
q->window()->setWindowTitle(title);
toggleAction->setText(title);
}
void DockWidgetBase::Private::toggle(bool enabled)
{
if (enabled) {
show();
if (SideBar *sb = sideBar()) {
// The widget is in the sidebar, let's toggle its overlayed state
sb->toggleOverlay(q);
} else {
q->close();
// The most common case. The dock widget is not in the sidebar. just close or open it.
if (enabled) {
show();
} else {
q->close();
}
}
}
@@ -656,9 +577,9 @@ void DockWidgetBase::Private::updateToggleAction()
{
QScopedValueRollback<bool> recursionGuard(m_updatingToggleAction, true); // Guard against recursiveness
m_updatingToggleAction = true;
if ((q->isVisible() || q->frame()) && !toggleAction->isChecked()) {
if ((q->isVisible() || frame()) && !toggleAction->isChecked()) {
toggleAction->setChecked(true);
} else if ((!q->isVisible() && !q->frame()) && toggleAction->isChecked()) {
} else if ((!q->isVisible() && !frame()) && toggleAction->isChecked()) {
toggleAction->setChecked(false);
}
}
@@ -692,6 +613,10 @@ void DockWidgetBase::Private::onDockWidgetHidden()
void DockWidgetBase::Private::close()
{
if (!m_processingToggleAction && !q->isOpen()) {
return;
}
if (!m_isForceClosing && q->isFloating() && q->isVisible()) { // only user-closing is interesting to save the geometry
// We check for isVisible so we don't save geometry if you call close() on an already closed dock widget
m_lastPositions.setLastFloatingGeometry(q->window()->geometry());
@@ -700,7 +625,7 @@ void DockWidgetBase::Private::close()
saveTabIndex();
// Do some cleaning. Widget is hidden, but we must hide the tab containing it.
if (Frame *frame = q->frame()) {
if (Frame *frame = this->frame()) {
frame->removeWidget(q);
q->setParent(nullptr);
@@ -708,6 +633,9 @@ void DockWidgetBase::Private::close()
sb->removeDockWidget(q);
}
}
if (!m_isMovingToSideBar && (options & DockWidgetBase::Option_DeleteOnClose))
q->deleteLater();
}
bool DockWidgetBase::Private::restoreToPreviousPosition()
@@ -717,7 +645,7 @@ bool DockWidgetBase::Private::restoreToPreviousPosition()
Layouting::Item *item = m_lastPositions.lastItem();
MultiSplitter *layout = DockRegistry::self()->layoutForItem(item);
LayoutWidget *layout = DockRegistry::self()->layoutForItem(item);
Q_ASSERT(layout);
layout->restorePlaceholder(q, item, m_lastPositions.lastTabIndex());
return true;
@@ -737,7 +665,7 @@ void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
if (m_lastPositions.wasFloating())
return; // Nothing to do, it was floating before, now it'll just get visible
Frame *frame = q->frame();
Frame *frame = this->frame();
if (frame && frame->QWidgetAdapter::parentWidget() == DockRegistry::self()->layoutForItem(layoutItem)) {
// There's a frame already. Means the DockWidget was hidden instead of closed.
@@ -758,7 +686,7 @@ void DockWidgetBase::Private::maybeRestoreToPreviousPosition()
int DockWidgetBase::Private::currentTabIndex() const
{
Frame *frame = q->frame();
Frame *frame = this->frame();
return frame ? frame->indexOfDockWidget(q) : 0;
}
@@ -783,6 +711,8 @@ void DockWidgetBase::onParentChanged()
#endif
d->updateToggleAction();
d->updateFloatAction();
Q_EMIT actualTitleBarChanged();
}
void DockWidgetBase::onShown(bool spontaneous)
@@ -790,7 +720,7 @@ void DockWidgetBase::onShown(bool spontaneous)
d->onDockWidgetShown();
Q_EMIT shown();
if (Frame *f = frame()) {
if (Frame *f = d->frame()) {
if (!spontaneous) {
f->onDockWidgetShown(this);
}
@@ -799,7 +729,7 @@ void DockWidgetBase::onShown(bool spontaneous)
d->maybeRestoreToPreviousPosition();
// Transform into a FloatingWindow if this will be a regular floating dock widget.
QTimer::singleShot(0, this, &DockWidgetBase::maybeMorphIntoFloatingWindow);
QTimer::singleShot(0, d, &DockWidgetBase::Private::maybeMorphIntoFloatingWindow);
}
void DockWidgetBase::onHidden(bool spontaneous)
@@ -807,7 +737,7 @@ void DockWidgetBase::onHidden(bool spontaneous)
d->onDockWidgetHidden();
Q_EMIT hidden();
if (Frame *f = frame()) {
if (Frame *f = d->frame()) {
if (!spontaneous) {
f->onDockWidgetHidden(this);
}
@@ -817,7 +747,7 @@ void DockWidgetBase::onHidden(bool spontaneous)
bool DockWidgetBase::onResize(QSize newSize)
{
if (isOverlayed()) {
if (auto frame = this->frame()) {
if (auto frame = d->frame()) {
d->m_lastOverlayedSize = frame->QWidgetAdapter::size();
} else {
qWarning() << Q_FUNC_INFO << "Overlayed dock widget without frame shouldn't happen";
@@ -865,10 +795,127 @@ DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &
return dw;
}
LayoutSaver::DockWidget::Ptr DockWidgetBase::serialize() const
void DockWidgetBase::setUserType(int userType)
{
auto ptr = LayoutSaver::DockWidget::dockWidgetForName(uniqueName());
ptr->affinities = affinities();
d->m_userType = userType;
}
int DockWidgetBase::userType() const
{
return d->m_userType;
}
void DockWidgetBase::setMDIPosition(QPoint pos)
{
if (MDILayoutWidget *layout = d->mdiLayout())
layout->moveDockWidget(this, pos);
}
void DockWidgetBase::setMDISize(QSize size)
{
if (MDILayoutWidget *layout = d->mdiLayout())
layout->resizeDockWidget(this, size);
}
void DockWidgetBase::setMDIZ(int z)
{
#ifdef KDDOCKWIDGETS_QTQUICK
if (Frame *frame = d->frame()) {
if (!frame->isMDI())
return;
frame->setZ(z);
}
#else
Q_UNUSED(z);
qWarning() << Q_FUNC_INFO << "Not implemented for QtQuick";
#endif
}
LayoutSaver::DockWidget::Ptr DockWidgetBase::Private::serialize() const
{
auto ptr = LayoutSaver::DockWidget::dockWidgetForName(q->uniqueName());
ptr->affinities = q->affinities();
return ptr;
}
void DockWidgetBase::Private::forceClose()
{
QScopedValueRollback<bool> rollback(m_isForceClosing, true);
close();
}
DockWidgetBase::Private::Private(const QString &dockName, DockWidgetBase::Options options_,
LayoutSaverOptions layoutSaverOptions_, DockWidgetBase *qq)
: name(dockName)
, title(dockName)
, q(qq)
, options(options_)
, layoutSaverOptions(layoutSaverOptions_)
, toggleAction(new QAction(q))
, floatAction(new QAction(q))
{
q->connect(toggleAction, &QAction::toggled, q, [this](bool enabled) {
if (!m_updatingToggleAction) { // guard against recursiveness
toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock
// widget is inserted into a tab widget it might get
// hide events, ignore those. The Dock Widget is open.
m_processingToggleAction = true;
toggle(enabled);
toggleAction->blockSignals(false);
m_processingToggleAction = false;
}
});
q->connect(floatAction, &QAction::toggled, q, [this](bool checked) {
if (!m_updatingFloatAction) { // guard against recursiveness
q->setFloating(checked);
}
Q_EMIT q->isFloatingChanged(checked);
// When floating, we remove from the sidebar
if (checked && q->isOpen()) {
if (SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
sb->mainWindow()->clearSideBarOverlay(/* deleteFrame=*/false);
sb->removeDockWidget(q);
}
}
});
toggleAction->setCheckable(true);
floatAction->setCheckable(true);
qApp->installEventFilter(this);
}
void DockWidgetBase::Private::addPlaceholderItem(Layouting::Item *item)
{
Q_ASSERT(item);
m_lastPositions.addPosition(item);
}
LastPositions &DockWidgetBase::Private::lastPositions()
{
return m_lastPositions;
}
Frame *DockWidgetBase::Private::frame() const
{
QWidgetOrQuick *p = q->parentWidget();
while (p) {
if (auto frame = qobject_cast<Frame *>(p))
return frame;
p = p->parentWidget();
}
return nullptr;
}
void DockWidgetBase::Private::saveLastFloatingGeometry()
{
if (q->isFloating() && q->isVisible()) {
// It's getting docked, save last floating position
lastPositions().setLastFloatingGeometry(q->window()->geometry());
}
}

View File

@@ -22,25 +22,17 @@
#include "docks_export.h"
#include "KDDockWidgets.h"
#include "QWidgetAdapter.h"
#include "LayoutSaver_p.h"
#include "LayoutSaver.h"
#include <QVector>
#include <memory>
// clazy:excludeall=ctor-missing-parent-argument
QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE
class TestDocks;
namespace Layouting {
class Item;
}
namespace KDDockWidgets {
struct LastPositions;
class Frame;
class FloatingWindow;
class DragController;
@@ -51,6 +43,8 @@ class TitleBar;
class MainWindowBase;
class StateDragging;
class FrameQuick;
class DockWidgetQuick;
class LayoutWidget;
/**
* @brief The DockWidget base-class. DockWidget and DockWidgetBase are only
@@ -75,10 +69,19 @@ public:
enum Option {
Option_None = 0, ///< No option, the default
Option_NotClosable = 1, ///< The DockWidget can't be closed on the [x], only programatically
Option_NotDockable = 2 ///< The DockWidget can't be docked, it's always floating
Option_NotDockable = 2, ///< The DockWidget can't be docked, it's always floating
Option_DeleteOnClose = 4 ///< Deletes the DockWidget when closed
};
Q_DECLARE_FLAGS(Options, Option)
/// @brief Options which will affect LayoutSaver save/restore
enum class LayoutSaverOption {
None = 0, ///< Just use the defaults
Skip = 1, ///< The dock widget won't participate in save/restore. Currently only available for floating windows.
};
Q_DECLARE_FLAGS(LayoutSaverOptions, LayoutSaverOption)
enum class IconPlace {
TitleBar = 1,
TabBar = 2,
@@ -91,12 +94,15 @@ public:
/**
* @brief constructs a new DockWidget
* @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text.
* @param options optional options controlling behaviour
* @param options the options controlling certain behaviours
* @param layoutSaverOptions the options to control save/restore
*
* There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow
* when visible, or stays without a parent when hidden.
*/
explicit DockWidgetBase(const QString &uniqueName, Options options = DockWidgetBase::Options());
explicit DockWidgetBase(const QString &uniqueName,
Options options = DockWidgetBase::Options(),
LayoutSaverOptions layoutSaverOptions = LayoutSaverOptions());
///@brief destructor
~DockWidgetBase() override;
@@ -158,7 +164,7 @@ public:
*
* Returns true if the request was accomplished
*/
bool setFloating(bool floats);
Q_INVOKABLE bool setFloating(bool floats);
/**
* @brief Returns the QAction that allows to hide/show the dock widget
@@ -192,12 +198,28 @@ public:
*/
void setTitle(const QString &title);
/**
* @brief Returns the size of the dock widget's parent frame.
*
* This will always be bigger than the DockWidget's size, as there's margins and a title bar.
* Also, a frame can contain more than 1 dock widget (tabbed), meaning the geometry will account
* for the tab bar and title bar.
*
* The position of the rect is in layout coordinates. 0,0 is the top-left of the layout
* holding the widgets.
*/
QRect frameGeometry() const;
/**
* @brief Returns the dock widget's options which control behaviour.
* @sa setOptions(), optionsChanged()
*/
Options options() const;
/// @brief returns the per-dockwidget options which will affect LayoutSaver
/// These are the options which were passed to the constructor
KDDockWidgets::DockWidgetBase::LayoutSaverOptions layoutSaverOptions() const;
/**
* @brief Setter for the options.
* Only Option_NotClosable is allowed to change after construction. For the other options use
@@ -230,6 +252,12 @@ public:
*/
void setAsCurrentTab();
/**
* @brief Returns which tab index this dock widget occupies in the tab widget it's contained in
*/
int tabIndex() const;
/**
* @brief Sets an icon to show on title bars and tab bars.
* @param places Specifies where the icon will be shown (TitleBar, TabBar, ToggleAction, or All)
@@ -322,7 +350,7 @@ public:
bool isMainWindow() const;
/**
* @brief Returns whether this dock widget is docked into a main window.
* @brief Returns whether this dock widget is docked into a main window (as opposed to floating)
*
* Note that isFloating() returning false might either mean the dock widget is docked into a
* main window or into a floating window (groupped/nested with other dock widgets. Use this function
@@ -331,6 +359,7 @@ public:
bool isInMainWindow() const;
/// @brief Returns the main window this dock widget is in. nullptr if it's not inside a main window
/// Also returns nullptr if it's minimized to a sidebar
MainWindowBase *mainWindow() const;
///@brief Returns whether This or any child of this dock widget is focused
@@ -350,15 +379,22 @@ public:
*/
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.
/// @brief Returns whether this dock widget is overlayed from the side-bar.
///
/// This is only relevant when using the auto-hide and side-bar feature.
/// Not to be confused with "floating", which means top-level window.
bool isOverlayed() const;
///@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.
/// @sa isInSideBar
SideBarLocation sideBarLocation() const;
/// @brief Returns where this dockwidget is in a sidebar
/// Similar to sideBarLocation(), but returns a bool
bool isInSideBar() const;
/// @brief Returns whether this floating dock widget knows its previous docked location
/// Result only makes sense if it's floating.
///
@@ -375,6 +411,34 @@ public:
/// nullptr is returned if the dock widget isn't found.
static DockWidgetBase* byName(const QString &uniqueName);
/// @brief Returns whether this widget has the LayoutSaverOption::Skip flag
bool skipsRestore() const;
/// @brief If this dock widget is floating, then sets its geometry to @p geo.
///
/// If this dock widget is hidden then it stores the geometry so it can be used the next
/// time it becomes floating.
///
/// This is just convenience, equivalent to calling window()->setGeometry(rect), with the
/// added bonus of remembering the requested geometry in case it's still hidden.
void setFloatingGeometry(QRect geo);
///@brief Allows the user to set a type on this dock widget
///The type is opaque and will not be interpreted by KDDockWidgets.
///This type is passed to FrameWorkWidgetFactory::createTitleBar(), which the user can override
///and return different TitleBar subclasses, depending on the type.
void setUserType(int userType);
int userType() const;
/// @brief Sets this dock widgets position to pos within the MDI layout
/// This only applies if the main window is in MDI mode, which it is not by default
void setMDIPosition(QPoint pos);
/// @brief like setMDIPosition(), but for the size.
void setMDISize(QSize size);
/// @brief like setMDIPosition(), but for the Z
/// only implemented for QtQuick
void setMDIZ(int z);
Q_SIGNALS:
#ifdef KDDOCKWIDGETS_QTWIDGETS
///@brief signal emitted when the parent changed
@@ -424,6 +488,9 @@ Q_SIGNALS:
/// have it's 'activeWindow' property updated yet at this point.
void windowActiveAboutToChange(bool activated);
///@brief Emitted when the title bar that serves this dock widget changes
void actualTitleBarChanged();
protected:
void onParentChanged();
void onShown(bool spontaneous);
@@ -434,21 +501,6 @@ protected:
bool onResize(QSize newSize) override;
#endif
#if defined(DOCKS_DEVELOPER_MODE)
public Q_SLOTS:
#else
private Q_SLOTS:
#endif
/**
* @brief Creates a FloatingWindow and adds itself into it
* @return the created FloatingWindow
*/
KDDockWidgets::FloatingWindow *morphIntoFloatingWindow();
/// @brief calls morphIntoFloatingWindow() if the dock widget is visible and is a top-level
/// This is called delayed whenever we show a floating dock widget, so we get a FloatingWindow
void maybeMorphIntoFloatingWindow();
#if defined(DOCKS_DEVELOPER_MODE)
public:
#else
@@ -456,6 +508,8 @@ private:
#endif
Q_DISABLE_COPY(DockWidgetBase)
friend class MultiSplitter;
friend class LayoutWidget;
friend class MDILayoutWidget;
friend class Frame;
friend class DropArea;
friend class ::TestDocks;
@@ -467,125 +521,22 @@ private:
friend class KDDockWidgets::LayoutSaver;
friend class KDDockWidgets::MainWindowBase;
friend class KDDockWidgets::FrameQuick;
friend class KDDockWidgets::DockWidgetQuick;
/**
* @brief Constructs a dock widget from its serialized form.
* @internal
*/
static DockWidgetBase *deserialize(const LayoutSaver::DockWidget::Ptr &);
static DockWidgetBase *deserialize(const std::shared_ptr<LayoutSaver::DockWidget> &);
/**
* @brief Serializes this dock widget into an intermediate form
*/
LayoutSaver::DockWidget::Ptr serialize() const;
/**
* @brief the Frame which contains this dock widgets.
*
* A frame wraps a docked DockWidget, giving it a TabWidget so it can accept other dock widgets.
* Frame is also the actual class that goes into a MultiSplitter.
*
* It's nullptr immediately after creation.
*/
Frame *frame() const;
/**
* @brief returns the FloatingWindow this dock widget is in. If nullptr then it's in a MainWindow.
*
* Note: Being in a FloatingWindow doesn't necessarily mean @ref isFloating() returns true, as
* the dock widget might be in a floating window with other dock widgets side by side.
*/
FloatingWindow *floatingWindow() const;
///@brief adds the current layout item containing this dock widget
void addPlaceholderItem(Layouting::Item*);
///@brief returns the last position, just for tests. TODO Make tests just use the d-pointer.
LastPositions &lastPositions() const;
///@brief If this dock widget is floating, then it saves its geometry
void saveLastFloatingGeometry();
///@brief Updates the floatAction state
void updateFloatAction();
///@reimp
bool eventFilter(QObject *, QEvent *) override;
class Private;
Private *const d;
Private *dptr() const;
};
}
Q_DECLARE_METATYPE(KDDockWidgets::Location)
#if defined(QT_WIDGETS_LIB)
# include <QAction>
#else
// A QAction for QtQuick. Just so it compiles, for now
class QAction : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
bool isChecked() const {
return m_isChecked ;
}
void setCheckable(bool is) {
m_isCheckable = is;
}
void setText(const QString &text) {
m_text = text;
}
void setToolTip(const QString &text) {
m_toolTip = text;
}
QString toolTip() const {
returm m_toolTip;
}
bool enabled() const {
return m_enabled;
}
void setEnabled(bool enabled) {
m_enabled = enabled;
}
bool checked() const {
return m_checked;
}
void setChecked(bool checked) {
m_checked = checked;
}
bool isEnabled() const {
return m_enabled;
}
void toggle() {
m_enabled = !m_enabled;
Q_EMIT toggled(m_enabled);
}
Q_SIGNALS:
bool toggled(bool);
private:
QString m_text;
QString m_toolTip;
bool m_isChecked = false;
bool m_isCheckable = false;
bool m_enabled = false;
bool m_checked = false;
};
#endif
#endif

View File

@@ -10,6 +10,9 @@
*/
#include "DockWidgetQuick.h"
#include "DockWidgetBase_p.h"
#include "FrameworkWidgetFactory.h"
#include "private/quick/FrameQuick_p.h"
#include <Config.h>
#include <QQuickItem>
@@ -29,7 +32,8 @@ class DockWidgetQuick::Private
public:
Private(DockWidgetQuick *dw)
: q(dw)
, m_visualItem(q->createItem(Config::self().qmlEngine(), QStringLiteral("qrc:/kddockwidgets/private/quick/qml/DockWidget.qml")))
, m_visualItem(q->createItem(Config::self().qmlEngine(),
Config::self().frameworkWidgetFactory()->dockwidgetFilename().toString()))
{
Q_ASSERT(m_visualItem);
m_visualItem->setParent(q);
@@ -40,8 +44,8 @@ public:
QQuickItem *const m_visualItem;
};
DockWidgetQuick::DockWidgetQuick(const QString &name, Options options)
: DockWidgetBase(name, options)
DockWidgetQuick::DockWidgetQuick(const QString &name, Options options, LayoutSaverOptions layoutSaverOptions)
: DockWidgetBase(name, options, layoutSaverOptions)
, d(new Private(this))
{
// To mimic what QtWidgets does when creating a new QWidget.
@@ -59,11 +63,7 @@ void DockWidgetQuick::setWidget(const QString &qmlFilename)
if (!guest)
return;
auto adapter = new QWidgetAdapter(this);
guest->setParentItem(adapter);
guest->setParent(adapter);
setWidget(adapter);
setWidget(guest);
}
void DockWidgetQuick::setWidget(QWidgetAdapter *widget)
@@ -73,10 +73,27 @@ void DockWidgetQuick::setWidget(QWidgetAdapter *widget)
DockWidgetBase::setWidget(widget);
}
void DockWidgetQuick::setWidget(QQuickItem *guest)
{
auto adapter = new QWidgetAdapter(this);
adapter->setIsWrapper();
// In case the user app needs to use them:
adapter->setProperty("originalParent", QVariant::fromValue(guest->parent()));
adapter->setProperty("originalParentItem", QVariant::fromValue(guest->parentItem()));
guest->setParentItem(adapter);
guest->setParent(adapter);
QWidgetAdapter::makeItemFillParent(guest);
setWidget(adapter);
}
bool DockWidgetQuick::event(QEvent *e)
{
if (e->type() == QEvent::ParentChange) {
onParentChanged();
Q_EMIT actualTitleBarChanged();
} else if (e->type() == QEvent::Show) {
onShown(e->spontaneous());
} else if (e->type() == QEvent::Hide) {
@@ -107,3 +124,31 @@ QSize DockWidgetQuick::maximumSize() const
return DockWidgetBase::maximumSize();
}
TitleBar *DockWidgetQuick::actualTitleBar() const
{
if (Frame *frame = DockWidgetBase::d->frame())
return frame->actualTitleBar();
return nullptr;
}
QQuickItem *DockWidgetQuick::frameVisualItem() const
{
if (auto frame = qobject_cast<FrameQuick *>(DockWidgetBase::d->frame()))
return frame->visualItem();
return nullptr;
}
void DockWidgetQuick::onGeometryUpdated()
{
if (auto frame = qobject_cast<FrameQuick *>(DockWidgetBase::d->frame())) {
frame->updateConstriants();
frame->updateGeometry();
}
}
Frame *DockWidgetQuick::frame() const
{
return qobject_cast<FrameQuick *>(DockWidgetBase::d->frame());
}

View File

@@ -20,6 +20,7 @@
#define KD_DOCKWIDGET_QUICK_H
#include "DockWidgetBase.h"
#include "private/TitleBar_p.h"
QT_BEGIN_NAMESPACE
class QCloseEvent;
@@ -27,6 +28,8 @@ QT_END_NAMESPACE
namespace KDDockWidgets {
class Frame;
/**
* @brief Represents a dock widget.
*
@@ -35,17 +38,18 @@ namespace KDDockWidgets {
class DOCKS_EXPORT DockWidgetQuick : public DockWidgetBase
{
Q_OBJECT
Q_PROPERTY(KDDockWidgets::TitleBar* actualTitleBar READ actualTitleBar NOTIFY actualTitleBarChanged)
public:
/**
* @brief constructs a new DockWidget
* @param name the name of the dockwidget, should be unique. Use title for user visible text.
* @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text.
* @param options optional options controlling behaviour
* @param parent optional QWidget parent, for ownership purposes
*
* There's no parent argument. The DockWidget is either parented to FloatingWindow or MainWindow
* when visible, or stays without a parent when hidden.
*/
explicit DockWidgetQuick(const QString &uniqueName, Options options = {});
explicit DockWidgetQuick(const QString &uniqueName, Options options = {},
LayoutSaverOptions layoutSaverOptions = LayoutSaverOptions());
///@brief destructor
~DockWidgetQuick() override;
@@ -57,12 +61,28 @@ public:
/// @reimp
void setWidget(QWidgetAdapter *widget) override;
/// @reimp
Q_INVOKABLE void setWidget(QQuickItem *widget);
/// @reimp
QSize minimumSize() const override;
/// @reimp
QSize maximumSize() const override;
/// @brief Returns the title bar
TitleBar *actualTitleBar() const;
/// @brief Returns the visual item which represents Frame in the screen
/// Equivalent to Frame::visualItem().
QQuickItem *frameVisualItem() const;
///@internal
Frame *frame() const;
/// @brief Called by QtQuick when min-size changes
Q_INVOKABLE void onGeometryUpdated();
protected:
bool event(QEvent *e) override;

View File

@@ -217,6 +217,21 @@ QUrl DefaultWidgetFactory::titleBarFilename() const
return QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/TitleBar.qml"));
}
QUrl DefaultWidgetFactory::dockwidgetFilename() const
{
return QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/DockWidget.qml"));
}
QUrl DefaultWidgetFactory::frameFilename() const
{
return QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/Frame.qml"));
}
QUrl DefaultWidgetFactory::floatingWindowFilename() const
{
return QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/FloatingWindow.qml"));
}
#endif // QtQuick
// iconForButtonType impl is the same for QtQuick and QtWidgets
@@ -252,25 +267,15 @@ QIcon DefaultWidgetFactory::iconForButtonType(TitleBarButtonType type, qreal dpr
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.
// 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
// Mostly affects Linux. Unless you're using Qt::HighDpiScaleFactorRoundingPolicy::PassThrough, in which case it will
// affect other OSes too.
if (!scalingFactorIsSupported(dpr))
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
if (scalingFactorIsSupported(1.5))
icon.addFile(QStringLiteral(":/img/%1-1.5x.png").arg(iconName));
icon.addFile(QStringLiteral(":/img/%1-2x.png").arg(iconName));
return icon;

View File

@@ -66,9 +66,6 @@ class TabWidgetQuick;
*/
class DOCKS_EXPORT FrameworkWidgetFactory : public QObject
{
#ifdef KDDOCKWIDGETS_QTQUICK
Q_PROPERTY(QUrl titleBarFilename READ titleBarFilename CONSTANT)
#endif
Q_OBJECT
public:
FrameworkWidgetFactory() = default;
@@ -146,6 +143,9 @@ public:
virtual QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const = 0;
#else
virtual QUrl titleBarFilename() const = 0;
virtual QUrl dockwidgetFilename() const = 0;
virtual QUrl frameFilename() const = 0;
virtual QUrl floatingWindowFilename() const = 0;
#endif
/// @brief Returns the icon to be used with the specified @p type
@@ -178,7 +178,10 @@ public:
#ifdef KDDOCKWIDGETS_QTWIDGETS
QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const override;
#else
QUrl titleBarFilename() const override;
Q_INVOKABLE QUrl titleBarFilename() const override;
QUrl dockwidgetFilename() const override;
QUrl frameFilename() const override;
QUrl floatingWindowFilename() const override;
#endif
QIcon iconForButtonType(TitleBarButtonType type, qreal dpr) const override;

View File

@@ -36,6 +36,7 @@ class ItemBoxContainer;
namespace KDDockWidgets
{
Q_NAMESPACE
class MultiSplitter;
class DropArea;
@@ -49,7 +50,8 @@ namespace KDDockWidgets
enum MainWindowOption {
MainWindowOption_None = 0, ///> No option set
MainWindowOption_HasCentralFrame = 1 ///> Makes the MainWindow always have a central frame, for tabbing documents
MainWindowOption_HasCentralFrame = 1, ///> Makes the MainWindow always have a central frame, for tabbing documents
MainWindowOption_MDI = 2 ///> EXPERIMENTAL!!1 The layout will be MDI. DockWidgets can have arbitrary positions, not restricted by any layout
};
Q_DECLARE_FLAGS(MainWindowOptions, MainWindowOption)
@@ -154,15 +156,6 @@ namespace KDDockWidgets
const DefaultSizeMode sizeMode = DefaultSizeMode::Fair;
};
///@internal
enum FrameOption {
FrameOption_None = 0,
FrameOption_AlwaysShowsTabs = 1,
FrameOption_IsCentralFrame = 2,
FrameOption_IsOverlayed = 4
};
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
enum RestoreOption {
RestoreOption_None = 0,
RestoreOption_RelativeToMainWindow = 1, ///< Skips restoring the main window geometry and the restored dock widgets will use relative sizing.
@@ -177,23 +170,12 @@ namespace KDDockWidgets
};
///@internal
inline QString locationStr(Location loc)
{
switch (loc) {
case KDDockWidgets::Location_None:
return QStringLiteral("none");
case KDDockWidgets::Location_OnLeft:
return QStringLiteral("left");
case KDDockWidgets::Location_OnTop:
return QStringLiteral("top");
case KDDockWidgets::Location_OnRight:
return QStringLiteral("right");
case KDDockWidgets::Location_OnBottom:
return QStringLiteral("bottom");
}
return QString();
}
enum SuggestedGeometryHint {
SuggestedGeometryHint_None,
SuggestedGeometryHint_PreserveCenter = 1,
SuggestedGeometryHint_GeometryIsFromDocked = 2
};
Q_DECLARE_FLAGS(SuggestedGeometryHints, SuggestedGeometryHint)
/// @brief Each main window supports 4 sidebars
enum class SideBarLocation {
@@ -220,6 +202,52 @@ namespace KDDockWidgets
{
return ::qHash(static_cast<uint>(loc), seed);
}
///@internal
enum CursorPosition {
CursorPosition_Undefined = 0,
CursorPosition_Left = 1,
CursorPosition_Right = 2,
CursorPosition_Top = 4,
CursorPosition_Bottom = 8,
CursorPosition_TopLeft = CursorPosition_Top | CursorPosition_Left,
CursorPosition_TopRight = CursorPosition_Top | CursorPosition_Right,
CursorPosition_BottomRight = CursorPosition_Bottom | CursorPosition_Right,
CursorPosition_BottomLeft = CursorPosition_Bottom | CursorPosition_Left,
CursorPosition_Horizontal = CursorPosition_Right | CursorPosition_Left,
CursorPosition_Vertical = CursorPosition_Top | CursorPosition_Bottom,
CursorPosition_All = CursorPosition_Left | CursorPosition_Right | CursorPosition_Top | CursorPosition_Bottom
};
Q_DECLARE_FLAGS(CursorPositions, CursorPosition)
Q_ENUM_NS(CursorPosition)
///@internal
enum FrameOption {
FrameOption_None = 0,
FrameOption_AlwaysShowsTabs = 1,
FrameOption_IsCentralFrame = 2,
FrameOption_IsOverlayed = 4
};
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
///@internal
inline QString locationStr(Location loc)
{
switch (loc) {
case KDDockWidgets::Location_None:
return QStringLiteral("none");
case KDDockWidgets::Location_OnLeft:
return QStringLiteral("left");
case KDDockWidgets::Location_OnTop:
return QStringLiteral("top");
case KDDockWidgets::Location_OnRight:
return QStringLiteral("right");
case KDDockWidgets::Location_OnBottom:
return QStringLiteral("bottom");
}
return QString();
}
}
QT_BEGIN_NAMESPACE

View File

@@ -16,7 +16,7 @@ if (@KDDockWidgets_QTQUICK@)
find_dependency(Qt5Quick REQUIRED)
endif()
if (NOT WIN32 AND NOT APPLE)
if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN)
find_dependency(Qt5X11Extras REQUIRED)
endif()

View File

@@ -17,17 +17,18 @@
*/
#include "LayoutSaver.h"
#include "LayoutSaver_p.h"
#include "Config.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase.h"
#include "DropArea_p.h"
#include "Logging_p.h"
#include "Frame_p.h"
#include "Position_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "LayoutSaver_p.h"
#include "LayoutWidget_p.h"
#include "Logging_p.h"
#include "MainWindowBase.h"
#include "Position_p.h"
#include <qmath.h>
#include <QDebug>
@@ -69,6 +70,9 @@ public:
return m_affinityNames.isEmpty() || affinities.isEmpty() || DockRegistry::self()->affinitiesMatch(m_affinityNames, affinities);
}
void floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames);
template <typename T>
void deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel);
void deleteEmptyFrames();
@@ -174,7 +178,7 @@ QByteArray LayoutSaver::serializeLayout() const
layout.closedDockWidgets.reserve(closedDockWidgets.size());
for (DockWidgetBase *dockWidget : closedDockWidgets) {
if (d->matchesAffinity(dockWidget->affinities()))
layout.closedDockWidgets.push_back(dockWidget->serialize());
layout.closedDockWidgets.push_back(dockWidget->d->serialize());
}
// Save the placeholder info. We do it last, as we also restore it last, since we need all items to be created
@@ -184,8 +188,8 @@ QByteArray LayoutSaver::serializeLayout() const
layout.allDockWidgets.reserve(dockWidgets.size());
for (DockWidgetBase *dockWidget : dockWidgets) {
if (d->matchesAffinity(dockWidget->affinities())) {
auto dw = dockWidget->serialize();
dw->lastPosition = dockWidget->lastPositions().serialize();
auto dw = dockWidget->d->serialize();
dw->lastPosition = dockWidget->d->lastPositions().serialize();
layout.allDockWidgets.push_back(dw);
}
}
@@ -229,9 +233,12 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
if (d->m_restoreOptions & RestoreOption_RelativeToMainWindow)
layout.scaleSizes();
d->floatWidgetsWhichSkipRestore(layout.mainWindowNames());
// Hide all dockwidgets and unparent them from any layout before starting restore
// We only close the stuff that the loaded JSON knows about. Unknown widgets might be newer.
d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetNames()),
d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetsToClose()),
d->m_dockRegistry->mainWindows(layout.mainWindowNames()),
d->m_affinityNames);
@@ -258,14 +265,15 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
}
// 2. Restore FloatingWindows
for (const LayoutSaver::FloatingWindow &fw : qAsConst(layout.floatingWindows)) {
if (!d->matchesAffinity(fw.affinities))
for (LayoutSaver::FloatingWindow &fw : layout.floatingWindows) {
if (!d->matchesAffinity(fw.affinities) || fw.skipsRestore())
continue;
MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
: DockRegistry::self()->mainwindows().at(fw.parentIndex);
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
fw.floatingWindowInstance = floatingWindow;
d->deserializeWindowGeometry(fw, floatingWindow);
if (!floatingWindow->deserialize(fw)) {
qWarning() << Q_FUNC_INFO << "Failed to deserialize floating window";
@@ -286,7 +294,7 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
continue;
if (DockWidgetBase *dockWidget = d->m_dockRegistry->dockByName(dw->uniqueName)) {
dockWidget->lastPositions().deserialize(dw->lastPosition);
dockWidget->d->lastPositions().deserialize(dw->lastPosition);
} else {
qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName;
}
@@ -332,6 +340,24 @@ void LayoutSaver::Private::deserializeWindowGeometry(const T &saved, QWidgetOrQu
topLevel->setVisible(saved.isVisible);
}
void LayoutSaver::Private::floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames)
{
// Widgets with the DockWidget::LayoutSaverOption::Skip flag skip restore completely.
// If they were visible before they need to remain visible now.
// If they were previously docked we need to float them, as the main window they were on will
// be loading a new layout.
for (MainWindowBase *mw : DockRegistry::self()->mainWindows(mainWindowNames)) {
const KDDockWidgets::DockWidgetBase::List docks = mw->layoutWidget()->dockWidgets();
for (auto dw : docks) {
if (dw->skipsRestore()) {
dw->setFloating(true);
}
}
}
}
void LayoutSaver::Private::deleteEmptyFrames()
{
// After a restore it can happen that some DockWidgets didn't exist, so weren't restored.
@@ -471,6 +497,14 @@ LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
return mainWindows.at(index);
}
LayoutSaver::FloatingWindow LayoutSaver::Layout::floatingWindowForIndex(int index) const
{
if (index < 0 || index >= floatingWindows.size())
return {};
return floatingWindows.at(index);
}
QStringList LayoutSaver::Layout::mainWindowNames() const
{
QStringList names;
@@ -493,6 +527,35 @@ QStringList LayoutSaver::Layout::dockWidgetNames() const
return names;
}
QStringList LayoutSaver::Layout::dockWidgetsToClose() const
{
// Before restoring a layout we close all dock widgets, unless they're a floating window with the DontCloseBeforeRestore flag
QStringList names;
names.reserve(allDockWidgets.size());
auto registry = DockRegistry::self();
for (const auto &dw : allDockWidgets) {
if (DockWidgetBase *dockWidget = registry->dockByName(dw->uniqueName)) {
bool doClose = true;
if (dockWidget->skipsRestore()) {
if (auto fw = dockWidget->floatingWindow()) {
if (fw->allDockWidgetsHave(DockWidgetBase::LayoutSaverOption::Skip)) {
// All dock widgets in this floating window skips float, so we can honour it for all.
doClose = false;
}
}
}
if (doClose)
names << dw->uniqueName;
}
}
return names;
}
bool LayoutSaver::Frame::isValid() const
{
if (isNull)
@@ -528,9 +591,24 @@ bool LayoutSaver::Frame::isValid() const
return true;
}
void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo)
bool LayoutSaver::Frame::hasSingleDockWidget() const
{
scalingInfo.applyFactorsTo(geometry);
return dockWidgets.size() == 1;
}
bool LayoutSaver::Frame::skipsRestore() const
{
return std::all_of(dockWidgets.cbegin(), dockWidgets.cend(), [] (LayoutSaver::DockWidget::Ptr dw) {
return dw->skipsRestore();
});
}
LayoutSaver::DockWidget::Ptr LayoutSaver::Frame::singleDockWidget() const
{
if (!hasSingleDockWidget())
return {};
return dockWidgets.first();
}
QVariantMap LayoutSaver::Frame::toVariantMap() const
@@ -583,6 +661,14 @@ void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
lastPosition.scaleSizes(scalingInfo);
}
bool LayoutSaver::DockWidget::skipsRestore() const
{
if (DockWidgetBase *dw = DockRegistry::self()->dockByName(uniqueName))
return dw->skipsRestore();
return false;
}
QVariantMap LayoutSaver::DockWidget::toVariantMap() const
{
QVariantMap map;
@@ -621,10 +707,24 @@ bool LayoutSaver::FloatingWindow::isValid() const
return true;
}
bool LayoutSaver::FloatingWindow::hasSingleDockWidget() const
{
return multiSplitterLayout.hasSingleDockWidget();
}
LayoutSaver::DockWidget::Ptr LayoutSaver::FloatingWindow::singleDockWidget() const
{
return multiSplitterLayout.singleDockWidget();
}
bool LayoutSaver::FloatingWindow::skipsRestore() const
{
return multiSplitterLayout.skipsRestore();
}
void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(/*by-ref*/geometry);
multiSplitterLayout.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
@@ -682,9 +782,6 @@ void LayoutSaver::MainWindow::scaleSizes()
}
scalingInfo = ScalingInfo(uniqueName, geometry);
if (scalingInfo.isValid())
multiSplitterLayout.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::MainWindow::toVariantMap() const
@@ -698,6 +795,7 @@ QVariantMap LayoutSaver::MainWindow::toVariantMap() const
map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
map.insert(QStringLiteral("isVisible"), isVisible);
map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
map.insert(QStringLiteral("windowState"), windowState);
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc);
@@ -718,6 +816,7 @@ void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
isVisible = map.value(QStringLiteral("isVisible")).toBool();
affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
windowState = Qt::WindowState(map.value(QStringLiteral("windowState"), Qt::WindowNoState).toInt());
// Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
@@ -747,11 +846,24 @@ bool LayoutSaver::MultiSplitter::isValid() const
return true;
}
void LayoutSaver::MultiSplitter::scaleSizes(const ScalingInfo &)
bool LayoutSaver::MultiSplitter::hasSingleDockWidget() const
{
// scalingInfo.applyFactorsTo(/*by-ref*/size);
//for (LayoutSaver::Item &item : items) TODO
// item.scaleSizes(scalingInfo);
return frames.size() == 1 && frames.cbegin()->hasSingleDockWidget();
}
LayoutSaver::DockWidget::Ptr LayoutSaver::MultiSplitter::singleDockWidget() const
{
if (!hasSingleDockWidget())
return {};
return frames.cbegin()->singleDockWidget();
}
bool LayoutSaver::MultiSplitter::skipsRestore() const
{
return std::all_of(frames.cbegin(), frames.cend(), [] (const LayoutSaver::Frame &frame) {
return frame.skipsRestore();
});
}
QVariantMap LayoutSaver::MultiSplitter::toVariantMap() const
@@ -883,8 +995,7 @@ void LayoutSaver::ScalingInfo::translatePos(QPoint &pt) const
void LayoutSaver::ScalingInfo::applyFactorsTo(QPoint &pt) const
{
pt.setX(qCeil(pt.x() * widthFactor));
pt.setY(qCeil(pt.y() * heightFactor));
translatePos(pt);
}
void LayoutSaver::ScalingInfo::applyFactorsTo(QSize &sz) const

View File

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

View File

@@ -18,26 +18,45 @@
#include "MainWindow.h"
#include "Config.h"
#include "DockRegistry_p.h"
#include "DropAreaWithCentralFrame_p.h"
#include "DropArea_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "Logging_p.h"
#include "SideBar_p.h"
#include "DropAreaWithCentralFrame_p.h"
#include "FrameworkWidgetFactory.h"
#include <QVBoxLayout>
#include <QPainter>
#include <QScreen>
#include <QVBoxLayout>
#include <QWindow>
// clazy:excludeall=ctor-missing-parent-argument,missing-qobject-macro
using namespace KDDockWidgets;
namespace KDDockWidgets {
class MyCentralWidget : public QWidget
{
public:
explicit MyCentralWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setObjectName(QStringLiteral("MyCentralWidget"));
}
~MyCentralWidget() override;
};
}
class MainWindow::Private
{
public:
explicit Private(MainWindowOptions, MainWindowBase *mainWindow)
: m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
explicit Private(MainWindowOptions, MainWindow *mainWindow)
: q(mainWindow)
, m_supportsAutoHide(Config::self().flags() & Config::Flag_AutoHideSupport)
, m_centralWidget(new MyCentralWidget(mainWindow))
, m_layout(new QHBoxLayout(m_centralWidget)) // 1 level of indirection so we can add some margins
{
if (m_supportsAutoHide) {
for (auto location : { SideBarLocation::North, SideBarLocation::East,
@@ -45,33 +64,23 @@ public:
m_sideBars.insert(location, Config::self().frameworkWidgetFactory()->createSideBar(location, mainWindow) );
}
}
m_layout->setSpacing(0);
updateMargins();
}
void updateMargins()
{
m_layout->setContentsMargins(q->centerWidgetMargins());
}
MainWindow *const q;
const bool m_supportsAutoHide;
QHash<SideBarLocation, SideBar*> m_sideBars;
MyCentralWidget *const m_centralWidget;
QHBoxLayout *const m_layout;
};
namespace KDDockWidgets {
class MyCentralWidget : public QWidget
{
public:
explicit MyCentralWidget(QWidget *parent = nullptr) : QWidget(parent)
{
setObjectName(QStringLiteral("MyCentralWidget"));
}
~MyCentralWidget() override;
void paintEvent(QPaintEvent *) override
{
QPainter p(this);
QPen pen(QColor(184, 184, 184, 184));
p.setPen(pen);
p.drawLine(0, 0, width(), 0);
}
};
}
MyCentralWidget::~MyCentralWidget() {}
@@ -80,26 +89,28 @@ MainWindow::MainWindow(const QString &name, MainWindowOptions options,
: MainWindowBase(name, options, parent, flags)
, d(new Private(options, this))
{
auto centralWidget = new MyCentralWidget(this);
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));
d->m_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(layoutWidget());
innerVLayout->addWidget(sideBar(SideBarLocation::South));
layout->addLayout(innerVLayout);
layout->addWidget(sideBar(SideBarLocation::East));
d->m_layout->addLayout(innerVLayout);
d->m_layout->addWidget(sideBar(SideBarLocation::East));
} else {
layout->addWidget(dropArea());
d->m_layout->addWidget(layoutWidget());
}
setCentralWidget(centralWidget);
setCentralWidget(d->m_centralWidget);
create();
connect(windowHandle(), &QWindow::screenChanged, DockRegistry::self(),
[this] {
d->updateMargins(); // logical dpi might have changed
Q_EMIT DockRegistry::self()->windowChangedScreen(windowHandle());
});
}
MainWindow::~MainWindow()
@@ -125,7 +136,8 @@ void MainWindow::resizeEvent(QResizeEvent *ev)
QMargins MainWindow::centerWidgetMargins() const
{
return { 1, 5, 1, 1};
const QMargins margins = { 1, 5, 1, 1 };
return margins * logicalDpiFactor(this);
}
QRect MainWindow::centralAreaGeometry() const

View File

@@ -19,6 +19,7 @@
#include "MainWindowBase.h"
#include "DockRegistry_p.h"
#include "MDILayoutWidget_p.h"
#include "DropArea_p.h"
#include "Frame_p.h"
#include "Utils_p.h"
@@ -27,16 +28,26 @@
#include "WidgetResizeHandler_p.h"
#include "FrameworkWidgetFactory.h"
#include "DropAreaWithCentralFrame_p.h"
#include "LayoutSaver_p.h"
#include "DockWidgetBase_p.h"
using namespace KDDockWidgets;
static LayoutWidget* createLayoutWidget(MainWindowBase *mainWindow, MainWindowOptions options)
{
if (options & MainWindowOption_MDI)
return new MDILayoutWidget(mainWindow);
return new DropAreaWithCentralFrame(mainWindow, options);
}
class MainWindowBase::Private
{
public:
explicit Private(MainWindowBase *mainWindow, MainWindowOptions options)
: m_options(options)
, q(mainWindow)
, m_dropArea(new DropAreaWithCentralFrame(mainWindow, options))
, m_layoutWidget(createLayoutWidget(mainWindow, options))
{
}
@@ -45,11 +56,11 @@ public:
return m_options & MainWindowOption_HasCentralFrame;
}
WidgetResizeHandler::CursorPositions allowedResizeSides(SideBarLocation loc) const;
CursorPositions allowedResizeSides(SideBarLocation loc) const;
QRect rectForOverlay(Frame *, SideBarLocation) const;
SideBarLocation preferredSideBar(DockWidgetBase *) const;
void updateOverlayGeometry(bool reusePreviousSize = false);
void updateOverlayGeometry(QSize suggestedSize);
void clearSideBars();
QString name;
@@ -57,7 +68,7 @@ public:
const MainWindowOptions m_options;
MainWindowBase *const q;
QPointer<DockWidgetBase> m_overlayedDockWidget;
DropAreaWithCentralFrame *const m_dropArea;
LayoutWidget *const m_layoutWidget;
};
MainWindowBase::MainWindowBase(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
@@ -90,6 +101,11 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
return;
}
if (isMDI()) {
// Not applicable to MDI
return;
}
if (d->supportsCentralFrame()) {
dropArea()->m_centralFrame->addWidget(widget);
} else {
@@ -105,6 +121,11 @@ void MainWindowBase::addDockWidget(DockWidgetBase *dw, Location location,
return;
}
if (isMDI()) {
// Not applicable to MDI
return;
}
dropArea()->addDockWidget(dw, location, relativeTo, option);
}
@@ -120,7 +141,7 @@ MainWindowOptions MainWindowBase::options() const
DropAreaWithCentralFrame *MainWindowBase::dropArea() const
{
return d->m_dropArea;
return qobject_cast<DropAreaWithCentralFrame *>(d->m_layoutWidget);
}
MultiSplitter *MainWindowBase::multiSplitter() const
@@ -128,6 +149,16 @@ MultiSplitter *MainWindowBase::multiSplitter() const
return dropArea();
}
LayoutWidget *MainWindowBase::layoutWidget() const
{
return d->m_layoutWidget;
}
MDILayoutWidget *MainWindowBase::mdiLayoutWidget() const
{
return qobject_cast<MDILayoutWidget *>(layoutWidget());
}
void MainWindowBase::setAffinities(const QStringList &affinityNames)
{
QStringList affinities = affinityNames;
@@ -161,25 +192,25 @@ void MainWindowBase::layoutParentContainerEqually(DockWidgetBase *dockWidget)
dropArea()->layoutParentContainerEqually(dockWidget);
}
WidgetResizeHandler::CursorPositions MainWindowBase::Private::allowedResizeSides(SideBarLocation loc) const
CursorPositions MainWindowBase::Private::allowedResizeSides(SideBarLocation loc) const
{
// When a sidebar is on top, you can only resize its bottom.
// and so forth...
switch (loc) {
case SideBarLocation::North:
return WidgetResizeHandler::CursorPosition_Bottom;
return CursorPosition_Bottom;
case SideBarLocation::East:
return WidgetResizeHandler::CursorPosition_Left;
return CursorPosition_Left;
case SideBarLocation::West:
return WidgetResizeHandler::CursorPosition_Right;
return CursorPosition_Right;
case SideBarLocation::South:
return WidgetResizeHandler::CursorPosition_Top;
return CursorPosition_Top;
case SideBarLocation::None:
return WidgetResizeHandler::CursorPosition_Undefined;
return CursorPosition_Undefined;
}
return WidgetResizeHandler::CursorPosition_Undefined;
return CursorPosition_Undefined;
}
QRect MainWindowBase::Private::rectForOverlay(Frame *frame, SideBarLocation location) const
@@ -286,7 +317,7 @@ SideBarLocation MainWindowBase::Private::preferredSideBar(DockWidgetBase *dw) co
{
// TODO: Algorithm can still be made smarter
Layouting::Item *item = q->multiSplitter()->itemForFrame(dw->frame());
Layouting::Item *item = q->layoutWidget()->itemForFrame(dw->d->frame());
if (!item) {
qWarning() << Q_FUNC_INFO << "No item for dock widget";
return SideBarLocation::None;
@@ -348,7 +379,7 @@ SideBarLocation MainWindowBase::Private::preferredSideBar(DockWidgetBase *dw) co
: SideBarLocation::West;
}
void MainWindowBase::Private::updateOverlayGeometry(bool reusePreviousSize)
void MainWindowBase::Private::updateOverlayGeometry(QSize suggestedSize)
{
if (!m_overlayedDockWidget)
return;
@@ -359,46 +390,45 @@ void MainWindowBase::Private::updateOverlayGeometry(bool reusePreviousSize)
return;
}
const QRect defaultGeometry = rectForOverlay(m_overlayedDockWidget->frame(), sb->location());
const QRect defaultGeometry = rectForOverlay(m_overlayedDockWidget->d->frame(), sb->location());
QRect newGeometry = defaultGeometry;
Frame *frame = m_overlayedDockWidget->frame();
Frame *frame = m_overlayedDockWidget->d->frame();
if (reusePreviousSize) {
// Let's try to honour the previous overlay size
if (suggestedSize.isValid() && !suggestedSize.isEmpty()) {
// Let's try to honour the suggested overlay size
switch (sb->location()) {
case SideBarLocation::North: {
const int maxHeight = q->height() - frame->pos().y() - 10; // gap
newGeometry.setHeight(qMin(frame->height(), maxHeight));
newGeometry.setHeight(qMin(suggestedSize.height(), maxHeight));
break;
}
case SideBarLocation::South: {
const int maxHeight = sb->pos().y() - m_dropArea->pos().y() - 10; // gap
const int maxHeight = sb->pos().y() - m_layoutWidget->pos().y() - 10; // gap
const int bottom = newGeometry.bottom();
newGeometry.setHeight(qMin(frame->height(), maxHeight));
newGeometry.setHeight(qMin(suggestedSize.height(), maxHeight));
newGeometry.moveBottom(bottom);
break;
}
case SideBarLocation::East: {
const int maxWidth = sb->pos().x() - m_dropArea->pos().x() - 10; // gap
const int maxWidth = sb->pos().x() - m_layoutWidget->pos().x() - 10; // gap
const int right = newGeometry.right();
newGeometry.setWidth(qMin(frame->width(), maxWidth));
newGeometry.setWidth(qMin(suggestedSize.width(), maxWidth));
newGeometry.moveRight(right);
break;
}
case SideBarLocation::West: {
const int maxWidth = q->width() - frame->pos().x() - 10; // gap
newGeometry.setWidth(qMin(frame->height(), maxWidth));
newGeometry.setWidth(qMin(suggestedSize.width(), maxWidth));
break;
}
case SideBarLocation::None:
qWarning() << Q_FUNC_INFO << "Unexpected sidebar value";
break;
}
}
m_overlayedDockWidget->frame()->QWidgetAdapter::setGeometry(newGeometry);
m_overlayedDockWidget->d->frame()->QWidgetAdapter::setGeometry(newGeometry);
}
void MainWindowBase::Private::clearSideBars()
@@ -418,6 +448,7 @@ void MainWindowBase::moveToSideBar(DockWidgetBase *dw)
void MainWindowBase::moveToSideBar(DockWidgetBase *dw, SideBarLocation location)
{
if (SideBar *sb = sideBar(location)) {
QScopedValueRollback<bool> rollback(dw->d->m_isMovingToSideBar, true);
dw->forceClose();
sb->addDockWidget(dw);
} else {
@@ -465,12 +496,9 @@ void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
auto frame = Config::self().frameworkWidgetFactory()->createFrame(this, FrameOption_IsOverlayed);
d->m_overlayedDockWidget = dw;
frame->addWidget(dw);
d->updateOverlayGeometry(/*reusePreviousSize=*/ false);
// Uncomment once I'm happy with the resizing
auto resizeHandler = new WidgetResizeHandler(true, frame);
resizeHandler->setAllowedResizeSides(d->allowedResizeSides(sb->location()));
d->updateOverlayGeometry(dw->d->lastPositions().lastOverlayedGeometry(sb->location()).size());
frame->setAllowedResizeSides(d->allowedResizeSides(sb->location()));
frame->QWidgetAdapter::show();
Q_EMIT dw->isOverlayedChanged(true);
@@ -485,16 +513,29 @@ void MainWindowBase::toggleOverlayOnSideBar(DockWidgetBase *dw)
}
}
void MainWindowBase::clearSideBarOverlay()
void MainWindowBase::clearSideBarOverlay(bool deleteFrame)
{
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;
Frame *frame = d->m_overlayedDockWidget->d->frame();
const SideBarLocation loc = d->m_overlayedDockWidget->sideBarLocation();
d->m_overlayedDockWidget->d->lastPositions().setLastOverlayedGeometry(
loc, frame->QWidgetAdapter::geometry());
frame->unoverlay();
if (deleteFrame) {
d->m_overlayedDockWidget->setParent(nullptr);
Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false);
d->m_overlayedDockWidget = nullptr;
delete frame;
} else {
// No cleanup, just unset. When we drag the overlay it becomes a normal floating window
// meaning we reuse Frame. Don't delete it.
Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false);
d->m_overlayedDockWidget = nullptr;
}
}
SideBar *MainWindowBase::sideBarForDockWidget(const DockWidgetBase *dw) const
@@ -536,6 +577,11 @@ bool MainWindowBase::anySideBarIsVisible() const
return false;
}
bool MainWindowBase::isMDI() const
{
return d->m_options & MainWindowOption_MDI;
}
void MainWindowBase::setUniqueName(const QString &uniqueName)
{
if (uniqueName.isEmpty())
@@ -553,7 +599,7 @@ void MainWindowBase::setUniqueName(const QString &uniqueName)
void MainWindowBase::onResized(QResizeEvent *)
{
if (d->m_overlayedDockWidget)
d->updateOverlayGeometry(/*reusePreviousSize=*/ true);
d->updateOverlayGeometry(d->m_overlayedDockWidget->d->frame()->QWidgetAdapter::size());
}
bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
@@ -571,7 +617,7 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
d->affinities = mw.affinities;
}
const bool success = dropArea()->deserialize(mw.multiSplitterLayout);
const bool success = layoutWidget()->deserialize(mw.multiSplitterLayout);
// Restore the SideBars
d->clearSideBars();
@@ -594,6 +640,12 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
}
}
if (mw.windowState != Qt::WindowNoState) {
if (auto w = windowHandle()) {
w->setWindowState(mw.windowState);
}
}
// Commented-out for now, we dont' want to restore the popup/overlay. popups are perishable
//if (!mw.overlayedDockWidget.isEmpty())
// overlayOnSideBar(DockRegistry::self()->dockByName(mw.overlayedDockWidget));
@@ -611,8 +663,10 @@ LayoutSaver::MainWindow MainWindowBase::serialize() const
m.uniqueName = uniqueName();
m.screenIndex = screenNumberForWidget(this);
m.screenSize = screenSizeForWidget(this);
m.multiSplitterLayout = dropArea()->serialize();
m.multiSplitterLayout = layoutWidget()->serialize();
m.affinities = d->affinities;
m.windowState = windowHandle() ? windowHandle()->windowState()
: Qt::WindowNoState;
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
if (SideBar *sb = sideBar(loc)) {

View File

@@ -23,7 +23,7 @@
#include "docks_export.h"
#include "KDDockWidgets.h"
#include "QWidgetAdapter.h"
#include "LayoutSaver_p.h"
#include "LayoutSaver.h"
#include <QVector>
#include <QMargins>
@@ -35,7 +35,9 @@ namespace KDDockWidgets {
class DockWidgetBase;
class Frame;
class DropArea;
class MDILayoutWidget;
class MultiSplitter;
class LayoutWidget;
class DropAreaWithCentralFrame;
class SideBar;
@@ -100,7 +102,15 @@ public:
///@internal
///@brief returns the MultiSplitter.
MultiSplitter* multiSplitter() const;
MultiSplitter *multiSplitter() const;
///@internal
///@brief returns the MultiSplitter.
LayoutWidget *layoutWidget() const;
///@internal
///@brief Returns the MDI layout. Or nullptr if this isn't a MDI main window
MDILayoutWidget *mdiLayoutWidget() const;
/**
* @brief Sets the affinities names. Dock widgets can only dock into main windows of the same affinity.
@@ -159,7 +169,7 @@ public:
void toggleOverlayOnSideBar(DockWidgetBase *);
/// @brief closes any overlayed dock widget. The sidebar still displays them as button.
void clearSideBarOverlay();
void clearSideBarOverlay(bool deleteFrame = true);
/// @brief Returns the sidebar this dockwidget is in. nullptr if not in any.
SideBar *sideBarForDockWidget(const DockWidgetBase *) const;
@@ -174,6 +184,10 @@ public:
/// @brief Returns whether any side bar is visible
bool anySideBarIsVisible() const;
/// @brief Returns whether this main window is using an MDI layout.
/// In other words, returns true if MainWindowOption_MDI was passed in the ctor.
bool isMDI() const;
protected:
void setUniqueName(const QString &uniqueName);
void onResized(QResizeEvent *); // Because QtQuick doesn't have resizeEvent()

35
src/MainWindowMDI.cpp Normal file
View File

@@ -0,0 +1,35 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MainWindowMDI.h"
#include "MDILayoutWidget_p.h"
using namespace KDDockWidgets;
MainWindowMDI::MainWindowMDI(const QString &uniqueName, QWidgetOrQuick *parent, Qt::WindowFlags flags)
: MDIMainWindowBase(uniqueName, MainWindowOption_MDI, parent, flags)
{
}
MainWindowMDI::~MainWindowMDI()
{
}
void MainWindowMDI::addDockWidget(DockWidgetBase *dockWidget, QPoint localPos, InitialOption addingOption)
{
auto layout = static_cast<MDILayoutWidget *>(this->layoutWidget());
layout->addDockWidget(dockWidget, localPos, addingOption);
}
void MainWindowMDI::addDockWidget(DockWidgetBase *dockWidget, QPointF localPos, InitialOption addingOption)
{
MainWindowMDI::addDockWidget(dockWidget, localPos.toPoint(), addingOption);
}

53
src/MainWindowMDI.h Normal file
View File

@@ -0,0 +1,53 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
/**
* @file
* @brief MainWindow sub-class which uses MDI as a layout
*
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#ifndef KD_MAINWINDOW_MDI_H
#define KD_MAINWINDOW_MDI_H
#ifdef KDDOCKWIDGETS_QTWIDGETS
# include "MainWindow.h"
#else
# include "private/quick/MainWindowQuick_p.h"
#endif
namespace KDDockWidgets {
/// @brief MainWindow sub-class which uses MDI as a layout
class DOCKS_EXPORT MainWindowMDI : public KDDockWidgets::MDIMainWindowBase
{
Q_OBJECT
public:
///@brief Constructor. See base class documentation
explicit MainWindowMDI(const QString &uniqueName, QWidgetOrQuick *parent = nullptr,
Qt::WindowFlags flags = Qt::WindowFlags());
///@brief Destructor
~MainWindowMDI() override;
///@brief Docks @p dockWidget
/// The widget will be placed at the specified position
void addDockWidget(DockWidgetBase *dockWidget, QPoint localPos, InitialOption addingOption = {});
///@brief Convenience overload
void addDockWidget(DockWidgetBase *dockWidget, QPointF localPos, InitialOption addingOption = {});
};
}
#endif

View File

@@ -51,6 +51,7 @@ inline bool isMinimized(QWindow *window)
typedef QMainWindow QMainWindowOrQuick;
typedef Layouting::Widget_qwidget LayoutGuestWidgetBase;
typedef KDDockWidgets::MainWindow MainWindowType;
typedef KDDockWidgets::MainWindow MDIMainWindowBase;
typedef KDDockWidgets::DockWidget DockWidgetType;
typedef QWidget WidgetType;
}
@@ -64,12 +65,17 @@ inline bool isMinimized(QWindow *window)
typedef QWidgetOrQuick QMainWindowOrQuick;
typedef Layouting::Widget_quick LayoutGuestWidgetBase;
typedef KDDockWidgets::MainWindowQuick MainWindowType;
typedef KDDockWidgets::MainWindowQuick MDIMainWindowBase;
typedef KDDockWidgets::DockWidgetQuick DockWidgetType;
typedef QQuickItem WidgetType;
}
#endif
namespace KDDockWidgets {
/// @brief LayoutGuestWidget is is the type that Item will host.
///
/// The layouting deals in items, represented by Item. Each item wraps a QWidget (or QQuickItem),
/// such widgets derive from LayoutGuestWidget.
class LayoutGuestWidget : public KDDockWidgets::QWidgetAdapter
, public LayoutGuestWidgetBase
{

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-FileCopyrightText: 2020-2021 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

View File

@@ -9,4 +9,4 @@
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../../LayoutSaver_p.h"
#include "../../MainWindowMDI.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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/DragController_p.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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/Frame_p.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 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/WidgetResizeHandler_p.h"

View File

@@ -4,6 +4,8 @@
<file>private/quick/qml/DropArea.qml</file>
<file>private/quick/qml/FloatingWindow.qml</file>
<file>private/quick/qml/Frame.qml</file>
<file>private/quick/qml/MainWindowMDI.qml</file>
<file>private/quick/qml/ResizeHandlerHelper.qml</file>
<file>private/quick/qml/RubberBand.qml</file>
<file>private/quick/qml/TitleBarBase.qml</file>
<file>private/quick/qml/TitleBar.qml</file>

View File

@@ -17,12 +17,12 @@
*/
#include "DebugWindow_p.h"
#include "ObjectViewer_p.h"
#include "DockRegistry_p.h"
#include "FloatingWindow_p.h"
#include "DropArea_p.h"
#include "MainWindow.h"
#include "LayoutSaver.h"
#include "LayoutWidget_p.h"
#include "MainWindow.h"
#include "ObjectViewer_p.h"
#include "Qt5Qt6Compat_p.h"
#include <QVBoxLayout>
@@ -197,12 +197,12 @@ DebugWindow::DebugWindow(QWidget *parent)
connect(button, &QPushButton::clicked, this, [] {
const auto mainWindows = DockRegistry::self()->mainwindows();
for (MainWindowBase *mainWindow : mainWindows) {
mainWindow->multiSplitter()->checkSanity();
mainWindow->layoutWidget()->checkSanity();
}
const auto floatingWindows = DockRegistry::self()->floatingWindows();
for (FloatingWindow *floatingWindow : floatingWindows) {
floatingWindow->multiSplitter()->checkSanity();
floatingWindow->layoutWidget()->checkSanity();
}
});
@@ -314,12 +314,12 @@ void DebugWindow::dumpDockWidgetInfo()
for (FloatingWindow *fw : floatingWindows) {
qDebug() << fw << "; affinities=" << fw->affinities();
fw->dropArea()->dumpLayout();
fw->layoutWidget()->dumpLayout();
}
for (MainWindowBase *mw : mainWindows) {
qDebug() << mw << "; affinities=" << mw->affinities();
mw->multiSplitter()->dumpLayout();
mw->layoutWidget()->dumpLayout();
}
for (DockWidgetBase *dw : dockWidgets) {

View File

@@ -9,17 +9,20 @@
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "Config.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase.h"
#include "Logging_p.h"
#include "Position_p.h"
#include "MultiSplitter_p.h"
#include "QWidgetAdapter.h"
#include "Config.h"
#include "SideBar_p.h"
#include "WindowBeingDragged_p.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "LayoutWidget_p.h"
#include "Logging_p.h"
#include "MainWindowMDI.h"
#include "Position_p.h"
#include "QWidgetAdapter.h"
#include "SideBar_p.h"
#include "Utils_p.h"
#include "WidgetResizeHandler_p.h"
#include "WindowBeingDragged_p.h"
#include <QPointer>
#include <QDebug>
@@ -47,9 +50,10 @@ static void initKDDockWidgetResources()
DockRegistry::DockRegistry(QObject *parent)
: QObject(parent)
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
qApp->installEventFilter(this);
#ifdef KDDOCKWIDGETS_QTWIDGETS
# ifdef DOCKS_DEVELOPER_MODE
if (qEnvironmentVariableIntValue("KDDOCKWIDGETS_SHOW_DEBUG_WINDOW") == 1) {
auto dv = new Debug::DebugWindow();
@@ -222,6 +226,25 @@ SideBar *DockRegistry::sideBarForDockWidget(const DockWidgetBase *dw) const
return nullptr;
}
Frame *DockRegistry::frameInMDIResize() const
{
for (auto mw : m_mainWindows) {
if (!mw->isMDI())
continue;
LayoutWidget *layout = mw->layoutWidget();
const QList<Frame *> frames = layout->frames();
for (Frame *frame : frames) {
if (WidgetResizeHandler *wrh = frame->resizeHandler()) {
if (wrh->isResizing())
return frame;
}
}
}
return nullptr;
}
MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &affinities) const
{
MainWindowBase::List result;
@@ -236,12 +259,12 @@ MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &af
return result;
}
MultiSplitter *DockRegistry::layoutForItem(const Layouting::Item *item) const
LayoutWidget *DockRegistry::layoutForItem(const Layouting::Item *item) const
{
if (!item->hostWidget())
return nullptr;
if (auto ms = qobject_cast<MultiSplitter*>(item->hostWidget()->asQObject()))
if (auto ms = qobject_cast<LayoutWidget *>(item->hostWidget()->asQObject()))
return ms;
return nullptr;
@@ -314,12 +337,12 @@ void DockRegistry::unregisterFloatingWindow(FloatingWindow *window)
maybeDelete();
}
void DockRegistry::registerLayout(MultiSplitter *layout)
void DockRegistry::registerLayout(LayoutWidget *layout)
{
m_layouts << layout;
}
void DockRegistry::unregisterLayout(MultiSplitter *layout)
void DockRegistry::unregisterLayout(LayoutWidget *layout)
{
m_layouts.removeOne(layout);
}
@@ -359,6 +382,11 @@ MainWindowBase *DockRegistry::mainWindowByName(const QString &name) const
return nullptr;
}
MainWindowMDI *DockRegistry::mdiMainWindowByName(const QString &name) const
{
return qobject_cast<MainWindowMDI *>(mainWindowByName(name));
}
DockWidgetBase *DockRegistry::dockWidgetForGuest(QWidgetOrQuick *guest) const
{
if (!guest)
@@ -457,7 +485,7 @@ const MainWindowBase::List DockRegistry::mainwindows() const
return m_mainWindows;
}
const QVector<MultiSplitter *> DockRegistry::layouts() const
const QVector<LayoutWidget *> DockRegistry::layouts() const
{
return m_layouts;
}
@@ -505,6 +533,18 @@ bool DockRegistry::hasFloatingWindows() const
});
}
QWindow *DockRegistry::windowForHandle(WId id) const
{
const QWindowList windows = qApp->topLevelWindows();
for (QWindow *w : windows) {
if (w->isVisible() && w->handle()) {
if (w->winId() == id)
return w;
}
}
return nullptr;
}
FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) const
{
for (FloatingWindow *fw : m_floatingWindows) {
@@ -591,7 +631,7 @@ void DockRegistry::clear(const DockWidgetBase::List &dockWidgets,
for (auto dw : qAsConst(dockWidgets)) {
if (affinities.isEmpty() || affinitiesMatch(affinities, dw->affinities())) {
dw->forceClose();
dw->lastPositions().removePlaceholders();
dw->d->lastPositions().removePlaceholders();
}
}
@@ -606,7 +646,7 @@ void DockRegistry::ensureAllFloatingWidgetsAreMorphed()
{
for (DockWidgetBase *dw : qAsConst(m_dockWidgets)) {
if (dw->window() == dw && dw->isVisible())
dw->morphIntoFloatingWindow();
dw->d->morphIntoFloatingWindow();
}
}
@@ -626,6 +666,13 @@ bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
}
}
} else if (event->type() == QEvent::MouseButtonPress) {
// When clicking on a MDI Frame we raise the window
if (Frame *f = parentFrame(watched)) {
if (f->isMDI())
f->raise();
}
// The following code is for hididng the overlay
if (!(Config::self().flags() & Config::Flag_AutoHideSupport))
return false;
@@ -637,7 +684,16 @@ bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
auto p = watched;
while (p) {
if (auto dw = qobject_cast<DockWidgetBase*>(p))
return onDockWidgetPressed(dw, static_cast<QMouseEvent*>(event));
return onDockWidgetPressed(dw, static_cast<QMouseEvent *>(event));
if (auto layoutWidget = qobject_cast<LayoutWidget *>(p)) {
if (auto mw = layoutWidget->mainWindow()) {
// The user clicked somewhere in the main window's drop area, but outside of the
// overlayed dock widget
mw->clearSideBarOverlay();
return false;
}
}
p = p->parent();
}
@@ -651,13 +707,19 @@ bool DockRegistry::onDockWidgetPressed(DockWidgetBase *dw, QMouseEvent *ev)
// Here we implement "auto-hide". If there's a overlayed dock widget, we hide it if some other
// dock widget is clicked.
#ifdef KDDOCKWIDGETS_QTWIDGETS
// Don't be sending mouse events around if a popup is open, they are sensitive
if (qApp->activePopupWidget())
return false;
#endif
MainWindowBase *mainWindow = dw->mainWindow();
if (!mainWindow) // Only docked widgets are interesting
return false;
if (DockWidgetBase *overlayedDockWidget = mainWindow->overlayedDockWidget()) {
ev->ignore();
qApp->sendEvent(overlayedDockWidget->frame(), ev);
qApp->sendEvent(overlayedDockWidget->d->frame(), ev);
if (ev->isAccepted()) {
// The Frame accepted it. It means the user is resizing it. We allow for 4px outside for better resize.

View File

@@ -12,8 +12,10 @@
#ifndef KD_DOCKREGISTRY_P_H
#define KD_DOCKREGISTRY_P_H
#include "../DockWidgetBase.h"
#include "../MainWindowBase.h"
#include "kddockwidgets/DockWidgetBase.h"
#include "kddockwidgets/MainWindowBase.h"
#include "kddockwidgets/private/Frame_p.h"
#include <QVector>
#include <QObject>
@@ -29,12 +31,16 @@ namespace KDDockWidgets
class FloatingWindow;
class Frame;
class LayoutWidget;
class MainWindowMDI;
class SideBar;
struct WindowBeingDragged;
class DOCKS_EXPORT DockRegistry : public QObject
{
Q_OBJECT
Q_PROPERTY(
KDDockWidgets::Frame *frameInMDIResize READ frameInMDIResize NOTIFY frameInMDIResizeChanged)
public:
static DockRegistry *self();
~DockRegistry();
@@ -47,8 +53,8 @@ public:
void registerFloatingWindow(FloatingWindow *);
void unregisterFloatingWindow(FloatingWindow *);
void registerLayout(MultiSplitter *);
void unregisterLayout(MultiSplitter *);
void registerLayout(LayoutWidget *);
void unregisterLayout(LayoutWidget *);
void registerFrame(Frame *);
void unregisterFrame(Frame *);
@@ -56,8 +62,9 @@ public:
DockWidgetBase *focusedDockWidget() const;
bool containsDockWidget(const QString &uniqueName) const;
DockWidgetBase *dockByName(const QString &) const;
Q_INVOKABLE KDDockWidgets::DockWidgetBase *dockByName(const QString &) const;
MainWindowBase *mainWindowByName(const QString &) const;
MainWindowMDI *mdiMainWindowByName(const QString &) const;
/// @brief returns the dock widget that hosts @p guest widget. Nullptr if there's none.
DockWidgetBase *dockWidgetForGuest(QWidgetOrQuick *guest) const;
@@ -79,8 +86,8 @@ public:
///@brief overload returning only the ones with the specified names
const MainWindowBase::List mainWindows(const QStringList &names);
///@brief returns the list of MultiSplitter instances
const QVector<MultiSplitter*> layouts() const;
///@brief returns the list of LayoutWidget instances
const QVector<LayoutWidget *> layouts() const;
///@brief returns a list of all Frame instances
const QList<Frame*> frames() const;
@@ -95,6 +102,9 @@ public:
///@brief returns whether if there's at least one floating window
bool hasFloatingWindows() const;
///@brief Returns the window with the specified id
QWindow *windowForHandle(WId id) const;
///@brief returns the FloatingWindow with handle @p windowHandle
FloatingWindow *floatingWindowForHandle(QWindow *windowHandle) const;
@@ -150,12 +160,12 @@ public:
bool isEmpty(bool excludeBeingDeleted = false) const;
/**
* @brief Calls MultiSplitter::checkSanity() on all layouts.
* @brief Calls LayoutWidget::checkSanity() on all layouts.
*
* @param dumpDebug If true then each layout is dumped too
*
* This is called by the unit-tests or the fuzzer. If during this the framework spits a qWarning()
* then the app will qFatal()
* This is called by the unit-tests or the fuzzer. If during this the framework spits a
* qWarning() then the app will qFatal()
*/
void checkSanityAll(bool dumpDebug = false);
@@ -172,7 +182,7 @@ public:
MainWindowBase::List mainWindowsWithAffinity(const QStringList &affinities) const;
// TODO: docs
MultiSplitter* layoutForItem(const Layouting::Item *) const;
LayoutWidget *layoutForItem(const Layouting::Item *) const;
// TODO: docs
bool itemIsInMainWindow(const Layouting::Item *) const;
@@ -202,7 +212,17 @@ public:
SideBarLocation sideBarLocationForDockWidget(const DockWidgetBase *) const;
///@brief Overload that returns the SideBar itself
SideBar* sideBarForDockWidget(const DockWidgetBase *) const;
SideBar *sideBarForDockWidget(const DockWidgetBase *) const;
///@brief Returns the Frame which is being resized in a MDI layout. nullptr if none
Frame *frameInMDIResize() const;
Q_SIGNALS:
/// @brief emitted when a main window or a floating window change screen
void windowChangedScreen(QWindow *);
/// @brief emitted when the MDI frame that's being resized changed
void frameInMDIResizeChanged();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
@@ -219,7 +239,7 @@ private:
MainWindowBase::List m_mainWindows;
QList<Frame*> m_frames;
QVector<FloatingWindow*> m_floatingWindows;
QVector<MultiSplitter*> m_layouts;
QVector<LayoutWidget *> m_layouts;
QPointer<DockWidgetBase> m_focusedDockWidget;
};

View File

@@ -0,0 +1,242 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef KD_DOCKWIDGET_BASE_P_H
#define KD_DOCKWIDGET_BASE_P_H
#include "DockWidgetBase.h"
#include "SideBar_p.h"
#include "DockRegistry_p.h"
#include "Position_p.h"
#include "FloatingWindow_p.h"
#include <QCoreApplication>
#include <QString>
#include <QSize>
QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE
namespace KDDockWidgets {
class DOCKS_EXPORT_FOR_UNIT_TESTS DockWidgetBase::Private : public QObject /// clazy:exclude=missing-qobject-macro
{
public:
Private(const QString &dockName, DockWidgetBase::Options options_,
LayoutSaverOptions layoutSaverOptions_, DockWidgetBase *qq);
void init()
{
updateTitle();
}
/**
* @brief returns the FloatingWindow this dock widget is in. If nullptr then it's in a
* MainWindow.
*
* Note: Being in a FloatingWindow doesn't necessarily mean @ref isFloating() returns true, as
* the dock widget might be in a floating window with other dock widgets side by side.
*/
FloatingWindow *floatingWindow() const
{
return qobject_cast<FloatingWindow*>(q->window());
}
MainWindowBase *mainWindow() const
{
if (q->isWindow())
return nullptr;
// Note: Don't simply use window(), as the MainWindow might be embedded into something else
QWidgetOrQuick *p = q->parentWidget();
while (p) {
if (auto window = qobject_cast<MainWindowBase*>(p))
return window;
if (p->isWindow())
return nullptr;
p = p->parentWidget();
}
return nullptr;
}
SideBar* sideBar() const
{
return DockRegistry::self()->sideBarForDockWidget(q);
}
///@brief adds the current layout item containing this dock widget
void addPlaceholderItem(Layouting::Item *);
///@brief returns the last position, just for tests.
LastPositions &lastPositions();
void forceClose();
QPoint defaultCenterPosForFloating();
bool eventFilter(QObject *watched, QEvent *event) override;
void updateTitle();
void toggle(bool enabled);
void updateToggleAction();
void updateFloatAction();
void onDockWidgetShown();
void onDockWidgetHidden();
void show();
void close();
bool restoreToPreviousPosition();
void maybeRestoreToPreviousPosition();
int currentTabIndex() const;
/**
* @brief Serializes this dock widget into an intermediate form
*/
std::shared_ptr<LayoutSaver::DockWidget> serialize() const;
/**
* @brief the Frame which contains this dock widgets.
*
* A frame wraps a docked DockWidget, giving it a TabWidget so it can accept other dock widgets.
* Frame is also the actual class that goes into a LayoutWidget.
*
* It's nullptr immediately after creation.
*/
Frame *frame() const;
///@brief If this dock widget is floating, then it saves its geometry
void saveLastFloatingGeometry();
/**
* Before floating a dock widget we save its position. So it can be restored when calling
* DockWidget::setFloating(false)
*/
void saveTabIndex();
/**
* @brief Creates a FloatingWindow and adds itself into it
* @return the created FloatingWindow
*/
KDDockWidgets::FloatingWindow *morphIntoFloatingWindow();
/// @brief calls morphIntoFloatingWindow() if the dock widget is visible and is a top-level
/// This is called delayed whenever we show a floating dock widget, so we get a FloatingWindow
void maybeMorphIntoFloatingWindow();
/// @brief Returns the mdi layout this dock widget is in, if any.
MDILayoutWidget *mdiLayout() const;
const QString name;
QStringList affinities;
QString title;
QIcon titleBarIcon;
QIcon tabBarIcon;
QWidgetOrQuick *widget = nullptr;
DockWidgetBase *const q;
DockWidgetBase::Options options;
const LayoutSaverOptions layoutSaverOptions;
QAction *const toggleAction;
QAction *const floatAction;
LastPositions m_lastPositions;
bool m_processingToggleAction = false;
bool m_updatingToggleAction = false;
bool m_updatingFloatAction = false;
bool m_isForceClosing = false;
bool m_isMovingToSideBar = false;
QSize m_lastOverlayedSize = QSize(0, 0);
int m_userType = 0;
};
}
#if defined(QT_WIDGETS_LIB)
#include <QAction>
#else
// A QAction for QtQuick. Just so it compiles, for now
class QAction : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
bool isChecked() const
{
return m_isChecked;
}
void setCheckable(bool is)
{
m_isCheckable = is;
}
void setText(const QString &text)
{
m_text = text;
}
void setToolTip(const QString &text)
{
m_toolTip = text;
}
QString toolTip() const
{
returm m_toolTip;
}
bool enabled() const
{
return m_enabled;
}
void setEnabled(bool enabled)
{
m_enabled = enabled;
}
bool checked() const
{
return m_checked;
}
void setChecked(bool checked)
{
m_checked = checked;
}
bool isEnabled() const
{
return m_enabled;
}
void toggle()
{
m_enabled = !m_enabled;
Q_EMIT toggled(m_enabled);
}
Q_SIGNALS:
bool toggled(bool);
private:
QString m_text;
QString m_toolTip;
bool m_isChecked = false;
bool m_isCheckable = false;
bool m_enabled = false;
bool m_checked = false;
};
#endif
#endif

View File

@@ -10,14 +10,17 @@
*/
#include "DragController_p.h"
#include "Frame_p.h"
#include "Logging_p.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase_p.h"
#include "DropArea_p.h"
#include "FloatingWindow_p.h"
#include "WidgetResizeHandler_p.h"
#include "Utils_p.h"
#include "DockRegistry_p.h"
#include "Frame_p.h"
#include "Logging_p.h"
#include "Qt5Qt6Compat_p.h"
#include "Utils_p.h"
#include "WidgetResizeHandler_p.h"
#include "Config.h"
#include "MDILayoutWidget_p.h"
#include <QMouseEvent>
#include <QGuiApplication>
@@ -170,6 +173,8 @@ void StateNone::onEntry()
q->m_currentDropArea->removeHover();
q->m_currentDropArea = nullptr;
}
Q_EMIT q->isDraggingChanged();
}
bool StateNone::handleMouseButtonPress(Draggable *draggable, QPoint globalPos, QPoint pos)
@@ -213,7 +218,10 @@ bool StatePreDrag::handleMouseMove(QPoint globalPos)
}
if (q->m_draggable->dragCanStart(q->m_pressPos, globalPos)) {
Q_EMIT q->manhattanLengthMove();
if (q->m_draggable->isMDI())
Q_EMIT q->manhattanLengthMoveMDI();
else
Q_EMIT q->manhattanLengthMove();
return true;
}
return false;
@@ -246,25 +254,34 @@ void StateDragging::onEntry()
if (DockWidgetBase *dw = q->m_draggable->singleDockWidget()) {
// When we start to drag a floating window which has a single dock widget, we save the position
if (dw->isFloating())
dw->saveLastFloatingGeometry();
dw->d->saveLastFloatingGeometry();
}
const bool needsUndocking = !q->m_draggable->isWindow();
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 QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) && defined(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
// but now that we're dragging it as a floating window, switch to native drag, so we can still get aero-snap
FloatingWindow *fw = q->m_windowBeingDragged->floatingWindow();
q->m_nonClientDrag = true;
q->m_windowBeingDragged.reset();
q->m_windowBeingDragged = fw->makeWindow();
QWindow *window = fw->windowHandle();
if (needsUndocking) {
// Position the window before the drag start, otherwise if you move mouse too fast there will be an offset
// Only required when we've undocked/detached a window.
window->setPosition(QCursor::pos() - q->m_offset);
}
// Start the native move
window->startSystemMove();
}
# endif
#else
Q_UNUSED(needsUndocking);
#endif
qCDebug(state) << "StateDragging entered. m_draggable=" << q->m_draggable->asWidget()
@@ -284,6 +301,8 @@ void StateDragging::onEntry()
qWarning() << Q_FUNC_INFO << "No window being dragged for " << q->m_draggable->asWidget();
Q_EMIT q->dragCanceled();
}
Q_EMIT q->isDraggingChanged();
}
bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
@@ -369,6 +388,86 @@ bool StateDragging::handleMouseDoubleClick()
return false;
}
StateInternalMDIDragging::StateInternalMDIDragging(DragController *parent)
: StateBase(parent)
{
}
StateInternalMDIDragging::~StateInternalMDIDragging()
{
}
void StateInternalMDIDragging::onEntry()
{
qCDebug(state) << "StateInternalMDIDragging entered. draggable="
<< q->m_draggable->asWidget();
// Raise the dock widget being dragged
if (auto tb = qobject_cast<TitleBar *>(q->m_draggable->asWidget())) {
if (Frame *f = tb->frame())
f->raise();
}
Q_EMIT q->isDraggingChanged();
}
bool StateInternalMDIDragging::handleMouseButtonRelease(QPoint)
{
Q_EMIT q->dragCanceled();
return false;
}
bool StateInternalMDIDragging::handleMouseMove(QPoint globalPos)
{
// for MDI we only support dragging via the title bar, other cases don't make sense conceptually
auto tb = qobject_cast<TitleBar *>(q->m_draggable->asWidget());
if (!tb) {
qWarning() << Q_FUNC_INFO << "expected a title bar, not" << q->m_draggable->asWidget();
Q_EMIT q->dragCanceled();
return false;
}
Frame *frame = tb->frame();
if (!frame) {
// Doesn't happen.
qWarning() << Q_FUNC_INFO << "null frame.";
Q_EMIT q->dragCanceled();
return false;
}
const QSize parentSize = frame->QWidgetAdapter::parentWidget()->size();
const QPoint oldPos = frame->mapToGlobal(QPoint(0, 0));
const QPoint delta = globalPos - oldPos;
const QPoint newLocalPos = frame->pos() + delta - q->m_offset;
// Let's not allow the MDI window to go outside of its parent
QPoint newLocalPosBounded = {qMax(0, newLocalPos.x()), qMax(0, newLocalPos.y())};
newLocalPosBounded.setX(qMin(newLocalPosBounded.x(), parentSize.width() - frame->width()));
newLocalPosBounded.setY(qMin(newLocalPosBounded.y(), parentSize.height() - frame->height()));
auto layout = frame->mdiLayoutWidget();
Q_ASSERT(layout);
layout->moveDockWidget(frame, newLocalPosBounded);
// Check if we need to pop out the MDI window (make it float)
// If we drag the window against an edge, and move behind the edge some threshold, we float it
const int threshold = Config::self().mdiPopupThreshold();
if (threshold != -1) {
const QPoint overflow = newLocalPosBounded - newLocalPos;
if (qAbs(overflow.x()) > threshold || qAbs(overflow.y()) > threshold)
Q_EMIT q->mdiPopOut();
}
return false;
}
bool StateInternalMDIDragging::handleMouseDoubleClick()
{
Q_EMIT q->dragCanceled();
return false;
}
StateDraggingWayland::StateDraggingWayland(DragController *parent)
: StateDragging(parent)
{
@@ -471,13 +570,18 @@ DragController::DragController(QObject *parent)
auto statepreDrag = new StatePreDrag(this);
auto stateDragging = isWayland() ? new StateDraggingWayland(this)
: new StateDragging(this);
m_stateDraggingMDI = new StateInternalMDIDragging(this);
stateNone->addTransition(this, &DragController::mousePressed, statepreDrag);
statepreDrag->addTransition(this, &DragController::dragCanceled, stateNone);
statepreDrag->addTransition(this, &DragController::manhattanLengthMove, stateDragging);
statepreDrag->addTransition(this, &DragController::manhattanLengthMoveMDI, m_stateDraggingMDI);
stateDragging->addTransition(this, &DragController::dragCanceled, stateNone);
stateDragging->addTransition(this, &DragController::dropped, stateNone);
m_stateDraggingMDI->addTransition(this, &DragController::dragCanceled, stateNone);
m_stateDraggingMDI->addTransition(this, &DragController::mdiPopOut, stateDragging);
if (usesFallbackMouseGrabber())
enableFallbackMouseGrabber();
@@ -504,7 +608,7 @@ void DragController::unregisterDraggable(Draggable *drg)
bool DragController::isDragging() const
{
return m_windowBeingDragged != nullptr;
return m_windowBeingDragged != nullptr || activeState() == m_stateDraggingMDI;
}
bool DragController::isInNonClientDrag() const
@@ -605,7 +709,7 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
switch (e->type()) {
case QEvent::NonClientAreaMouseButtonPress: {
if (auto fw = qobject_cast<FloatingWindow*>(o)) {
if (fw->isInDragArea(Qt5Qt6Compat::eventGlobalPos(me))) {
if (KDDockWidgets::usesNativeTitleBar() || fw->isInDragArea(Qt5Qt6Compat::eventGlobalPos(me))) {
m_nonClientDrag = true;
return activeState()->handleMouseButtonPress(draggableForQObject(o), Qt5Qt6Compat::eventGlobalPos(me), me->pos());
}
@@ -627,6 +731,7 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
case QEvent::MouseMove:
return activeState()->handleMouseMove(Qt5Qt6Compat::eventGlobalPos(me));
case QEvent::MouseButtonDblClick:
case QEvent::NonClientAreaMouseButtonDblClick:
return activeState()->handleMouseDoubleClick();
default:
break;
@@ -645,6 +750,10 @@ static QWidgetOrQuick *qtTopLevelForHWND(HWND hwnd)
{
const QList<QWindow*> windows = qApp->topLevelWindows();
for (QWindow *window : windows) {
if (!window->isVisible()) {
continue;
}
if (hwnd == (HWND)window->winId()) {
return DockRegistry::self()->topLevelForHandle(window);
}

View File

@@ -12,7 +12,7 @@
#ifndef KD_DRAGCONTROLLER_P_H
#define KD_DRAGCONTROLLER_P_H
#include "docks_export.h"
#include "kddockwidgets/docks_export.h"
#include "TitleBar_p.h"
#include "WindowBeingDragged_p.h"
@@ -25,6 +25,7 @@
namespace KDDockWidgets {
class StateBase;
class StateInternalMDIDragging;
class DropArea;
class Draggable;
class FallbackMouseGrabber;
@@ -62,6 +63,7 @@ private:
class DOCKS_EXPORT DragController : public MinimalStateMachine
{
Q_OBJECT
Q_PROPERTY(bool isDragging READ isDragging NOTIFY isDraggingChanged)
public:
enum State {
State_None = 0,
@@ -94,8 +96,11 @@ public:
Q_SIGNALS:
void mousePressed();
void manhattanLengthMove();
void manhattanLengthMoveMDI();
void mdiPopOut();
void dragCanceled();
void dropped();
void isDraggingChanged();
protected:
bool eventFilter(QObject *, QEvent *) override;
@@ -105,6 +110,7 @@ private:
friend class StateNone;
friend class StatePreDrag;
friend class StateDragging;
friend class StateInternalMDIDragging;
friend class StateDropped;
friend class StateDraggingWayland;
@@ -118,11 +124,12 @@ private:
Draggable::List m_draggables;
Draggable *m_draggable = nullptr;
QPointer<QWidget> m_draggableGuard; // Just so we know if the draggable was destroyed for some reason
QPointer<WidgetType> m_draggableGuard; // Just so we know if the draggable was destroyed for some reason
std::unique_ptr<WindowBeingDragged> m_windowBeingDragged;
DropArea *m_currentDropArea = nullptr;
bool m_nonClientDrag = false;
FallbackMouseGrabber *m_fallbackMouseGrabber = nullptr;
StateInternalMDIDragging *m_stateDraggingMDI = nullptr;
};
class StateBase : public State
@@ -185,6 +192,21 @@ public:
bool handleMouseDoubleClick() override;
};
/// @brief State when we're moving an MDI dock widget around the main window
/// without it becoming floating
class StateInternalMDIDragging : public StateBase
{
Q_OBJECT
public:
explicit StateInternalMDIDragging(DragController *parent);
~StateInternalMDIDragging() override;
void onEntry() override;
bool handleMouseButtonRelease(QPoint globalPos) override;
bool handleMouseMove(QPoint globalPos) override;
bool handleMouseDoubleClick() override;
};
// Used on wayland only to use QDrag instead of setting geometry on mouse-move.
class StateDraggingWayland : public StateDragging
{

View File

@@ -74,8 +74,17 @@ public:
* Example: This draggable is a floating window with only 1 dock widget
* Example: This draggable is a title bar with two dock widgets -> nullptr
*/
virtual DockWidgetBase* singleDockWidget() const = 0;
virtual DockWidgetBase *singleDockWidget() const = 0;
///@brief Returns whether this draggable is a MDI window, being dragged internally within a main window
virtual bool isMDI() const = 0;
/**
* @brief Returns whether this draggable is already a window.
*
* If true, means the drag will simply move the existing window, and no undocking/untabbing is involved.
*/
virtual bool isWindow() const = 0;
private:
class Private;
Private *const d;

View File

@@ -10,16 +10,17 @@
*/
#include "DropArea_p.h"
#include "Logging_p.h"
#include "DockWidgetBase.h"
#include "Draggable_p.h"
#include "FloatingWindow_p.h"
#include "Config.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase.h"
#include "DockWidgetBase_p.h"
#include "Draggable_p.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "Logging_p.h"
#include "MainWindowBase.h"
#include "Utils_p.h"
// #include "indicators/AnimatedIndicators_p.h"
@@ -53,11 +54,6 @@ DropArea::~DropArea()
qCDebug(creation) << "~DropArea";
}
int DropArea::numFrames() const
{
return visibleCount();
}
Frame::List DropArea::frames() const
{
return findChildren<Frame *>(QString(), Qt::FindDirectChildrenOnly);
@@ -104,7 +100,7 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location,
return;
}
if ((option.visibility == InitialVisibilityOption::StartHidden) && dw->frame() != nullptr) {
if ((option.visibility == InitialVisibilityOption::StartHidden) && dw->d->frame() != nullptr) {
// StartHidden is just to be used at startup, not to moving stuff around
qWarning() << Q_FUNC_INFO << "Dock widget already exists in the layout";
return;
@@ -114,15 +110,15 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location,
return;
Frame *frame = nullptr;
Frame *relativeToFrame = relativeTo ? relativeTo->frame() : nullptr;
Frame *relativeToFrame = relativeTo ? relativeTo->d->frame() : nullptr;
dw->saveLastFloatingGeometry();
dw->d->saveLastFloatingGeometry();
const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
// Check if the dock widget already exists in the layout
if (containsDockWidget(dw)) {
Frame *oldFrame = dw->frame();
Frame *oldFrame = dw->d->frame();
if (oldFrame->hasSingleDockWidget()) {
Q_ASSERT(oldFrame->containsDockWidget(dw));
// The frame only has this dock widget, and the frame is already in the layout. So move the frame instead
@@ -151,7 +147,7 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location,
bool DropArea::containsDockWidget(DockWidgetBase *dw) const
{
return dw->frame() && MultiSplitter::containsFrame(dw->frame());
return dw->d->frame() && LayoutWidget::containsFrame(dw->d->frame());
}
bool DropArea::hasSingleFloatingFrame() const
@@ -173,9 +169,9 @@ QStringList DropArea::affinities() const
void DropArea::layoutParentContainerEqually(DockWidgetBase *dw)
{
Layouting::Item *item = itemForFrame(dw->frame());
Layouting::Item *item = itemForFrame(dw->d->frame());
if (!item) {
qWarning() << Q_FUNC_INFO << "Item not found for" << dw << dw->frame();
qWarning() << Q_FUNC_INFO << "Item not found for" << dw << dw->d->frame();
return;
}
@@ -261,8 +257,10 @@ bool DropArea::drop(WindowBeingDragged *draggedWindow, Frame *acceptingFrame,
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
const DockWidgetBase::List droppedDockWidgets = needToFocusNewlyDroppedWidgets
? droppedWindow->layoutWidget()->dockWidgets()
: DockWidgetBase::List(); // just so save some memory allocations for the case where this
// variable isn't used
switch (droploc) {
case DropIndicatorOverlayInterface::DropLocation_Left:
@@ -298,7 +296,7 @@ bool DropArea::drop(WindowBeingDragged *draggedWindow, Frame *acceptingFrame,
// Let's also focus the newly dropped dock widget
if (!droppedDockWidgets.isEmpty()) {
// If more than 1 was dropped, we only focus the first one
Frame *frame = droppedDockWidgets.first()->frame();
Frame *frame = droppedDockWidgets.first()->d->frame();
frame->FocusScope::focus(Qt::MouseFocusReason);
} else {
// Doesn't happen.

View File

@@ -48,7 +48,6 @@ public:
DropIndicatorOverlayInterface::DropLocation hover(WindowBeingDragged *draggedWindow, 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;
Layouting::Item *centralFrame() const;

View File

@@ -20,9 +20,10 @@
#include "DockRegistry_p.h"
#include "Config.h"
#include "FrameworkWidgetFactory.h"
#include "DragController_p.h"
#include "../LayoutSaver_p.h"
#include <QCloseEvent>
#include <QAbstractNativeEventFilter>
#include <QWindow>
#include <QScopedValueRollback>
@@ -33,56 +34,16 @@
using namespace KDDockWidgets;
#if defined(Q_OS_WIN)
# ifdef KDDOCKWIDGETS_QTWIDGETS
namespace KDDockWidgets {
/**
* @brief Helper to rediriect WM_NCHITTEST from child widgets to the top-level widget
*
* To implement aero-snap the top-level window must respond to WM_NCHITTEST, we do that
* in FloatingWindow::nativeEvent(). But if the child widgets have a native handle, then
* the WM_NCHITTEST will go to them. They have to respond HTTRANSPARENT so the event
* is redirected.
*
* This only affects QtWidgets, since QQuickItems never have native WId.
*/
class NCHITTESTEventFilter : public QAbstractNativeEventFilter
{
public:
explicit NCHITTESTEventFilter(FloatingWindow *fw) : m_floatingWindow(fw) {}
bool nativeEventFilter(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result) override
{
if (eventType != "windows_generic_MSG" || !m_floatingWindow)
return false;
auto msg = static_cast<MSG *>(message);
if (msg->message != WM_NCHITTEST)
return false;
const WId wid = WId(msg->hwnd);
QWidget *child = QWidget::find(wid);
if (!child || child->window() != m_floatingWindow)
return false;
const bool isThisWindow = child == m_floatingWindow;
if (!isThisWindow) {
*result = HTTRANSPARENT;
return true;
}
return false;
}
QPointer<FloatingWindow> m_floatingWindow;
};
}
# endif
#endif // Q_OS_WIN
/** static */
Qt::WindowFlags FloatingWindow::s_windowFlagsOverride = {};
static Qt::WindowFlags windowFlagsToUse()
{
if (FloatingWindow::s_windowFlagsOverride) {
// The user specifically set different flags.
return FloatingWindow::s_windowFlagsOverride;
}
if (KDDockWidgets::usesNativeDraggingAndResizing())
return Qt::Window;
@@ -141,11 +102,17 @@ FloatingWindow::FloatingWindow(MainWindowBase *parent)
{
if (kddwUsesQtWidgets()) {
// For QtQuick we do it a bit later, once we have the QQuickWindow
setupWindow();
#ifdef Q_OS_WIN
create();
#ifdef KDDOCKWIDGETS_QTWIDGETS
m_nchittestFilter = new NCHITTESTEventFilter(this);
qApp->installNativeEventFilter(m_nchittestFilter);
#endif
WidgetResizeHandler::setupWindow(windowHandle());
#endif
}
DockRegistry::self()->registerFloatingWindow(this);
qCDebug(creation) << "FloatingWindow()" << this;
if (Config::self().flags() & Config::Flag_KeepAboveIfNotUtilityWindow)
setWindowFlag(Qt::WindowStaysOnTopHint, true);
@@ -156,9 +123,12 @@ FloatingWindow::FloatingWindow(MainWindowBase *parent)
}
updateTitleBarVisibility();
connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged, this, &FloatingWindow::onFrameCountChanged);
connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged, this, &FloatingWindow::numFramesChanged);
connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged, this, &FloatingWindow::onVisibleFrameCountChanged);
connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
&FloatingWindow::onFrameCountChanged);
connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
&FloatingWindow::numFramesChanged);
connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
&FloatingWindow::onVisibleFrameCountChanged);
m_layoutDestroyedConnection = connect(m_dropArea, &QObject::destroyed, this, &FloatingWindow::scheduleDeleteLater);
}
@@ -180,45 +150,24 @@ FloatingWindow::~FloatingWindow()
delete m_nchittestFilter;
DockRegistry::self()->unregisterFloatingWindow(this);
qCDebug(creation) << "~FloatingWindow";
}
void FloatingWindow::setupWindow()
{
// Does some minor setup on our QWindow.
// Like adding the drop shadow on Windows and two other workarounds.
#if defined(Q_OS_WIN)
// 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()) {
# ifdef KDDOCKWIDGETS_QTWIDGETS
m_nchittestFilter = new NCHITTESTEventFilter(this);
qApp->installNativeEventFilter(m_nchittestFilter);
#endif
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 // Q_OS_WIN
}
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result)
{
if (!m_inDtor && !m_deleteScheduled && KDDockWidgets::usesAeroSnapWithCustomDecos()) {
if (m_inDtor || m_deleteScheduled)
return QWidget::nativeEvent(eventType, message, result);
if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
// To enable aero snap we need to tell Windows where's our custom title bar
if (WidgetResizeHandler::handleWindowsNativeEvent(this, eventType, message, result))
return true;
} else if (KDDockWidgets::usesNativeTitleBar()) {
auto msg = static_cast<MSG *>(message);
if (msg->message == WM_SIZING) {
// Cancel any drag if we're resizing
Q_EMIT DragController::instance()->dragCanceled();
}
}
return QWidget::nativeEvent(eventType, message, result);
@@ -229,7 +178,7 @@ void FloatingWindow::maybeCreateResizeHandler()
{
if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
setFlag(Qt::FramelessWindowHint, true);
setWidgetResizeHandler(new WidgetResizeHandler(false, this));
setWidgetResizeHandler(new WidgetResizeHandler(/*topLevel=*/ true, this));
}
}
@@ -261,7 +210,7 @@ const Frame::List FloatingWindow::frames() const
return m_dropArea->frames();
}
void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, bool preserveCenter)
void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, SuggestedGeometryHints hint)
{
const Frame::List frames = this->frames();
if (frames.size() == 1) {
@@ -276,7 +225,13 @@ void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, bool preserveCent
// Resize to new size but preserve center
const QPoint originalCenter = suggestedRect.center();
suggestedRect.setSize(size);
if (preserveCenter)
if ((hint & SuggestedGeometryHint_GeometryIsFromDocked) && (Config::self().flags() & Config::Flag_NativeTitleBar)) {
const QMargins margins = contentMargins();
suggestedRect.setHeight(suggestedRect.height() - m_titleBar->height() + margins.top() + margins.bottom());
}
if (hint & SuggestedGeometryHint_PreserveCenter)
suggestedRect.moveCenter(originalCenter);
}
@@ -295,14 +250,19 @@ MultiSplitter *FloatingWindow::multiSplitter() const
return m_dropArea;
}
LayoutWidget *FloatingWindow::layoutWidget() const
{
return m_dropArea;
}
bool FloatingWindow::isInDragArea(QPoint globalPoint) const
{
#ifdef Q_OS_WIN
// A click near the border will still send a Qt::NonClientMousePressEvent. We shouldn't
// interpret that as a drag, as it's for a native resize.
// Keep track of how we handled the WM_NCHITTEST
if (m_lastHitTest != 0 && m_lastHitTest != HTCAPTION)
return false;
if (usesAeroSnapWithCustomDecos())
return m_lastHitTest == HTCAPTION;
#endif
return dragRect().contains(globalPoint);
@@ -328,7 +288,7 @@ bool FloatingWindow::anyNonDockable() const
bool FloatingWindow::hasSingleFrame() const
{
return m_dropArea->numFrames() == 1;
return m_dropArea->visibleCount() == 1;
}
bool FloatingWindow::hasSingleDockWidget() const
@@ -341,6 +301,14 @@ bool FloatingWindow::hasSingleDockWidget() const
return frame->dockWidgetCount() == 1;
}
Frame *FloatingWindow::singleFrame() const
{
const Frame::List frames = this->frames();
return frames.isEmpty() ? nullptr
: frames.first();
}
bool FloatingWindow::beingDeleted() const
{
if (m_deleteScheduled || m_inDtor)
@@ -357,7 +325,6 @@ bool FloatingWindow::beingDeleted() const
void FloatingWindow::onFrameCountChanged(int count)
{
qCDebug(docking) << "FloatingWindow::onFrameCountChanged" << count;
if (count == 0) {
scheduleDeleteLater();
} else {
@@ -370,7 +337,6 @@ void FloatingWindow::onFrameCountChanged(int count)
void FloatingWindow::onVisibleFrameCountChanged(int count)
{
if (!m_disableSetVisible) {
qCDebug(hiding) << "FloatingWindow::onVisibleFrameCountChanged count=" << count;
setVisible(count > 0);
}
}
@@ -385,6 +351,9 @@ void FloatingWindow::updateTitleBarVisibility()
bool visible = true;
for (Frame *frame : frames())
frame->updateTitleBarVisibility();
if (KDDockWidgets::usesClientTitleBar()) {
const auto flags = Config::self().flags();
if ((flags & Config::Flag_HideTitleBarWhenTabsVisible) && !(flags & Config::Flag_AlwaysTitleBarWhenFloating)) {
@@ -393,8 +362,7 @@ void FloatingWindow::updateTitleBarVisibility()
}
}
for (Frame *frame : frames())
frame->updateTitleBarVisibility();
m_titleBar->updateButtons();
} else {
visible = false;
}
@@ -430,8 +398,6 @@ void FloatingWindow::updateTitleAndIcon()
void FloatingWindow::onCloseEvent(QCloseEvent *e)
{
qCDebug(closing) << "Frame::closeEvent";
if (e->spontaneous() && anyNonClosable()) {
// Event from the window system won't close us
e->ignore();
@@ -497,7 +463,75 @@ bool FloatingWindow::event(QEvent *ev)
if (ev->type() == QEvent::ActivationChange) {
// Since QWidget is missing a signal for window activation
Q_EMIT activatedChanged();
} else if (ev->type() == QEvent::StatusTip && parent()) {
// show status tips in the main window
return parent()->event(ev);
}
return QWidgetAdapter::event(ev);
}
bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::Option option) const
{
const Frame::List frames = this->frames();
return std::all_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->allDockWidgetsHave(option);
});
}
bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::Option option) const
{
const Frame::List frames = this->frames();
return std::any_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->anyDockWidgetsHas(option);
});
}
bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::LayoutSaverOption option) const
{
const Frame::List frames = this->frames();
return std::all_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->allDockWidgetsHave(option);
});
}
bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption option) const
{
const Frame::List frames = this->frames();
return std::any_of(frames.begin(), frames.end(), [option] (Frame *frame) {
return frame->anyDockWidgetsHas(option);
});
}
void FloatingWindow::addDockWidget(DockWidgetBase *dw, Location location,
DockWidgetBase *relativeTo, InitialOption option)
{
m_dropArea->addDockWidget(dw, location, relativeTo, option);
}
bool FloatingWindow::isMDI() const
{
return false;
}
bool FloatingWindow::isWindow() const
{
return true;
}
MainWindowBase *FloatingWindow::mainWindow() const
{
return qobject_cast<MainWindowBase*>(parent());
}
QMargins FloatingWindow::contentMargins() const
{
return { 4, 4, 4, 4 };
}
int FloatingWindow::userType() const
{
if (Frame *f = singleFrame())
return f->userType();
return 0;
}

View File

@@ -12,13 +12,13 @@
#ifndef KD_FLOATING_WINDOW_P_H
#define KD_FLOATING_WINDOW_P_H
#include "../docks_export.h"
#include "../QWidgetAdapter.h"
#include "../LayoutSaver_p.h"
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/LayoutSaver.h"
#include "kddockwidgets/Qt5Qt6Compat_p.h"
#include "Frame_p.h"
#include "Draggable_p.h"
#include "DropArea_p.h"
#include "kddockwidgets/Qt5Qt6Compat_p.h"
QT_BEGIN_NAMESPACE
class QAbstractNativeEventFilter;
@@ -31,6 +31,7 @@ class MainWindowBase;
class DropArea;
class Frame;
class MultiSplitter;
class LayoutWidget;
class DOCKS_EXPORT FloatingWindow
: public QWidgetAdapter
@@ -50,11 +51,14 @@ public:
// Draggable:
std::unique_ptr<WindowBeingDragged> makeWindow() override;
DockWidgetBase *singleDockWidget() const override;
bool isWindow() const override;
const QVector<DockWidgetBase*> dockWidgets() const;
const Frame::List frames() const;
DropArea *dropArea() const { return m_dropArea; }
int userType() const;
#ifdef Q_OS_WIN
void setLastHitTest(int hitTest) {
m_lastHitTest = hitTest;
@@ -75,7 +79,7 @@ public:
* @param preserveCenter, if true, then the center is preserved
*
*/
void setSuggestedGeometry(QRect suggestedRect, bool preserveCenter = false);
void setSuggestedGeometry(QRect suggestedRect, SuggestedGeometryHints = SuggestedGeometryHint_None);
bool anyNonClosable() const;
bool anyNonDockable() const;
@@ -97,6 +101,9 @@ public:
*/
bool hasSingleDockWidget() const;
/// @brief If this floating window has only one Frame, it's returned, otherwise nullptr
Frame* singleFrame() const;
/**
* @brief Returns whether a deleteLater has already been issued
*/
@@ -112,12 +119,19 @@ public:
*/
MultiSplitter *multiSplitter() const;
/**
* @brief Returns the LayoutWidget
*/
LayoutWidget *layoutWidget() const;
/**
* @brief Returns whether @p globalPoint is inside the title bar (or, when there's no title-bar, the draggable empty
* area of a tab bar)
*/
bool isInDragArea(QPoint globalPoint) const;
bool isMDI() const override;
///@brief updates the title and the icon
void updateTitleAndIcon();
void updateTitleBarVisibility();
@@ -131,12 +145,40 @@ public:
*/
QRect dragRect() const;
///@brief Returns whether all dock widgets have the specified option set
bool allDockWidgetsHave(DockWidgetBase::Option) const;
///@brief Returns whether at least one dock widget has the specified option set
bool anyDockWidgetsHas(DockWidgetBase::Option) const;
///@brief Returns whether all dock widgets have the specified layout saver option set
bool allDockWidgetsHave(DockWidgetBase::LayoutSaverOption) const;
///@brief Returns whether at least one dock widget has the specified layout saver option set
bool anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption) const;
/// @brief Adds the dock widget to the specified location
void addDockWidget(DockWidgetBase *, KDDockWidgets::Location location,
DockWidgetBase *relativeTo, InitialOption = {});
/// @brief Returns the MainWindow which is the transient parent of this FloatingWindow
/// Can be nullptr if you create dock widgets before the main window. Can also be some
/// arbitrary value if you have more than one main window.
MainWindowBase *mainWindow() const;
///@brief Returns the contents margins
QMargins contentMargins() const;
///@brief Allows the user app to specify which window flags to use, instead of KDDWs default ones
///Bugs caused by this won't be supported, as the amount of combinations that could go wrong can
///be open ended
static Qt::WindowFlags s_windowFlagsOverride;
Q_SIGNALS:
void activatedChanged();
void numFramesChanged();
void windowStateChanged(QWindowStateChangeEvent *);
protected:
void setupWindow();
void maybeCreateResizeHandler();
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)

View File

@@ -16,17 +16,21 @@
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#include "Frame_p.h"
#include "DropArea_p.h"
#include "Logging_p.h"
#include "FloatingWindow_p.h"
#include "Utils_p.h"
#include "Position_p.h"
#include "DockRegistry_p.h"
#include "Config.h"
#include "TitleBar_p.h"
#include "TabWidget_p.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "LayoutSaver_p.h"
#include "LayoutWidget_p.h"
#include "Logging_p.h"
#include "Position_p.h"
#include "TabWidget_p.h"
#include "TitleBar_p.h"
#include "Utils_p.h"
#include "WidgetResizeHandler_p.h"
#include "MDILayoutWidget_p.h"
#include <QCloseEvent>
#include <QTimer>
@@ -47,23 +51,23 @@ static FrameOptions actualOptions(FrameOptions options)
}
}
Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
Frame::Frame(QWidgetOrQuick *parent, FrameOptions options, int userType)
: LayoutGuestWidget(parent)
, FocusScope(this)
, m_tabWidget(Config::self().frameworkWidgetFactory()->createTabWidget(this))
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
, m_options(actualOptions(options))
, m_userType(userType)
{
s_dbg_numFrames++;
DockRegistry::self()->registerFrame(this);
qCDebug(creation) << "Frame" << ((void*)this) << s_dbg_numFrames;
connect(this, &Frame::currentDockWidgetChanged, this, &Frame::updateTitleAndIcon);
connect(m_tabWidget->asWidget(), SIGNAL(currentTabChanged(int)), // clazy:exclude=old-style-connect
this, SLOT(onCurrentTabChanged(int)));
setDropArea(qobject_cast<DropArea *>(QWidgetAdapter::parentWidget()));
setLayoutWidget(qobject_cast<LayoutWidget *>(QWidgetAdapter::parentWidget()));
m_inCtor = false;
}
@@ -74,11 +78,13 @@ Frame::~Frame()
if (m_layoutItem)
m_layoutItem->unref();
qCDebug(creation) << "~Frame" << static_cast<void*>(this);
delete m_resizeHandler;
m_resizeHandler = nullptr;
DockRegistry::self()->unregisterFrame(this);
// Run some disconnects() too, so we don't receive signals during destruction:
setDropArea(nullptr);
setLayoutWidget(nullptr);
}
void Frame::updateTitleAndIcon()
@@ -136,17 +142,13 @@ void Frame::addWidget(FloatingWindow *floatingWindow, InitialOption addingOption
void Frame::insertWidget(DockWidgetBase *dockWidget, int index, InitialOption addingOption)
{
qCDebug(addwidget()) << Q_FUNC_INFO << ((void*)this) << "; dockWidget="
<< dockWidget << "; oldFrame=" << dockWidget->frame()
<< "; initialOption=" << addingOption;
Q_ASSERT(dockWidget);
if (containsDockWidget(dockWidget)) {
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
return;
}
if (m_layoutItem)
dockWidget->addPlaceholderItem(m_layoutItem);
dockWidget->d->addPlaceholderItem(m_layoutItem);
insertDockWidget(dockWidget, index);
@@ -181,6 +183,8 @@ FloatingWindow* Frame::detachTab(DockWidgetBase *dockWidget)
{
if (m_inCtor || m_inDtor) return nullptr;
dockWidget->d->saveTabIndex();
QRect r = dockWidget->geometry();
removeWidget(dockWidget);
@@ -192,13 +196,13 @@ FloatingWindow* Frame::detachTab(DockWidgetBase *dockWidget)
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(newFrame);
r.moveTopLeft(globalPoint);
floatingWindow->setSuggestedGeometry(r);
floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
floatingWindow->show();
return floatingWindow;
}
int Frame::indexOfDockWidget(DockWidgetBase *dw)
int Frame::indexOfDockWidget(const DockWidgetBase *dw)
{
if (m_inCtor || m_inDtor) return -1;
@@ -268,7 +272,7 @@ void Frame::onDockWidgetCountChanged()
const DockWidgetBase::List docks = dockWidgets();
for (DockWidgetBase *dock : docks)
dock->updateFloatAction();
dock->d->updateFloatAction();
}
Q_EMIT numDockWidgetsChanged();
@@ -316,7 +320,15 @@ void Frame::updateTitleBarVisibility()
visible = true;
}
const bool wasVisible = m_titleBar->isVisible();
m_titleBar->setVisible(visible);
if (wasVisible != visible) {
Q_EMIT actualTitleBarChanged();
for (auto dw : dockWidgets())
Q_EMIT dw->actualTitleBarChanged();
}
if (auto fw = floatingWindow()) {
// Update the floating window which might be using Flag_HideTitleBarWhenTabsVisible
// In that case it might not show title bar depending on the number of tabs that the frame has
@@ -328,7 +340,7 @@ void Frame::updateFloatingActions()
{
const QVector<DockWidgetBase *> widgets = dockWidgets();
for (DockWidgetBase *dw : widgets)
dw->updateFloatAction();
dw->d->updateFloatAction();
}
bool Frame::containsMouse(QPoint globalPos) const
@@ -506,10 +518,10 @@ void Frame::setLayoutItem(Layouting::Item *item)
m_layoutItem = item;
if (item) {
for (DockWidgetBase *dw : dockWidgets())
dw->addPlaceholderItem(item);
dw->d->addPlaceholderItem(item);
} else {
for (DockWidgetBase *dw : dockWidgets())
dw->lastPositions().removePlaceholders();
dw->d->lastPositions().removePlaceholders();
}
}
@@ -545,35 +557,40 @@ QStringList Frame::affinities() const
}
}
void Frame::setDropArea(DropArea *dt)
void Frame::setLayoutWidget(LayoutWidget *dt)
{
if (dt == m_dropArea)
if (dt == m_layoutWidget)
return;
qCDebug(docking) << "Frame::setDropArea dt=" << dt;
const bool wasInMainWindow = dt && isInMainWindow();
if (m_dropArea)
const bool wasMDI = isMDI();
if (m_layoutWidget)
disconnect(m_visibleWidgetCountChangedConnection);
m_dropArea = dt;
m_layoutWidget = dt;
delete m_resizeHandler;
m_resizeHandler = nullptr;
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);
if (m_layoutWidget) {
if (isMDI())
m_resizeHandler = new WidgetResizeHandler(/*topLevel=*/ false, this);
// We keep the connect result so we don't dereference m_layoutWidget at shutdown
m_visibleWidgetCountChangedConnection =
connect(m_layoutWidget, &LayoutWidget::visibleWidgetCountChanged, this,
&Frame::updateTitleBarVisibility);
updateTitleBarVisibility();
if (wasInMainWindow != isInMainWindow())
Q_EMIT isInMainWindowChanged();
}
if (wasMDI != isMDI())
Q_EMIT isMDIChanged();
}
bool Frame::isTheOnlyFrame() const
{
qCDebug(docking) << "Frame::isTheOnlyFrame() m_dropArea=" << m_dropArea << "; numFrames"
<< (m_dropArea ? m_dropArea->numFrames() : 0);
return m_dropArea && m_dropArea->numFrames() == 1;
return m_layoutWidget && m_layoutWidget->visibleCount() == 1;
}
bool Frame::isOverlayed() const
@@ -581,6 +598,11 @@ bool Frame::isOverlayed() const
return m_options & FrameOption_IsOverlayed;
}
void Frame::unoverlay()
{
m_options &= ~FrameOption_IsOverlayed;
}
bool Frame::isFloating() const
{
if (isInMainWindow())
@@ -602,10 +624,10 @@ bool Frame::isInMainWindow() const
bool Frame::event(QEvent *e)
{
if (e->type() == QEvent::ParentChange) {
if (auto dropArea = qobject_cast<DropArea *>(QWidgetAdapter::parentWidget())) {
setDropArea(dropArea);
if (auto layoutWidget = qobject_cast<LayoutWidget *>(QWidgetAdapter::parentWidget())) {
setLayoutWidget(layoutWidget);
} else {
setDropArea(nullptr);
setLayoutWidget(nullptr);
}
}
@@ -646,7 +668,7 @@ LayoutSaver::Frame Frame::serialize() const
frame.id = id(); // for coorelation purposes
for (DockWidgetBase *dock : docks)
frame.dockWidgets.push_back(dock->serialize());
frame.dockWidgets.push_back(dock->d->serialize());
return frame;
}
@@ -705,18 +727,78 @@ QRect Frame::dragRect() const
return rect;
}
DropArea *Frame::dropArea() const
{
return m_dropArea;
}
MainWindowBase *Frame::mainWindow() const
{
return m_dropArea ? m_dropArea->mainWindow()
: nullptr;
return m_layoutWidget ? m_layoutWidget->mainWindow() : nullptr;
}
TabWidget *Frame::tabWidget() const
{
return m_tabWidget;
}
///@brief Returns whether all dock widgets have the specified option set
bool Frame::allDockWidgetsHave(DockWidgetBase::Option option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::all_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->options() & option;
});
}
///@brief Returns whether at least one dock widget has the specified option set
bool Frame::anyDockWidgetsHas(DockWidgetBase::Option option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::any_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->options() & option;
});
}
bool Frame::allDockWidgetsHave(DockWidgetBase::LayoutSaverOption option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::all_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->layoutSaverOptions() & option;
});
}
bool Frame::anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption option) const
{
const DockWidgetBase::List docks = dockWidgets();
return std::any_of(docks.cbegin(), docks.cend(), [option] (DockWidgetBase *dw) {
return dw->layoutSaverOptions() & option;
});
}
void Frame::setAllowedResizeSides(CursorPositions sides)
{
if (sides) {
delete m_resizeHandler;
m_resizeHandler = new WidgetResizeHandler(/*topLevel=*/ false, this);
m_resizeHandler->setAllowedResizeSides(sides);
} else {
delete m_resizeHandler;
m_resizeHandler = nullptr;
}
}
bool Frame::isMDI() const
{
return mdiLayoutWidget() != nullptr;
}
MDILayoutWidget *Frame::mdiLayoutWidget() const
{
return qobject_cast<MDILayoutWidget *>(m_layoutWidget);
}
int Frame::userType() const
{
return m_userType;
}
WidgetResizeHandler *Frame::resizeHandler() const
{
return m_resizeHandler;
}

View File

@@ -22,7 +22,8 @@
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/FocusScope.h"
#include "../LayoutSaver_p.h"
#include "kddockwidgets/DockWidgetBase.h"
#include "kddockwidgets/LayoutSaver.h"
#include "multisplitter/Widget.h"
#include <QVector>
@@ -35,10 +36,11 @@ namespace KDDockWidgets {
class TitleBar;
class TabWidget;
class DropArea;
class DockWidgetBase;
class FloatingWindow;
class MainWindowBase;
class MDILayoutWidget;
class WidgetResizeHandler;
/**
* @brief A DockWidget wrapper that adds a QTabWidget and a TitleBar
@@ -46,9 +48,9 @@ class MainWindowBase;
* Frame is the actual widget that goes into the MultiSplitter. It provides a TitleBar which you
* can use to detach, and also a QTabWidget so you can tab dock widgets together.
*
* This class doesn't actually add window frames and it's never a top-level widget. A Frame is always
* inside a MultiSplitter (DropArea). Be it a MultiSplitter belonging to a MainWindow or belonging
* to a FloatingWindow.
* This class doesn't actually add window frames and it's never a top-level widget. A Frame is
* always inside a LayoutWidget. Be it a MultiSplitter belonging to a MainWindow or belonging to a
* FloatingWindow.
*/
class DOCKS_EXPORT Frame
: public LayoutGuestWidget
@@ -56,11 +58,15 @@ class DOCKS_EXPORT Frame
{
Q_OBJECT
Q_PROPERTY(KDDockWidgets::TitleBar* titleBar READ titleBar CONSTANT)
Q_PROPERTY(KDDockWidgets::TitleBar* actualTitleBar READ actualTitleBar NOTIFY actualTitleBarChanged)
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentDockWidgetChanged)
Q_PROPERTY(int userType READ userType CONSTANT)
Q_PROPERTY(bool isMDI READ isMDI NOTIFY isMDIChanged)
public:
typedef QList<Frame *> List;
explicit Frame(QWidgetOrQuick *parent = nullptr, FrameOptions = FrameOption_None);
explicit Frame(QWidgetOrQuick *parent = nullptr, FrameOptions = FrameOption_None,
int userType = 0);
~Frame() override;
static Frame *deserialize(const LayoutSaver::Frame &);
@@ -83,7 +89,7 @@ public:
FloatingWindow *detachTab(DockWidgetBase *);
///@brief returns the index of the specified dock widget
int indexOfDockWidget(DockWidgetBase *);
int indexOfDockWidget(const DockWidgetBase *);
///@brief returns the index of the current tab
int currentIndex() const;
@@ -120,16 +126,15 @@ public:
QIcon icon() const;
const QVector<DockWidgetBase *> dockWidgets() const;
void setDropArea(DropArea *);
///@brief Returns the drop area this Frame is in.
DropArea *dropArea() const;
bool isTheOnlyFrame() const;
///@brief Returns whether this frame is overlayed on top of the MainWindow (auto-hide feature);
bool isOverlayed() const;
///@brief clears the FrameOption_IsOverlayed flag.
/// For example, if you drag a side-bar overlay, then it becomes a normal floating window
void unoverlay();
/**
* @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,35 @@ public:
*/
virtual QRect dragRect() const;
///@brief Returns whether all dock widgets have the specified option set
bool allDockWidgetsHave(DockWidgetBase::Option) const;
///@brief Returns whether at least one dock widget has the specified option set
bool anyDockWidgetsHas(DockWidgetBase::Option) const;
///@brief Returns whether all dock widgets have the specified layout saver option set
bool allDockWidgetsHave(DockWidgetBase::LayoutSaverOption) const;
///@brief Returns whether at least one dock widget has the specified layout saver option set
bool anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption) const;
/// @brief Usually we do resize via the native window manager, but if a widget is docked like
/// in MDI mode, or in overlayed mode then we allow the user to resize with mouse
void setAllowedResizeSides(CursorPositions sides);
/// @brief Returns whether this frame is in a MDI layout
/// Usually no, unless you're using an MDI main window
bool isMDI() const;
/// @brief Returns the MDI layout. Or nullptr if this frame isn't in a MDI layout
MDILayoutWidget *mdiLayoutWidget() const;
/// @brief See DockWidgetBase::userType()
int userType() const;
/// @brief Returns the resize handler. Used mostly in MDI mode.
WidgetResizeHandler *resizeHandler() const;
Q_SIGNALS:
void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *);
void numDockWidgetsChanged();
@@ -247,6 +281,8 @@ Q_SIGNALS:
void isInMainWindowChanged();
void isFocusedChanged();
void focusedWidgetChanged();
void isMDIChanged();
void actualTitleBarChanged();
protected:
void isFocusedChangedCallback() final;
@@ -280,7 +316,7 @@ protected:
QSize biggestDockWidgetMaxSize() const;
virtual void removeWidget_impl(DockWidgetBase *) = 0;
virtual int indexOfDockWidget_impl(DockWidgetBase *) = 0;
virtual int indexOfDockWidget_impl(const DockWidgetBase *) = 0;
virtual int currentIndex_impl() const = 0;
virtual void setCurrentTabIndex_impl(int index) = 0;
virtual void setCurrentDockWidget_impl(DockWidgetBase *) = 0;
@@ -288,6 +324,7 @@ protected:
virtual DockWidgetBase *dockWidgetAt_impl(int index) const = 0;
virtual DockWidgetBase *currentDockWidget_impl() const = 0;
virtual int nonContentsHeight() const = 0;
private:
bool m_inCtor = true; // Needs to be initialized early, as pointed out by UBSAN
protected:
@@ -304,11 +341,16 @@ private:
void scheduleDeleteLater();
bool event(QEvent *) override;
DropArea *m_dropArea = nullptr;
const FrameOptions m_options;
/// @brief Sets the LayoutWidget which this frame is in
void setLayoutWidget(LayoutWidget *);
LayoutWidget *m_layoutWidget = nullptr;
WidgetResizeHandler *m_resizeHandler = nullptr;
FrameOptions m_options = FrameOption_None;
QPointer<Layouting::Item> m_layoutItem;
bool m_updatingTitleBar = false;
bool m_beingDeleted = false;
int m_userType = 0;
QMetaObject::Connection m_visibleWidgetCountChangedConnection;
};

View File

@@ -0,0 +1,287 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company
<info@kdab.com> Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../LayoutSaver_p.h"
#include "Config.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "Position_p.h"
using namespace KDDockWidgets;
LayoutWidget::LayoutWidget(QWidgetOrQuick *parent)
: LayoutGuestWidget(parent)
{
}
LayoutWidget::~LayoutWidget()
{
if (m_rootItem->hostWidget()->asQObject() == this)
delete m_rootItem;
DockRegistry::self()->unregisterLayout(this);
}
bool LayoutWidget::isInMainWindow() const
{
return mainWindow() != nullptr;
}
MainWindowBase *LayoutWidget::mainWindow() const
{
if (auto pw = QWidgetAdapter::parentWidget()) {
// 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;
}
FloatingWindow *LayoutWidget::floatingWindow() const
{
return qobject_cast<FloatingWindow *>(QWidgetAdapter::parentWidget());
}
void LayoutWidget::setRootItem(Layouting::ItemContainer *root)
{
delete m_rootItem;
m_rootItem = root;
connect(m_rootItem, &Layouting::ItemContainer::numVisibleItemsChanged, this,
&MultiSplitter::visibleWidgetCountChanged);
connect(m_rootItem, &Layouting::ItemContainer::minSizeChanged, this,
[this] { setMinimumSize(layoutMinimumSize()); });
}
QSize LayoutWidget::layoutMinimumSize() const
{
return m_rootItem->minSize();
}
QSize LayoutWidget::layoutMaximumSizeHint() const
{
return m_rootItem->maxSizeHint();
}
void LayoutWidget::setLayoutMinimumSize(QSize sz)
{
if (sz != m_rootItem->minSize()) {
setLayoutSize(size().expandedTo(m_rootItem->minSize())); // Increase size in case we need to
m_rootItem->setMinSize(sz);
}
}
QSize LayoutWidget::size() const
{
return m_rootItem->size();
}
void LayoutWidget::clearLayout()
{
m_rootItem->clear();
}
bool LayoutWidget::checkSanity() const
{
return m_rootItem->checkSanity();
}
void LayoutWidget::dumpLayout() const
{
m_rootItem->dumpLayout();
}
void LayoutWidget::restorePlaceholder(DockWidgetBase *dw, Layouting::Item *item, int tabIndex)
{
if (item->isPlaceholder()) {
Frame *newFrame = Config::self().frameworkWidgetFactory()->createFrame(this);
item->restore(newFrame);
}
auto frame = qobject_cast<Frame *>(item->guestAsQObject());
Q_ASSERT(frame);
if (tabIndex != -1 && frame->dockWidgetCount() >= tabIndex) {
frame->insertWidget(dw, tabIndex);
} else {
frame->addWidget(dw);
}
frame->QWidgetAdapter::setVisible(true);
}
void LayoutWidget::unrefOldPlaceholders(const Frame::List &framesBeingAdded) const
{
for (Frame *frame : framesBeingAdded) {
for (DockWidgetBase *dw : frame->dockWidgets()) {
dw->d->lastPositions().removePlaceholders(this);
}
}
}
void LayoutWidget::setLayoutSize(QSize size)
{
if (size != this->size()) {
m_rootItem->setSize_recursive(size);
if (!m_inResizeEvent && !LayoutSaver::restoreInProgress())
resize(size);
}
}
const Layouting::Item::List LayoutWidget::items() const
{
return m_rootItem->items_recursive();
}
bool LayoutWidget::containsItem(const Layouting::Item *item) const
{
return m_rootItem->contains_recursive(item);
}
bool LayoutWidget::containsFrame(const Frame *frame) const
{
return itemForFrame(frame) != nullptr;
}
int LayoutWidget::count() const
{
return m_rootItem->count_recursive();
}
int LayoutWidget::visibleCount() const
{
return m_rootItem->visibleCount_recursive();
}
int LayoutWidget::placeholderCount() const
{
return count() - visibleCount();
}
Layouting::Item *LayoutWidget::itemForFrame(const Frame *frame) const
{
if (!frame)
return nullptr;
return m_rootItem->itemForWidget(frame);
}
DockWidgetBase::List LayoutWidget::dockWidgets() const
{
DockWidgetBase::List dockWidgets;
const Frame::List frames = this->frames();
for (Frame *frame : frames)
dockWidgets << frame->dockWidgets();
return dockWidgets;
}
Frame::List LayoutWidget::framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const
{
if (auto frame = qobject_cast<Frame *>(frameOrMultiSplitter))
return { frame };
if (auto msw = qobject_cast<MultiSplitter *>(frameOrMultiSplitter))
return msw->frames();
return {};
}
Frame::List LayoutWidget::frames() const
{
const Layouting::Item::List items = m_rootItem->items_recursive();
Frame::List result;
result.reserve(items.size());
for (Layouting::Item *item : items) {
if (auto f = static_cast<Frame *>(item->guestAsQObject()))
result.push_back(f);
}
return result;
}
void LayoutWidget::removeItem(Layouting::Item *item)
{
if (!item) {
qWarning() << Q_FUNC_INFO << "nullptr item";
return;
}
item->parentContainer()->removeItem(item);
}
void LayoutWidget::updateSizeConstraints()
{
const QSize newMinSize = m_rootItem->minSize();
qCDebug(sizing) << Q_FUNC_INFO << "Updating size constraints from" << minimumSize() << "to"
<< newMinSize;
setLayoutMinimumSize(newMinSize);
}
bool LayoutWidget::deserialize(const LayoutSaver::MultiSplitter &l)
{
QHash<QString, Layouting::Widget *> frames;
for (const LayoutSaver::Frame &frame : qAsConst(l.frames)) {
Frame *f = Frame::deserialize(frame);
Q_ASSERT(!frame.id.isEmpty());
frames.insert(frame.id, f);
}
m_rootItem->fillFromVariantMap(l.layout, frames);
updateSizeConstraints();
m_rootItem->setSize_recursive(QWidgetAdapter::size());
return true;
}
void LayoutWidget::onLayoutRequest()
{
updateSizeConstraints();
}
bool LayoutWidget::onResize(QSize newSize)
{
QScopedValueRollback<bool> resizeGuard(m_inResizeEvent, true); // to avoid re-entrancy
if (!LayoutSaver::restoreInProgress()) {
// don't resize anything while we're restoring the layout
setLayoutSize(newSize);
}
return false; // So QWidget::resizeEvent is called
}
LayoutSaver::MultiSplitter LayoutWidget::serialize() const
{
LayoutSaver::MultiSplitter l;
l.layout = m_rootItem->toVariantMap();
const Layouting::Item::List items = m_rootItem->items_recursive();
l.frames.reserve(items.size());
for (Layouting::Item *item : items) {
if (!item->isContainer()) {
if (auto frame = qobject_cast<Frame *>(item->guestAsQObject()))
l.frames.insert(frame->id(), frame->serialize());
}
}
return l;
}

View File

@@ -0,0 +1,225 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company
<info@kdab.com> Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
/**
* @file
* @brief A widget that supports an arbitrary number of splitters (called Separators) in any
* combination of vertical/horizontal.
*
* This is a widget wrapper around the multisplitter layout (Layouting::Item)
*
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#ifndef KDDOCKWIDGETS_LAYOUTWIDGET_P_H
#define KDDOCKWIDGETS_LAYOUTWIDGET_P_H
#pragma once
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/KDDockWidgets.h"
#include "kddockwidgets/LayoutSaver.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include <QList>
namespace Layouting {
class Item;
class Separator;
class Widget_qwidget;
}
namespace KDDockWidgets {
class MainWindowBase;
class FloatingWindow;
class Frame;
class DockWidgetBase;
/**
* @brief The widget (QWidget or QQuickItem) which holds a layout of dock widgets.
*
* Usually this would simply be MultiSplitter, but we've introduced this base class to support
* different layouts, like MDI layouts, which are very different than traditional dock widget
* layouts.
*
* This class makes the bridge between the GUI world (QWidget) and Layouting::Item world.
* It's suitable to be set as a main window central widget for instance. The actual layouting is
* then done by the root Item.
*/
class DOCKS_EXPORT LayoutWidget : public LayoutGuestWidget
{
Q_OBJECT
public:
explicit LayoutWidget(QWidgetOrQuick *parent = nullptr);
~LayoutWidget() override;
bool isInMainWindow() const;
MainWindowBase *mainWindow() const;
FloatingWindow *floatingWindow() const;
/**
* @brief returns the layout's minimum size
* @ref setLayoutMinimumSize
*/
QSize layoutMinimumSize() const;
/**
* @brief returns the layout's maximum size hint
*/
QSize layoutMaximumSizeHint() const;
/**
* @brief returns the contents width.
* Usually it's the same width as the respective parent MultiSplitter.
*/
int width() const
{
return size().width();
}
/**
* @brief returns the contents height.
* Usually it's the same height as the respective parent MultiSplitter.
*/
int height() const
{
return size().height();
}
/**
* @brief getter for the size
*/
QSize size() const;
/// @brief Runs some sanity checks. Returns true if everything is OK
bool checkSanity() const;
/// @brief clears the layout
void clearLayout();
/// @brief dumps the layout to stderr
void dumpLayout() const;
/**
* @brief setter for the contents size
* The "contents size" is just the size() of this layout. However, since resizing
* QWidgets is async and we need it to be sync. As sometimes adding widgets will increase
* the MultiSplitter size (due to widget's min-size constraints).
*/
void setLayoutSize(QSize);
/// @brief restores the dockwidget @p dw to its previous position
void restorePlaceholder(DockWidgetBase *dw, Layouting::Item *, int tabIndex);
/**
* @brief The list of items in this layout.
*/
const QVector<Layouting::Item *> items() const;
/**
* @brief Returns true if this layout contains the specified item.
*/
bool containsItem(const Layouting::Item *) const;
/**
* @brief Returns true if this layout contains the specified frame.
*/
bool containsFrame(const Frame *) const;
/**
* @brief Returns the number of Item objects in this layout.
* This includes non-visible (placeholder) Items too.
* @sa visibleCount
*/
int count() const;
/**
* @brief Returns the number of visible Items in this layout.
* Which is @ref count minus @ref placeholderCount
* @sa count
*/
int visibleCount() const;
/**
* @brief Returns the number of placeholder items in this layout.
* This is the same as @ref count minus @ref visibleCount
* @sa count, visibleCount
*/
int placeholderCount() const;
/**
* @brief returns the Item that holds @p frame in this layout
*/
Layouting::Item *itemForFrame(const Frame *frame) const;
/**
* @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 Removes an item from this MultiSplitter.
*/
void removeItem(Layouting::Item *item);
/**
* @brief Updates the min size of this layout.
*/
void updateSizeConstraints();
virtual bool deserialize(const LayoutSaver::MultiSplitter &);
LayoutSaver::MultiSplitter serialize() const;
protected:
void setRootItem(Layouting::ItemContainer *root);
/**
* @brief setter for the minimum size
* @ref minimumSize
*/
void setLayoutMinimumSize(QSize);
void onLayoutRequest() override;
bool onResize(QSize newSize) override;
/**
* @brief Removes unneeded placeholder items when adding new frames.
*
* A floating frame A might have a placeholder in the main window (for example to remember its
* position on the Left), but then the user might attach it to the right, so the left
* placeholder is no longer need. Right before adding the frame to the right we remove the left
* placeholder, otherwise it's unrefed while we're adding causing a segfault. So what this does
* is making the unrefing happen a bit earlier.
*/
void unrefOldPlaceholders(const QList<Frame *> &framesBeingAdded) const;
/**
* @brief returns the frames contained in @p frameOrMultiSplitter
* If frameOrMultiSplitter is a Frame, it returns a list of 1 element, with that frame
* If frameOrMultiSplitter is a MultiSplitter then it returns a list of all frames it contains
*/
QList<Frame *> framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const;
Q_SIGNALS:
void visibleWidgetCountChanged(int count);
private:
bool m_inResizeEvent = false;
Layouting::ItemContainer *m_rootItem = nullptr;
};
}
#endif

View File

@@ -0,0 +1,115 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "ItemFreeContainer_p.h"
#include "MDILayoutWidget_p.h"
#include "DockWidgetBase_p.h"
#include "Config.h"
#include "FrameworkWidgetFactory.h"
using namespace KDDockWidgets;
MDILayoutWidget::MDILayoutWidget(QWidgetOrQuick *parent)
: LayoutWidget(parent)
, m_rootItem(new Layouting::ItemFreeContainer(this))
{
setRootItem(m_rootItem);
}
MDILayoutWidget::~MDILayoutWidget()
{
}
void MDILayoutWidget::addDockWidget(DockWidgetBase *dw, QPoint localPt, InitialOption addingOption)
{
if (!dw) {
qWarning() << Q_FUNC_INFO << "Refusing to add null dock widget";
return;
}
auto frame = qobject_cast<Frame*>(dw->d->frame());
if (itemForFrame(frame) != nullptr) {
// Item already exists, remove it. See also comment in MultiSplitter::addWidget().
frame->QWidgetAdapter::setParent(nullptr);
frame->setLayoutItem(nullptr);
}
Layouting::Item *newItem = new Layouting::Item(this);
if (frame) {
newItem->setGuestWidget(frame);
} else {
frame = Config::self().frameworkWidgetFactory()->createFrame(nullptr, FrameOption_None);
frame->addWidget(dw, addingOption);
newItem->setGuestWidget(frame);
}
Q_ASSERT(!newItem->geometry().isEmpty());
m_rootItem->addDockWidget(newItem, localPt);
if (addingOption.startsHidden()) {
delete frame;
}
}
void MDILayoutWidget::setDockWidgetGeometry(Frame *frame, QRect geometry)
{
if (!frame)
return;
Layouting::Item *item = itemForFrame(frame);
if (!item) {
qWarning() << Q_FUNC_INFO << "Frame not found in the layout" << frame;
return;
}
item->setGeometry(geometry);
}
void MDILayoutWidget::moveDockWidget(DockWidgetBase *dw, QPoint pos)
{
moveDockWidget(dw->d->frame(), pos);
}
void MDILayoutWidget::moveDockWidget(Frame *frame, QPoint pos)
{
if (!frame)
return;
Layouting::Item *item = itemForFrame(frame);
if (!item) {
qWarning() << Q_FUNC_INFO << "Frame not found in the layout" << frame;
return;
}
QRect geo = item->geometry();
geo.moveTopLeft(pos);
item->setGeometry(geo);
}
void MDILayoutWidget::resizeDockWidget(DockWidgetBase *dw, QSize size)
{
resizeDockWidget(dw->d->frame(), size);
}
void MDILayoutWidget::resizeDockWidget(Frame *frame, QSize size)
{
if (!frame)
return;
Layouting::Item *item = itemForFrame(frame);
if (!item) {
qWarning() << Q_FUNC_INFO << "Frame not found in the layout" << frame;
return;
}
item->setSize(size.expandedTo(frame->minimumSize()));
}

View File

@@ -0,0 +1,64 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef KDDOCKWIDGETS_MDI_LAYOUT_WIDGET_P_H
#define KDDOCKWIDGETS_MDI_LAYOUT_WIDGET_P_H
#include "LayoutWidget_p.h"
#include "kddockwidgets/KDDockWidgets.h"
#include "kddockwidgets/docks_export.h"
namespace Layouting {
class ItemFreeContainer;
}
namespace KDDockWidgets {
/**
* @brief The MDILayoutWidget class implements a layout suitable for MDI style docking.
* Where dock widgets are free to be positioned in arbitrary positions, not restricted by layouting.
*/
class DOCKS_EXPORT MDILayoutWidget : public LayoutWidget
{
Q_OBJECT
public:
explicit MDILayoutWidget(QWidgetOrQuick *parent = nullptr);
~MDILayoutWidget() override;
/// @brief docks the dock widgets into this MDI area, at the specified position
void addDockWidget(DockWidgetBase *dw, QPoint localPt, InitialOption addingOption);
/// @brief Moves a dock widget @p dw to point @p pos
void moveDockWidget(DockWidgetBase *dw, QPoint pos);
/// @brief Moves a dock widget @p f to point @p pos
/// Convenience overload.
void moveDockWidget(Frame *f, QPoint pos);
/// @brief Sets the size of dock widget @p dw to @p size
void resizeDockWidget(DockWidgetBase *dw, QSize size);
/// @brief Sets the size of dock widget @p f to @p size
/// Convenience overload.
void resizeDockWidget(Frame *f, QSize size);
/// @brief sets the size and position of the dock widget @p f
void setDockWidgetGeometry(Frame *f, QRect);
private:
Layouting::ItemFreeContainer *const m_rootItem;
};
}
#endif

View File

@@ -17,28 +17,28 @@
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#include "MultiSplitter_p.h"
#include "MainWindowBase.h"
#include "../LayoutSaver_p.h"
#include "Config.h"
#include "DockRegistry_p.h"
#include "DockWidgetBase.h"
#include "DockWidgetBase_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "FrameworkWidgetFactory.h"
#include "LayoutSaver.h"
#include "Logging_p.h"
#include "Frame_p.h"
#include "DockWidgetBase.h"
#include "MainWindowBase.h"
#include "MultiSplitter_p.h"
#include "Position_p.h"
#include "DockRegistry_p.h"
#include "Config.h"
#include "FrameworkWidgetFactory.h"
#include "multisplitter/Widget.h"
#include "DropArea_p.h"
#include "WindowBeingDragged_p.h"
#include "multisplitter/Widget.h"
#include <QScopedValueRollback>
using namespace KDDockWidgets;
MultiSplitter::MultiSplitter(QWidgetOrQuick *parent)
: LayoutGuestWidget(parent)
: LayoutWidget(parent)
{
Q_ASSERT(parent);
setRootItem(new Layouting::ItemBoxContainer(this));
@@ -46,8 +46,6 @@ MultiSplitter::MultiSplitter(QWidgetOrQuick *parent)
setLayoutSize(parent->size());
qCDebug(creation) << "MultiSplitter";
// Initialize min size
updateSizeConstraints();
@@ -56,59 +54,10 @@ MultiSplitter::MultiSplitter(QWidgetOrQuick *parent)
MultiSplitter::~MultiSplitter()
{
qCDebug(creation) << "~MultiSplitter" << this;
if (m_rootItem->hostWidget()->asQObject() == this)
delete m_rootItem;
DockRegistry::self()->unregisterLayout(this);
}
void MultiSplitter::onLayoutRequest()
{
updateSizeConstraints();
}
bool MultiSplitter::onResize(QSize newSize)
{
qCDebug(sizing) << Q_FUNC_INFO << "; new=" << newSize
<< "; window=" << window();
QScopedValueRollback<bool> resizeGuard(m_inResizeEvent, true); // to avoid re-entrancy
if (!LayoutSaver::restoreInProgress()) {
// don't resize anything while we're restoring the layout
setLayoutSize(newSize);
}
return false; // So QWidget::resizeEvent is called
}
bool MultiSplitter::isInMainWindow() const
{
return mainWindow() != nullptr;
}
MainWindowBase *MultiSplitter::mainWindow() const
{
if (auto pw = QWidgetAdapter::parentWidget()) {
// 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;
}
FloatingWindow *MultiSplitter::floatingWindow() const
{
return qobject_cast<FloatingWindow*>(QWidgetAdapter::parentWidget());
}
bool MultiSplitter::validateInputs(QWidgetOrQuick *widget,
Location location,
const Frame *relativeToFrame, InitialOption option) const
bool MultiSplitter::validateInputs(QWidgetOrQuick *widget, Location location,
const Frame *relativeToFrame, InitialOption option) const
{
if (!widget) {
qWarning() << Q_FUNC_INFO << "Widget is null";
@@ -164,14 +113,6 @@ void MultiSplitter::addWidget(QWidgetOrQuick *w, Location location,
InitialOption option)
{
auto frame = qobject_cast<Frame*>(w);
qCDebug(addwidget) << Q_FUNC_INFO << w
<< "; location=" << locationStr(location)
<< "; relativeTo=" << relativeToWidget
<< "; size=" << size()
<< "; w.size=" << w->size()
<< "; frame=" << frame
<< "; options=" << option;
if (itemForFrame(frame) != nullptr) {
// Item already exists, remove it.
// Changing the frame parent will make the item clean itself up. It turns into a placeholder and is removed by unrefOldPlaceholders
@@ -232,55 +173,11 @@ void MultiSplitter::addMultiSplitter(MultiSplitter *sourceMultiSplitter, Locatio
addWidget(sourceMultiSplitter, location, relativeTo, option);
}
void MultiSplitter::removeItem(Layouting::Item *item)
{
if (!item) {
qWarning() << Q_FUNC_INFO << "nullptr item";
return;
}
item->parentContainer()->removeItem(item);
}
bool MultiSplitter::containsItem(const Layouting::Item *item) const
{
return m_rootItem->contains_recursive(item);
}
bool MultiSplitter::containsFrame(const Frame *frame) const
{
return itemForFrame(frame) != nullptr;
}
int MultiSplitter::count() const
{
return m_rootItem->count_recursive();
}
int MultiSplitter::visibleCount() const
{
return m_rootItem->visibleCount_recursive();
}
int MultiSplitter::placeholderCount() const
{
return count() - visibleCount();
}
QVector<Layouting::Separator*> MultiSplitter::separators() const
{
return m_rootItem->separators_recursive();
}
void MultiSplitter::updateSizeConstraints()
{
const QSize newMinSize = m_rootItem->minSize();
qCDebug(sizing) << Q_FUNC_INFO << "Updating size constraints from" << minimumSize()
<< "to" << newMinSize;
setLayoutMinimumSize(newMinSize);
}
int MultiSplitter::availableLengthForOrientation(Qt::Orientation orientation) const
{
if (orientation == Qt::Vertical)
@@ -294,69 +191,6 @@ QSize MultiSplitter::availableSize() const
return m_rootItem->availableSize();
}
Layouting::Item *MultiSplitter::itemForFrame(const Frame *frame) const
{
if (!frame)
return nullptr;
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))
return { frame };
if (auto msw = qobject_cast<MultiSplitter*>(frameOrMultiSplitter))
return msw->frames();
return {};
}
Frame::List MultiSplitter::frames() const
{
const Layouting::Item::List items = m_rootItem->items_recursive();
Frame::List result;
result.reserve(items.size());
for (Layouting::Item *item : items) {
if (auto f = static_cast<Frame*>(item->guestAsQObject()))
result.push_back(f);
}
return result;
}
void MultiSplitter::restorePlaceholder(DockWidgetBase *dw, Layouting::Item *item, int tabIndex)
{
if (item->isPlaceholder()) {
Frame *newFrame = Config::self().frameworkWidgetFactory()->createFrame(this);
item->restore(newFrame);
}
auto frame = qobject_cast<Frame*>(item->guestAsQObject());
Q_ASSERT(frame);
if (tabIndex != -1 && frame->dockWidgetCount() >= tabIndex) {
frame->insertWidget(dw, tabIndex);
} else {
frame->addWidget(dw);
}
frame->QWidgetAdapter::setVisible(true);
}
void MultiSplitter::layoutEqually()
{
layoutEqually(m_rootItem);
@@ -371,75 +205,10 @@ void MultiSplitter::layoutEqually(Layouting::ItemBoxContainer *container)
}
}
void MultiSplitter::clearLayout()
{
m_rootItem->clear();
}
bool MultiSplitter::checkSanity() const
{
return m_rootItem->checkSanity();
}
void MultiSplitter::unrefOldPlaceholders(const Frame::List &framesBeingAdded) const
{
for (Frame *frame : framesBeingAdded) {
for (DockWidgetBase *dw : frame->dockWidgets()) {
dw->lastPositions().removePlaceholders(this);
}
}
}
void MultiSplitter::dumpLayout() const
{
m_rootItem->dumpLayout();
}
void MultiSplitter::setLayoutSize(QSize size)
{
if (size != this->size()) {
m_rootItem->setSize_recursive(size);
if (!m_inResizeEvent && !LayoutSaver::restoreInProgress())
resize(size);
}
}
QSize MultiSplitter::layoutMinimumSize() const
{
return m_rootItem->minSize();
}
QSize MultiSplitter::layoutMaximumSizeHint() const
{
return m_rootItem->maxSizeHint();
}
QSize MultiSplitter::size() const { return m_rootItem->size(); }
void MultiSplitter::setLayoutMinimumSize(QSize sz)
{
if (sz != m_rootItem->minSize()) {
setLayoutSize(size().expandedTo(m_rootItem->minSize())); // Increase size in case we need to
m_rootItem->setMinSize(sz);
}
qCDebug(sizing) << Q_FUNC_INFO << "minSize = " << m_rootItem->minSize();
}
void MultiSplitter::setRootItem(Layouting::ItemBoxContainer *root)
{
delete m_rootItem;
LayoutWidget::setRootItem(root);
m_rootItem = root;
connect(m_rootItem, &Layouting::ItemBoxContainer::numVisibleItemsChanged,
this, &MultiSplitter::visibleWidgetCountChanged);
connect(m_rootItem, &Layouting::ItemBoxContainer::minSizeChanged, this, [this] {
setMinimumSize(layoutMinimumSize());
});
}
const Layouting::Item::List MultiSplitter::items() const
{
return m_rootItem->items_recursive();
}
Layouting::ItemBoxContainer *MultiSplitter::rootItem() const
@@ -467,34 +236,5 @@ QRect MultiSplitter::rectForDrop(const WindowBeingDragged *wbd, Location locatio
bool MultiSplitter::deserialize(const LayoutSaver::MultiSplitter &l)
{
setRootItem(new Layouting::ItemBoxContainer(this));
QHash<QString, Layouting::Widget*> frames;
for (const LayoutSaver::Frame &frame : qAsConst(l.frames)) {
Frame *f = Frame::deserialize(frame);
Q_ASSERT(!frame.id.isEmpty());
frames.insert(frame.id, f);
}
m_rootItem->fillFromVariantMap(l.layout, frames);
updateSizeConstraints();
m_rootItem->setSize_recursive(QWidgetAdapter::size());
return true;
}
LayoutSaver::MultiSplitter MultiSplitter::serialize() const
{
LayoutSaver::MultiSplitter l;
l.layout = m_rootItem->toVariantMap();
const Layouting::Item::List items = m_rootItem->items_recursive();
l.frames.reserve(items.size());
for (Layouting::Item *item : items) {
if (!item->isContainer()) {
if (auto frame = qobject_cast<Frame*>(item->guestAsQObject()))
l.frames.insert(frame->id(), frame->serialize());
}
}
return l;
return LayoutWidget::deserialize(l);
}

View File

@@ -22,25 +22,15 @@
#ifndef KDDOCKWIDGETS_MULTISPLITTER_P_H
#define KDDOCKWIDGETS_MULTISPLITTER_P_H
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include "LayoutWidget_p.h"
#include "kddockwidgets/KDDockWidgets.h"
#include "kddockwidgets/private/LayoutSaver_p.h"
namespace Layouting {
class Item;
class Separator;
class Widget_qwidget;
}
#include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/docks_export.h"
class TestDocks;
namespace KDDockWidgets {
class MainWindowBase;
class FloatingWindow;
class Frame;
struct WindowBeingDragged;
/**
@@ -54,16 +44,12 @@ struct WindowBeingDragged;
* 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 MultiSplitter
: public LayoutGuestWidget
class DOCKS_EXPORT MultiSplitter : public LayoutWidget
{
Q_OBJECT
public:
explicit MultiSplitter(QWidgetOrQuick *parent = nullptr);
~MultiSplitter() override;
bool isInMainWindow() const;
MainWindowBase* mainWindow() const;
FloatingWindow* floatingWindow() const;
/**
* @brief Adds a widget to this MultiSplitter.
@@ -82,48 +68,6 @@ public:
Frame *relativeTo = nullptr,
InitialOption option = DefaultSizeMode::Fair);
/**
* @brief Removes an item from this MultiSplitter.
*/
void removeItem(Layouting::Item *item);
/**
* @brief Returns true if this layout contains the specified item.
*/
bool containsItem(const Layouting::Item *) const;
/**
* @brief Returns true if this layout contains the specified frame.
*/
bool containsFrame(const Frame *) const;
/**
* @brief Returns the number of Item objects in this layout.
* This includes non-visible (placeholder) Items too.
* @sa visibleCount
*/
int count() const;
/**
* @brief Returns the number of visible Items in this layout.
* Which is @ref count minus @ref placeholderCount
* @sa count
*/
int visibleCount() const;
/**
* @brief Returns the number of placeholder items in this layout.
* This is the same as @ref count minus @ref visibleCount
* @sa count, visibleCount
*/
int placeholderCount() const;
/**
* @brief The list of items in this layout.
*/
const QVector<Layouting::Item*> items() const;
/**
* Called by the indicators, so they draw the drop rubber band at the correct place.
* The rect for the rubberband when dropping a widget at the specified location.
@@ -133,123 +77,26 @@ public:
QRect rectForDrop(const WindowBeingDragged *wbd, KDDockWidgets::Location location,
const Layouting::Item *relativeTo) const;
bool deserialize(const LayoutSaver::MultiSplitter &);
LayoutSaver::MultiSplitter serialize() const;
bool deserialize(const LayoutSaver::MultiSplitter &) override;
///@brief returns the list of separators
QVector<Layouting::Separator*> separators() const;
/**
* @brief Updates the min size of this layout.
*/
void updateSizeConstraints();
/**
* @brief setter for the contents size
* The "contents size" is just the size() of this layout. However, since resizing
* QWidgets is async and we need it to be sync. As sometimes adding widgets will increase
* the MultiSplitter size (due to widget's min-size constraints).
*/
void setLayoutSize(QSize);
/**
* @brief returns the contents width.
* Usually it's the same width as the respective parent MultiSplitter.
*/
int width() const { return size().width(); }
/**
* @brief returns the contents height.
* Usually it's the same height as the respective parent MultiSplitter.
*/
int height() const { return size().height(); }
/**
* @brief returns the layout's minimum size
* @ref setLayoutMinimumSize
*/
QSize layoutMinimumSize() const;
/**
* @brief returns the layout's maximum size hint
*/
QSize layoutMaximumSizeHint() const;
/**
* @brief getter for the size
*/
QSize size() const;
/// @brief Runs some sanity checks. Returns true if everything is OK
bool checkSanity() const;
/// @brief dumps the layout to stderr
void dumpLayout() const;
/**
* @brief returns the Item that holds @p frame in this layout
*/
Layouting::Item *itemForFrame(const Frame *frame) const;
/**
* @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);
/// @brief See docs for MainWindowBase::layoutEqually()
void layoutEqually();
/// @brief overload that just resizes widgets within a sub-tree
void layoutEqually(Layouting::ItemBoxContainer *);
/// @brief clears the layout
void clearLayout();
Q_SIGNALS:
void visibleWidgetCountChanged(int count);
protected:
void onLayoutRequest() override;
bool onResize(QSize newSize) override;
private:
bool m_inResizeEvent = false;
friend class ::TestDocks;
/**
* @brief returns the frames contained in @p frameOrMultiSplitter
* If frameOrMultiSplitter is a Frame, it returns a list of 1 element, with that frame
* If frameOrMultiSplitter is a MultiSplitter then it returns a list of all frames it contains
*/
QList<Frame*> framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const;
Layouting::ItemBoxContainer *rootItem() const;
// For debug/hardening
bool validateInputs(QWidgetOrQuick *widget, KDDockWidgets::Location location,
const Frame *relativeToFrame, InitialOption option) const;
/**
* @brief Removes unneeded placeholder items when adding new frames.
*
* A floating frame A might have a placeholder in the main window (for example to remember its position on the Left),
* but then the user might attach it to the right, so the left placeholder is no longer need.
* Right before adding the frame to the right we remove the left placeholder, otherwise it's unrefed while we're adding
* causing a segfault. So what this does is making the unrefing happen a bit earlier.
*/
void unrefOldPlaceholders(const QList<Frame*> &framesBeingAdded) const;
/**
* @brief setter for the minimum size
* @ref minimumSize
*/
void setLayoutMinimumSize(QSize);
void setRootItem(Layouting::ItemBoxContainer *);

View File

@@ -15,10 +15,11 @@
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#include "Position_p.h"
#include "DockRegistry_p.h"
#include "MultiSplitter_p.h"
#include "FloatingWindow_p.h"
#include "LayoutSaver_p.h"
#include "LayoutWidget_p.h"
#include "Position_p.h"
#include <algorithm>
@@ -87,7 +88,7 @@ void Position::removePlaceholders()
m_placeholders.clear();
}
void Position::removePlaceholders(const MultiSplitter *ms)
void Position::removePlaceholders(const LayoutWidget *ms)
{
m_placeholders.erase(std::remove_if(m_placeholders.begin(), m_placeholders.end(), [ms] (const std::unique_ptr<ItemRef> &itemref) {
return itemref->item->hostWidget() == *ms;
@@ -119,17 +120,20 @@ void Position::removePlaceholder(Layouting::Item *placeholder)
void Position::deserialize(const LayoutSaver::Position &lp)
{
for (const auto &placeholder : qAsConst(lp.placeholders)) {
MultiSplitter *layout;
LayoutWidget *layout;
int itemIndex = placeholder.itemIndex;
if (placeholder.isFloatingWindow) {
const int index = placeholder.indexOfFloatingWindow;
if (index == -1) {
continue; // Skip
} else {
const auto floatingWindows = DockRegistry::self()->floatingWindows();
if (index >= 0 && index < floatingWindows.size()) {
FloatingWindow *fw = floatingWindows.at(index);
layout = fw->multiSplitter();
auto serializedFw = LayoutSaver::Layout::s_currentLayoutBeingRestored->floatingWindowForIndex(index);
if (serializedFw.isValid()) {
if (FloatingWindow *fw = serializedFw.floatingWindowInstance) {
layout = fw->layoutWidget();
} else {
continue;
}
} else {
qWarning() << "Invalid floating window position to restore" << index;
continue;
@@ -137,7 +141,7 @@ void Position::deserialize(const LayoutSaver::Position &lp)
}
} else {
MainWindowBase *mainWindow = DockRegistry::self()->mainWindowByName(placeholder.mainWindowUniqueName);
layout = mainWindow->multiSplitter();
layout = mainWindow->layoutWidget();
}
const Layouting::Item::List &items = layout->items();
@@ -162,7 +166,7 @@ LayoutSaver::Position Position::serialize() const
LayoutSaver::Placeholder p;
Layouting::Item *item = itemRef->item;
MultiSplitter *layout = DockRegistry::self()->layoutForItem(item);
LayoutWidget *layout = DockRegistry::self()->layoutForItem(item);
const auto itemIndex = layout->items().indexOf(item);
auto fw = layout->floatingWindow();
@@ -202,3 +206,18 @@ ItemRef::~ItemRef()
item->unref();
}
}
LayoutSaver::Position LastPositions::serialize()
{
LayoutSaver::Position result = lastPosition->serialize();
result.lastFloatingGeometry = lastFloatingGeometry();
result.lastOverlayedGeometries = m_lastOverlayedGeometries;
return result;
}
void LastPositions::deserialize(const LayoutSaver::Position &p)
{
m_lastFloatingGeometry = p.lastFloatingGeometry;
m_lastOverlayedGeometries = p.lastOverlayedGeometries;
lastPosition->deserialize(p);
}

View File

@@ -18,13 +18,15 @@
#ifndef KDDOCKWIDGETS_POSITION_P_H
#define KDDOCKWIDGETS_POSITION_P_H
#include "docks_export.h"
#include "kddockwidgets/docks_export.h"
#include "Logging_p.h"
#include "LayoutSaver_p.h"
#include "LayoutSaver.h"
#include "QWidgetAdapter.h"
#include <QScopedValueRollback>
#include <QHash>
#include <QPointer>
#include <QScopedValueRollback>
#include <memory>
@@ -34,7 +36,7 @@ class Item;
namespace KDDockWidgets {
class MultiSplitter;
class LayoutWidget;
// Just a RAII class so we don't forget to unref
struct ItemRef
@@ -103,7 +105,7 @@ public:
const std::vector<std::unique_ptr<ItemRef>>& placeholders() const { return m_placeholders; }
///@brief Removes the placeholders that belong to this multisplitter
void removePlaceholders(const MultiSplitter *);
void removePlaceholders(const LayoutWidget *);
///@brief Removes the placeholders that reference a FloatingWindow
void removeNonMainWindowPlaceholders();
@@ -143,19 +145,19 @@ struct LastPositions
return m_lastFloatingGeometry;
}
LayoutSaver::Position serialize()
QRect lastOverlayedGeometry(SideBarLocation loc) const
{
LayoutSaver::Position result = lastPosition->serialize();
result.lastFloatingGeometry = lastFloatingGeometry();
return result;
return m_lastOverlayedGeometries.value(loc);
}
void deserialize(const LayoutSaver::Position &p)
void setLastOverlayedGeometry(SideBarLocation loc, QRect rect)
{
m_lastFloatingGeometry = p.lastFloatingGeometry;
lastPosition->deserialize(p);
m_lastOverlayedGeometries[loc] = rect;
}
LayoutSaver::Position serialize();
void deserialize(const LayoutSaver::Position &p);
Layouting::Item* lastItem() const {
return lastPosition->layoutItem();
}
@@ -174,7 +176,8 @@ struct LastPositions
lastPosition->removePlaceholders();
}
void removePlaceholders(const MultiSplitter *hostWidget) const {
void removePlaceholders(const LayoutWidget *hostWidget) const
{
lastPosition->removePlaceholders(hostWidget);
}
@@ -184,6 +187,7 @@ struct LastPositions
private:
QRect m_lastFloatingGeometry;
QHash<SideBarLocation, QRect> m_lastOverlayedGeometries;
friend inline QDebug operator<<(QDebug d, const KDDockWidgets::LastPositions &);
Position::Ptr lastPosition = std::make_shared<Position>();

View File

@@ -12,7 +12,8 @@
#ifndef KD_SIDEBAR_P_H
#define KD_SIDEBAR_P_H
#include "docks_export.h"
#include "kddockwidgets/docks_export.h"
#include "KDDockWidgets.h"
#include "QWidgetAdapter.h"

View File

@@ -18,14 +18,15 @@
*/
#include "TabWidget_p.h"
#include "Config.h"
#include "DockWidgetBase_p.h"
#include "DragController_p.h"
#include "FloatingWindow_p.h"
#include "Frame_p.h"
#include "WindowBeingDragged_p.h"
#include "FrameworkWidgetFactory.h"
#include "Logging_p.h"
#include "Utils_p.h"
#include "Config.h"
#include "FrameworkWidgetFactory.h"
#include "WindowBeingDragged_p.h"
#ifdef QT_WIDGETS_LIB
# include <QTabWidget>
@@ -93,6 +94,12 @@ std::unique_ptr<WindowBeingDragged> TabBar::makeWindow()
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, draggable));
}
bool TabBar::isWindow() const
{
// Same semantics as tab widget, no need to duplicate logic
return m_tabWidget->isWindow();
}
void TabBar::onMousePress(QPoint localPos)
{
m_lastPressedDockWidget = dockWidgetAt(localPos);
@@ -130,6 +137,12 @@ DockWidgetBase *TabBar::singleDockWidget() const
return m_tabWidget->singleDockWidget();
}
bool TabBar::isMDI() const
{
Frame *f = frame();
return f && f->isMDI();
}
Frame *TabBar::frame() const
{
return m_tabWidget->frame();
@@ -172,7 +185,7 @@ bool TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
return false;
}
QPointer<Frame> oldFrame = dock->frame();
QPointer<Frame> oldFrame = dock->d->frame();
insertDockWidget(index, dock, dock->icon(DockWidgetBase::IconPlace::TabBar), dock->title());
setCurrentDockWidget(index);
@@ -224,12 +237,22 @@ std::unique_ptr<WindowBeingDragged> TabWidget::makeWindow()
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
r.moveTopLeft(globalPoint);
floatingWindow->setSuggestedGeometry(r);
floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
floatingWindow->show();
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, this));
}
bool TabWidget::isWindow() const
{
if (auto floatingWindow = qobject_cast<FloatingWindow*>(asWidget()->window())) {
// Case of dragging via the tab widget when the title bar is hidden
return floatingWindow->hasSingleFrame();
}
return false;
}
DockWidgetBase *TabWidget::singleDockWidget() const
{
if (m_frame->hasSingleDockWidget())
@@ -238,6 +261,11 @@ DockWidgetBase *TabWidget::singleDockWidget() const
return nullptr;
}
bool TabWidget::isMDI() const
{
return m_frame && m_frame->isMDI();
}
void TabWidget::onTabInserted()
{
m_frame->onDockWidgetCountChanged();

View File

@@ -57,6 +57,7 @@ public:
// Draggable
std::unique_ptr<WindowBeingDragged> makeWindow() override;
bool isWindow() const override;
void onMousePress(QPoint localPos);
void onMouseDoubleClick(QPoint localPos);
@@ -80,8 +81,14 @@ public:
DockWidgetBase *singleDockWidget() const override;
/// @reimp
bool isMDI() const override;
Frame *frame() const;
/// Like QTabBar::moveTab(from, to)
virtual void moveTabTo(int from, int to) = 0;
private:
TabWidget *const m_tabWidget;
QPointer<DockWidgetBase> m_lastPressedDockWidget = nullptr;
@@ -109,7 +116,7 @@ public:
/**
* @brief Returns the index of the dock widget, or -1 if it doesn't exist
*/
virtual int indexOfDockWidget(DockWidgetBase *) const = 0;
virtual int indexOfDockWidget(const DockWidgetBase *) const = 0;
/**
* @brief Sets the current dock widget index
@@ -169,6 +176,10 @@ public:
// Draggable interface
std::unique_ptr<WindowBeingDragged> makeWindow() override;
DockWidgetBase *singleDockWidget() const override;
bool isWindow() const override;
/// @reimp
bool isMDI() const override;
//Q_SIGNALS: // Not a OQbject
virtual void currentTabChanged(int index) = 0;

View File

@@ -51,10 +51,7 @@ TitleBar::TitleBar(FloatingWindow *parent)
, 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);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMinimizeButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateButtons);
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::activatedChanged , this, &TitleBar::isFocusedChanged);
init();
@@ -63,14 +60,14 @@ TitleBar::TitleBar(FloatingWindow *parent)
void TitleBar::init()
{
qCDebug(creation) << "TitleBar" << this;
setFixedHeight(30);
setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
connect(this, &TitleBar::isFocusedChanged, this, [this] {
// repaint
update();
});
updateCloseButton();
updateFloatButton();
updateButtons();
}
TitleBar::~TitleBar()
@@ -91,6 +88,35 @@ bool TitleBar::onDoubleClicked()
return false;
}
MainWindowBase *TitleBar::mainWindow() const
{
if (m_floatingWindow)
return nullptr;
if (m_frame)
return m_frame->mainWindow();
qWarning() << Q_FUNC_INFO << "null frame and null floating window";
return nullptr;
}
bool TitleBar::isMDI() const
{
if (auto mw = mainWindow())
return mw->isMDI();
return false;
}
void TitleBar::updateButtons()
{
updateCloseButton();
updateFloatButton();
updateMaximizeButton();
updateMinimizeButton();
updateAutoHideButton();
}
void TitleBar::updateCloseButton()
{
@@ -172,8 +198,9 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
if (!isVisible() && window()->isVisible()) {
qWarning() << "TitleBar::makeWindow shouldn't be called on invisible title bar"
<< this << window()->isVisible();
if (m_floatingWindow) {
if (m_frame) {
qWarning() << "this=" << this << "; actual=" << m_frame->actualTitleBar();
} else if (m_floatingWindow) {
qWarning() << "Has floating window with titlebar=" << m_floatingWindow->titleBar()
<< "; fw->isVisible=" << m_floatingWindow->isVisible();
}
@@ -187,7 +214,6 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(m_floatingWindow, this));
}
qCDebug(hovering) << "TitleBar::makeWindow: isFloating=" << floatingWindow() << "; isTheOnlyFrame=" << m_frame->isTheOnlyFrame() << "; frame=" << m_frame;
if (FloatingWindow *fw = QWidgetAdapter::floatingWindow()) { // Already floating
if (m_frame->isTheOnlyFrame()) { // We dont' detach. This one drags the entire window instead.
qCDebug(hovering) << "TitleBar::makeWindow no detach needed";
@@ -196,13 +222,11 @@ 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)));
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
floatingWindow->setSuggestedGeometry(r);
floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
floatingWindow->show();
qCDebug(hovering) << "TitleBar::makeWindow setting geometry" << r << "actual=" << floatingWindow->geometry();
auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable*>(floatingWindow)
: static_cast<Draggable*>(this);
@@ -284,16 +308,40 @@ QIcon TitleBar::icon() const
void TitleBar::onCloseClicked()
{
const bool closeOnlyCurrentTab = Config::self().flags() & Config::Flag_CloseOnlyCurrentTab;
if (m_frame) {
if (m_frame->isTheOnlyFrame() && !m_frame->isInMainWindow()) {
m_frame->window()->close();
if (closeOnlyCurrentTab) {
if (DockWidgetBase *dw = m_frame->currentDockWidget()) {
dw->close();
} else {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
}
} else {
m_frame->close();
if (m_frame->isTheOnlyFrame() && !m_frame->isInMainWindow()) {
m_frame->window()->close();
} else {
m_frame->close();
}
}
} else if (m_floatingWindow) {
if (closeOnlyCurrentTab) {
if (Frame *f = m_floatingWindow->singleFrame()) {
if (DockWidgetBase *dw = f->currentDockWidget()) {
dw->close();
} else {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
}
} else {
m_floatingWindow->close();
}
} else {
m_floatingWindow->close();
}
}
if (m_floatingWindow)
m_floatingWindow->close();
}
bool TitleBar::isFloating() const
@@ -419,3 +467,8 @@ void TitleBar::updateFloatButton()
setFloatButtonToolTip(floatingWindow() ? tr("Dock window") : tr("Undock window"));
setFloatButtonVisible(supportsFloatingButton());
}
bool TitleBar::isWindow() const
{
return m_floatingWindow != nullptr;
}

Some files were not shown because too many files have changed in this diff Show More