Compare commits

...

166 Commits

Author SHA1 Message Date
Sergio Martins
86d1a52bbf Fix unit-test
Was leaving window leftovers behind.
Delete them.
2020-05-03 16:41:52 +01:00
Sergio Martins
2d007c558a Update .gitignore 2020-05-02 14:18:30 +01:00
Sergio Martins
dc4a1975ab Fix build with cmake < 3.12
3.12 is needed for HOMEPAGE_URL and DESCRIPTION support.

Bumped the minimum to 3.7, older fails.
Fixes: 43
2020-05-02 14:14:04 +01:00
Sergio Martins
144897e831 Fix namespaced build 2020-05-01 15:35:05 +01:00
Sergio Martins
e87f6d001e example: Fix duplicate command line argument switch 2020-05-01 13:09:27 +01:00
Sergio Martins
8cc53ca5bb Also build the example when building KDDW
No need to install the library anymore
Fixes: #42
2020-05-01 13:07:25 +01:00
Sergio Martins
1b337b5391 Merge pull request #41 from KDAB/float_action
Add test for dock floating action
2020-05-01 11:45:43 +01:00
Colin Ogilvie
e383bc797e Run tests with ctest 2020-04-30 18:12:35 +01:00
Colin Ogilvie
43a25ef93b Add test for foating action 2020-04-30 16:56:13 +01:00
Sergio Martins
19e519912b Merge pull request #40 from KDAB/float_action
Float action
2020-04-30 13:45:07 +01:00
Colin Ogilvie
68f0c64954 Add a DockWidgetBase::floatAction()
Helper action to allow docking/detaching
2020-04-30 13:39:02 +01:00
Sergio Martins
e8db004874 Windows: Fix maximized window size when using the maximize
When the maximize is triggered by Qt, we get the WM_GETMINMAXINFO
too soon, as QWindow already says it's maximized, while it's still
in process of being.

Just removed the condition, doesn't seem to have any bad side-effect
2020-04-20 20:55:11 +01:00
Sergio Martins
6ee3cfbb2d Introduce "Maximize/restore" title bar button support
As happens with other docking frameworks:
- Maximized/restore button only visible when window is floating
- floating button is never visible

Available by setting Flag_TitleBarHasMaximizeButton.
Added example too.

Fixes: #37
2020-04-20 19:55:51 +01:00
Sergio Martins
ab0fc1e328 Add DockWidget::Option_NonDockable
A dock widget with this option will always be floating and not be
able to dock into anything. Other widgets can't dock into it either.
2020-04-13 13:23:38 +01:00
Sergio Martins
899ca6af6a Merge pull request #34 from jcelerier/master
Add an include needed from Qt 5.15
2020-03-27 12:31:56 +00:00
Jean-Michaël Celerier
c8c56d293c Add an include needed from Qt 5.15 2020-03-26 23:27:23 +01:00
Sergio Martins
b236d744e3 Fix calculating main window scale 2020-03-25 15:56:19 +00:00
Sergio Martins
425d859f5b Merge pull request #33 from KDAB/fix_tabbarwidget_leak
Don't leak TabBarWidget's proxy style
2020-03-25 13:35:04 +00:00
Milian Wolff
d58abf170f Don't leak TabBarWidget's proxy style
Fixes leak reported by LSAN:

```
    #0 0x7f7b5687e968 in operator new(unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:104
    #1 0x7f7b2e5b1096 in KDDockWidgets::TabBarWidget::TabBarWidget(KDDockWidgets::TabWidget*) ../3rdParty/kddockwidgets/src/private/widgets/TabBarWidget.cpp:71
    #2 0x7f7b2e295ba4 in KDDockWidgets::DefaultWidgetFactory::createTabBar(KDDockWidgets::TabWidget*) const ../3rdParty/kddockwidgets/src/FrameworkWidgetFactory.cpp:71
    #3 0x7f7b2e5b8f54 in KDDockWidgets::TabWidgetWidget::TabWidgetWidget(KDDockWidgets::Frame*) ../3rdParty/kddockwidgets/src/private/widgets/TabWidgetWidget.cpp:38
```

The owernship rules for QProxyStyle <-> QStyle are pure madness.
We do not want to delete the proxy we use as a base. And we must
not ever create multiple proxy styles for the same style either.
So add a static singleton for the proxy style and set its parent
to the qApp. This seems to work fine, at least the leak is gone
and we also don't get a crash at shutdown...
2020-03-25 14:03:24 +01:00
Sergio Martins
225aa6a098 Add unit-test for DockWidget::raise()
and make it pass on offscreen platform too, as that QPA plugin
fails to keep z-order between order.
2020-03-24 22:36:21 +00:00
Sergio Martins
b0665cd003 When raising, also set current tab if it's floating
If the dock widget is in a FloatingWindow, but hidden by some
other tab, we not only need to raise the FloatingWindow but also
set our tab current
2020-03-24 22:12:28 +00:00
Sergio Martins
3a501d1b5c DebugWindow: Add test to DockWidget::raise() 2020-03-24 22:06:53 +00:00
Sergio Martins
acfd53672c Add DockWidget::raise()
Mostly like QWindow::raise(), but if in a tab widget, makes the tab
widget current too.
2020-03-24 22:06:13 +00:00
Sergio Martins
afb3edfd1d unit-tests++ 2020-03-24 13:33:51 +00:00
Sergio Martins
b2c3442233 example: Move the floating window away from the top-border
Fixes on Windows the its titlebar being hidden
2020-03-24 11:16:27 +00:00
Sergio Martins
fc63f843b7 Workaround QTBUG-83030
Fixes a case where the floating window wouldn't disappear.
This is deep in Qt. Got it down to a minimal test-case and reported it.

Probably won't get fixed ever.
2020-03-24 10:58:31 +00:00
Sergio Martins
f77d052dbb Allow to change NonClosable mid game
Before it was immutable, now you can change it
2020-03-23 16:28:16 +00:00
Sergio Martins
cc1e1eaa51 Don't flicker when showing a floating dock widget
Create the FloatingWindow directly, instead of creating the DockWidget
then reparenting it
2020-03-07 21:15:44 +00:00
Sergio Martins
ca4d63ed2e Don't override the users tab bar QProxyStyle 2020-03-05 18:02:23 +00:00
Sergio Martins
6564be122f Workaround Qt QWidget::showMaximized() bug on windows
When we cover the native title bar and maximize the window, it won't
appear at 0,0, but at -5, -5 or so.

Use Windows native maximize instead, which works fine
2020-03-02 12:15:37 +00:00
Sergio Martins
eabcdfb945 Add unit-test for affinity 2020-03-01 15:03:29 +00:00
Sergio Martins
5b91021745 LayoutSaver: insert the DockWidget ptr into the cash 2020-03-01 15:03:07 +00:00
Sergio Martins
dbd90b8a02 LayoutSaver: Clear by affinity too
When restoring windows of the specified affinity clear only
the ones of that affinity
2020-03-01 15:02:22 +00:00
Sergio Martins
9904d847f2 LayoutSaver now can save/restore a sub-set of windows
Each subset is identified by its "affinity name".
See DockWidget/MainWindow setAffinityName property.

Patch contributed by Andras Mantia, I've changed it to deal in
terms of affinity names instead of unique names.
2020-03-01 14:25:31 +00:00
Sergio Martins
dedda6cb14 LayoutSaver: Also store the affinity in the JSON 2020-03-01 14:13:02 +00:00
Sergio Martins
2143b09206 DockRegistry: Minor refactoring
- affinitiesForMainWindowNames() pulled to a separate method
- By appending QString() (empty affinity) to the affinity list it
simplifies the ifs, which reduces the lambdas to a single comparison.
- Remove the lambdas as they're used only once now and only have 1 comparison,
put the check directly in the if
2020-03-01 12:45:17 +00:00
Andras Mantia
947d638cb5 Added a DockRegistry::clear() overload to close a subset
(Part of pull/31)
2020-03-01 12:34:44 +00:00
Sergio Martins
63e662323f Added unit-test for Flag_DoubleClickMaximizes 2020-02-27 15:13:01 +00:00
Sergio Martins
90b893e46a Introduce Flag_DoubleClickMaximizes
Double clicking on the title bar of a floating window will maximize it.
2020-02-27 15:00:31 +00:00
Sergio Martins
802c6206ce Added comment, so we don't remove this warning 2020-02-26 23:36:08 +00:00
Sergio Martins
ce6fd20efd LayoutSaver: Also restore position percentage
Fixes anchors going to weird places when the window is resized.
Calculated percentage was wrong because layout hadn't been deserialized
yet.
2020-02-26 23:31:59 +00:00
Sergio Martins
af5bb4a3e4 Also store the percentage position in the json
just for checking that the restore is sane, even though
that property is calculated at runtime
2020-02-26 22:45:40 +00:00
Sergio Martins
ee786f57c5 LayoutSaver: Fix rounding errors when doing a relative restore
After a restore with relative sizes the items might not be on a pixel
perfect position regarding to their anchors. Make sure they are after
the restore is complete.
2020-02-25 23:24:39 +00:00
Sergio Martins
170f4efbb2 example: Add a menu action to close all dock widgets 2020-02-25 23:23:36 +00:00
Sergio Martins
d97b001ec8 DebugWindow: Make the actions apply to all layouts
even the ones in floating windows
2020-02-25 23:23:10 +00:00
Sergio Martins
043b481ddd Example: Add a quit action 2020-02-25 23:19:10 +00:00
Sergio Martins
2155e11bd7 Debug++ 2020-02-25 23:13:41 +00:00
Sergio Martins
670da82164 DebugWindow: Add an option to resize the window by QSize(1, 1)
For debug purposes. Resizing the window fixes some layouting issues
in FloatingWindows when restoring a layout with relative sizes,
need to find out why
2020-02-25 23:08:16 +00:00
Sergio Martins
3f732e91a7 Minor refactoring 2020-02-25 22:53:28 +00:00
Sergio Martins
a286432176 DebugWindow: Add option to invoke "redistributeSpace"
for testing purposes
2020-02-25 22:41:11 +00:00
Sergio Martins
1d7c0316ab LayoutSaver: Fix applying factor to position
Was adding instead of multiplying, typo...
2020-02-25 21:18:36 +00:00
Sergio Martins
564a7091e1 LayoutSaver: Fix off-by-ones case with relative-restoring
If the window didn't change size nothing should happen
2020-02-25 20:00:46 +00:00
Sergio Martins
ca211f2494 LayoutSaver: Add an option to restore in relative sizes
and not touching the main window geometry.

The option is not exposed yet, there's a layouting bug to fix first.
2020-02-25 19:06:43 +00:00
Sergio Martins
435e61288e LayoutSaver: Remove the QDataStream serializer
Keep only the deserializer, for now, so we can convert old layouts
to json.
2020-02-24 15:22:44 +00:00
Sergio Martins
822f48ccdd DebugWindow: Add a way to update from old layouts to json 2020-02-24 15:20:13 +00:00
Sergio Martins
6fb0694aab LayoutSaver: Replace saveToDisk() with saveToFile(jsonFilename)
Same for restore.
This is better API as LayoutSaver shouldn't be concerned about
QSettings. It also assumed the user only had a single layout, which
is normally not the case.

And finally, storing escaped json in .ini files is not very elegant.
2020-02-24 15:10:12 +00:00
Sergio Martins
bab0ff085e LayoutSaver now saves/loads in JSON format
This makes it easier to extend the format without being worried
about adding version checks all over the code
2020-02-24 14:57:03 +00:00
Sergio Martins
5b0eb93cda LayoutSaver: Add a json to/from methods
Still not being used by public api though.
2020-02-24 14:45:06 +00:00
Sergio Martins
758b4de5cc LayoutSaver: Fix serializing QSize and QRect to json
Failed silently to null instead
2020-02-24 14:40:38 +00:00
Sergio Martins
b78becfc01 Fix typo in json serialization 2020-02-24 13:39:48 +00:00
Sergio Martins
15d9f074fa Fix converting Item to json 2020-02-24 13:38:54 +00:00
Sergio Martins
fdb95e344a Fix converting Placeholder to json
Only dump the floating window index if we're in a floating window
2020-02-24 13:31:38 +00:00
Sergio Martins
69ada1bdad LayoutSaver: Add to/from variant map methods
To make it easy to use json instead of QDataStream
2020-02-23 22:55:30 +00:00
Sergio Martins
950ef943b7 LayoutSaver: Save the top-levels screen number
So we can be smarter when restoring layouts with different screen
setups
2020-02-20 18:39:06 +00:00
Sergio Martins
21660d9fc5 Remove unused method 2020-02-20 18:19:41 +00:00
Sergio Martins
b0525ed1f1 LayoutSaver: Save the list of screens and their properties
So we can be smarter when restoring on different screen setups
2020-02-20 17:50:28 +00:00
Sergio Martins
e7661cca91 LayoutSaver: serialize/deserialize the screenSize and layoutSize too 2020-02-20 15:41:00 +00:00
Sergio Martins
2458b1c0f8 Add a way for the intermediate format to know it's version
It's a pain to pass the Layout or the version all the way
down the chain, so just add a static method
2020-02-20 14:56:51 +00:00
Sergio Martins
efdd8f9f81 LayoutSaver: Also store the screen and layout size
So when restoring we can restore relative to that
2020-02-20 14:14:06 +00:00
Sergio Martins
27e53ca68d Add missing reserve 2020-02-20 13:39:25 +00:00
Sergio Martins
63da84e343 LayoutSaver: Accept serialization formats with lower version 2020-02-20 13:36:10 +00:00
Sergio Martins
579b222418 Make AddingOption_StartHidden also work for tabs
You can now pass that option to DockWidget::addDockWidgetAsTab().
2020-02-19 16:36:50 +00:00
Sergio Martins
61303b14a7 Prophylaxis against a widget resizing itself while we're restoring
During a layout restore we should ignore a widget wanting to resize
itself by its initiative, as we don't have the anchors to push yet

Layouting is adjusted at the end of the restore.
2020-02-19 14:28:22 +00:00
Sergio Martins
7a2aa43b2d Fuzzer: Add SaveLayout and RestoreLayout operations too 2020-02-19 14:12:44 +00:00
Sergio Martins
cf0b8fd029 Fix build with Qt 5.9, SH_Widget_Animation_Duration is too new 2020-02-18 11:16:49 +00:00
Sergio Martins
a35e088988 Only allow to re-order tabs if we have more than 1
Otherwise it's confusing

Fixes: #27
2020-02-18 11:06:04 +00:00
Sergio Martins
232e96ee0e Fix crash when detaching tabs and the move animation is still enabled
Qt bug, but workaround here.
2020-02-18 10:36:16 +00:00
Sergio Martins
b43081851f example: Add an option to show a non-closable dock widget
Passing -n to the example will make the dock widget #0 non-closable
2020-02-17 21:20:43 +00:00
Sergio Martins
10621f3b4a Allow tabs to have a close button
Can be tested with kddockwidgets_example -c

Disabled by default, as I'm not 100% happy with it:
A dock widget with the "non closable" attribute will still show
the x button on the tab bar. Clicking on it won't do anything but
doesn't look so nice. Qt doesn't allow to have the close buttons only
in some tabs.

Fixes #26
2020-02-17 20:45:54 +00:00
Sergio Martins
723491d9b6 Fix app not quiting whent here's a floating NonClosable window
Since 5.14, after closing the main window, qApp sends a close
event to all top-levels, and only closes app if the event
is not ignored.
2020-02-15 18:06:21 +00:00
Sergio Martins
7d56860325 Don't allow to close NonClosable dock widgets via Alt+F4 2020-02-11 17:56:32 +00:00
Sergio Martins
d6997eaf7f Introduce main window affinity
By default a dock widget can dock into any main window.
With affinities, we can now have a dock widget "belong" to a main window
and only be able to dock into it (or into other floating dock widgets
with the same affinity).

See DockWidgetBase::setAffinity() and MainWindowBase::setAffinity().
2020-02-09 21:02:21 +00:00
Sergio Martins
59bb0d8e71 FloatingWindow now receives MainWindow as argument
Instead of a generic QWidget. Only works with main window so
let's just use the right type
2020-02-09 18:24:37 +00:00
Sergio Martins
37fe065bcf FloatingWindow: Receive MainWindow as the parent
Better for readability
2020-02-09 18:14:36 +00:00
Sergio Martins
aded290573 Don't save last position if the window was hidden already
Bug was happening when calling close() an closed dock widget, it
would then save the position of the invisible window. Next time it
was open it would appear in a bogus location. 0,0 in my case.
2020-02-06 15:01:58 +00:00
Sergio Martins
fca010ce2a Center floating dock widgets within their main window
In case they don't have any previous position.
This is better than showing them at 0,0
2020-02-06 14:30:01 +00:00
Sergio Martins
3771aa7a40 Fix toggleAction() emitting spurious toggle events
When showing a dockwidget it's inserted into a tab widget, which
will generate a hide event, which will toggle the action to off
even though the dock widget is opened for our purposes
2020-02-05 13:06:53 +00:00
Sergio Martins
1cac350547 Add convenience method DockWidget::isOpen() 2020-02-01 19:47:16 +00:00
Sergio Martins
4c15335339 Don't access MultiSplitter methods after its dtor ran
~MultiSplitter dtor eventually triggers Frame::setDropArea()
which dereferences the MultiSplitter. Instead, keep the connection
in a QMetaObject::Connection, so we can disconnect without needing
the MultiSplitter
2020-01-29 16:07:52 +00:00
Sergio Martins
6da1a65591 Fix build with gcc -Werror=type-limits 2020-01-29 15:27:11 +00:00
Sergio Martins
6338e6b03e Fix my Werror clang build 2020-01-29 15:17:01 +00:00
Sergio Martins
48ef73d6d3 cmake: Rename OPTION_ASAN_SUPPORT to OPTION_SANITIZER_SUPPORT
As it can work with any type of sanitizers
2020-01-29 15:04:51 +00:00
Sergio Martins
52e53c4fd9 fuzzer: Trim the failing test 2020-01-29 15:04:51 +00:00
Sergio Martins
81c180fbfd Debug++ 2020-01-27 22:12:28 +00:00
Sergio Martins
07f9e9a0df Fix fuzzer testcase failing/2.json
And renamed it to 17.json, as we already have an unrelated 2.json.

The removed check isn't needed, as we'll check again when we
finish restoring the placeholder. It was also a false-positive
warning sometimes because boundPositionForAnchor() would fail
since the "anchors following" wasnt initialized yet, as we do
a clearAnchorsFollowing while restoring the placeholder
2020-01-27 18:47:37 +00:00
Sergio Martins
6dd8a0cda1 Debug++ 2020-01-27 18:35:01 +00:00
Sergio Martins
25f3e83fc3 Debug++ 2020-01-27 18:30:13 +00:00
Sergio Martins
195c8121c0 fuzzer: Simplify names for 2.json 2020-01-27 15:17:30 +00:00
Sergio Martins
2b1518b1c2 Fuzzer: Add a new failing testcase 2020-01-27 15:10:55 +00:00
Sergio Martins
ca98d376b7 fuzzer: Move 14.json from failing to ok
It passes now
2020-01-24 20:05:43 +00:00
Sergio Martins
16b564a005 Add a test which is failing on macOS 2020-01-24 20:02:26 +00:00
Sergio Martins
c3483ba480 Fix ensureMinSize making the item smaller
this function should either make it bigger or leave it.
Never make it smaller.
Fixes fuzzer testcase 14
2020-01-24 18:13:19 +00:00
Sergio Martins
162bf0e828 Don't move anchors that are following others
Move the real ones instead. Fixes some bugs found with the fuzzer.
2020-01-24 16:19:42 +00:00
Sergio Martins
3bbeec06f8 DebugWindow: Allow to toggle a dock widget 2020-01-24 15:48:35 +00:00
Sergio Martins
2f3b7642ed Fix debug window crash 2020-01-24 12:04:45 +00:00
Sergio Martins
068eb7cc25 fuzzer: trim 15.json a bit
smaller and still repro
2020-01-24 11:52:51 +00:00
Sergio Martins
8c37c3a7ec fuzzer: print the name of the failing test 2020-01-24 11:38:21 +00:00
Sergio Martins
e2d47001db fuzzer: Move failing tests to a failing directory
So we can run the correct ones with *.json
2020-01-24 11:37:45 +00:00
Sergio Martins
8898ec82a4 Don't account for the anchor thickness of an anchor which is following
Fixes fuzzer testcase 10
2020-01-24 11:26:35 +00:00
Sergio Martins
9ca03b65f0 Improve comment
The function already explains what it does.
2020-01-24 10:12:15 +00:00
Sergio Martins
b80a795968 Improve comments on this function 2020-01-24 10:02:22 +00:00
Sergio Martins
8aaffa26c6 Fix build with c++11 2020-01-24 09:31:22 +00:00
Sergio Martins
da3917ae64 Replace assert with a warning and early return
A warning will still abort the unit-tests, so it's fine
2020-01-22 22:53:18 +00:00
Sergio Martins
0003b508d6 Bound interval when adding the widget
rectForDrop() doesn't know about the anchors bounds, so we need to
adjust the interval after. Fixes fuzzer testcase 16
2020-01-22 22:49:45 +00:00
Sergio Martins
d5162efa31 When calculating bounds start from the anchor we're following 2020-01-22 21:50:39 +00:00
Sergio Martins
a71361341c Don't warn when updateAnchorFollowing is being executed 2020-01-22 21:46:18 +00:00
Sergio Martins
08cbbd5178 Fix Anchor::cumulativeMinLength() not returning correct result
For complex situations with placeholders.
Now the way the number of anchors to take into account is easier.
It's simply numNonPlacholderItems -1.
2020-01-22 20:54:55 +00:00
Sergio Martins
94c02a1fe9 Fix case where the bounds wouldn't count with the actual anchor
Now the method is split in two and the preamble will always
add it
2020-01-22 18:05:13 +00:00
Sergio Martins
8a05980cd5 Minor refactoring 2020-01-22 17:40:50 +00:00
Sergio Martins
45b2fb63bc Fix frames with duplicate name in the debug output
when a tab is removed, update the frame's object name
Was making it very difficult to debug the fuzzer's output
2020-01-22 15:05:34 +00:00
Sergio Martins
4a6b73cae1 fuzzer: Decouple skipping last from no quitting
There's two separate options now, as it's useful to pause without
skipping the last test
2020-01-22 14:44:56 +00:00
Sergio Martins
d738fbd154 fuzzer: Fix description of skipped operation
Only generate the description after all other dockwidgets are placed
, as the state of the others will be in the last description too
2020-01-22 12:47:42 +00:00
Sergio Martins
7c10ab37f4 fuzzer: print description of skipped operation
involves making sure the description is generated
2020-01-22 11:54:09 +00:00
Sergio Martins
460d3f9ec1 fuzzer: Print the skipped operation 2020-01-22 11:48:04 +00:00
Sergio Martins
4c19ccade7 fuzzer: don't destroy the windows when we use -d
so we can debug the app
2020-01-22 11:36:49 +00:00
Sergio Martins
03cc8abd94 fuzzer: Add skipAndPause (-d) option
So we can pause before the last test and debug the app
2020-01-22 11:17:59 +00:00
Sergio Martins
8447cefc14 fuzzer: trim testcase 16
Now has the minimum to reproduce
2020-01-22 10:56:11 +00:00
Sergio Martins
141cbc55f4 add a failing testcase 2020-01-21 22:05:32 +00:00
Sergio Martins
873978a99e Add some failing testcases 2020-01-21 22:05:03 +00:00
Sergio Martins
b8279633ac fuzzer: add an -l option, so it loops until it crashes 2020-01-21 17:10:29 +00:00
Sergio Martins
cfa1cc47b6 Rephrase the warning about the insane layout 2020-01-13 12:50:45 +00:00
Sergio Martins
4ca140c0cc Fix unmatched beginBlockPropagateGeo()
Fixes an assert
2020-01-08 09:56:03 +00:00
Allen Winter
d505eeb0a5 LICENSE.GPL.txt - https: urls 2020-01-07 11:39:33 -05:00
Allen Winter
df692686e7 various - update copyright year 2020-01-07 11:36:56 -05:00
Sergio Martins
a25435fc2d Remove leftover duplicate close button
also fixes a weird hover effect when mouse is over the title bar
2020-01-07 15:53:59 +00:00
Sergio Martins
fbfbf2d801 Fix crash only reproducible in debug mode
don't dereference 'this' after it's deleted
2020-01-05 22:58:15 +00:00
Sergio Martins
63a75d7be9 Fix window decorations on Windows + Qt-5.9
For some reason our WC_NCCALCSIZE tricks aren't working and we need
to explicitly create the window. Works correctly since 5.10

Fixes #22
2020-01-05 18:37:53 +00:00
Sergio Martins
2ff2b78f5a Fix unit-tests on Windows with Qt 5.9
Our RAII class that ensures that each test leaves 0 top-levels behind
was being initialized with somthing bigger than 0 since the
rectForDrop() test wasn't doing proper cleanup.

Not sure why only repro with 5.9, but it's correct now.
2020-01-04 22:24:11 +00:00
Sergio Martins
6be2490c08 tests: When cleanup fails print the top-levels that we compared to
Since qApp->topLevels() contains buttons which will be deleted later
2020-01-04 21:15:39 +00:00
Sergio Martins
f37ecc0765 tests: Remove test with hardcoded values
It's flaky as the min sizes are varying with Qt versions. Also what
it tests is already tested in previous tests and elsewhere.
2020-01-04 18:33:43 +00:00
Sergio Martins
3b20f99092 tests: Add some missing window cleanup checks 2020-01-04 18:27:20 +00:00
Sergio Martins
9f5c7b5e6a tests: Use fusion style on all platforms
To make the tests more robust, as some widgets min size will
vary
2020-01-04 18:19:26 +00:00
Sergio Martins
4b0b11acee tests: Use QElapsedTimer instead of QTime::start()
The later is deprecated
2020-01-04 18:11:34 +00:00
Sergio Martins
9b8709724c Fix namespaced build 2020-01-03 11:29:29 +00:00
Sergio Martins
e5df8db045 tests: Add some tests for double close 2019-12-29 18:29:27 +00:00
Sergio Martins
9c6fd9ba0e Add support for lazy resize
In this mode the resize is only done when mouse is released.

Fixes #21
2019-12-29 17:55:19 +00:00
Sergio Martins
927510dfff Support re-ordering tabs with mouse
Just set KDDockWidgets::Config::Flag_AllowReorderTabs before creating
the dock widgets.

Fixes #20
2019-12-26 15:19:27 +00:00
Sergio Martins
a29a33a46d Abstract starting a drag
So draggables can give their opinion on when to start a drag.
So we can support movable tab-bars.
2019-12-24 10:18:20 +00:00
Sergio Martins
53238e4f29 Add a comment about isPositionDraggable 2019-12-24 10:02:59 +00:00
Sergio Martins
6be6aee56a Fix dock widgets not filling their complete available space
To repro:
 - stack 3 dock widgets vertically
 - close the top one
 - close the top one
 The remaining one should now fill the whole window but it wasn't.

Culprit was the code that makes sure that when a dock is closed that
the adjacent ones get to share their space
2019-12-23 14:28:40 +00:00
Sergio Martins
7452520662 Add DockRegistry::dockWidgetForGuest()
So you can find out the dock widget which hosts a widget
2019-12-20 10:57:05 +00:00
Sergio Martins
cdc70ec34b Support MainWindow being embedded into a widget on Linux/macOS too
Dragging indicators weren't being shown.

Suprisingly we already had a test for this: tst_embeddedMainWindow
But it passed on offscreen, just failed with xcb, which we don't
usually run
2019-12-18 18:00:11 +00:00
Sergio Martins
4a2a718f78 Add .cmake to .gitignore 2019-12-18 17:51:08 +00:00
Sergio Martins
617bfa4801 Add DockRegistry::topLevels() 2019-12-18 17:50:35 +00:00
Allen Winter
71160b38c6 various - use secure urls 2019-12-10 15:10:30 -05:00
Allen Winter
41d07ef8c6 examples - include guards. use qrand() 2019-12-10 14:48:10 -05:00
Allen Winter
1723f001c9 src/private/quick/QmlTypes.h - add include guard 2019-12-10 14:47:32 -05:00
Allen Winter
f2123ffb41 src/private/quick/qml/*.qml - add license+copyright header 2019-12-10 14:46:30 -05:00
Allen Winter
572a5cc6b5 .krazy - add more EXCLUDEs and SKIPs 2019-12-10 14:45:43 -05:00
Allen Winter
04d91863c6 README.md - add github url 2019-12-10 12:03:49 -05:00
Sergio Martins
89dbd20e1d Honour the widget's initial size when adding it to the layout
In MultiSplitterLayout::lengthForDrop(), we use frame's size
as the suggested size to use when add a dock widget. So give
the frame the size of the dock widget.

User can now do:
    dockwidget->resize(foo);
    mainwindow->addWidget(); and it will respect the size
2019-12-08 17:24:30 +00:00
Sergio Martins
7bb0a29aea Fix resizing height not preserving correct relative sizes
Because the percentage code was always using widths. It should
use height when the anchor orientation is horizontal.
2019-12-08 16:39:43 +00:00
Sergio Martins
af1a26ef59 Debug++ 2019-12-08 16:37:53 +00:00
Sergio Martins
2d0b7f1bee Add Frame::isInMainWindowChanged() signal 2019-12-03 19:53:50 +00:00
Sergio Martins
f351e5ed9c README: Mention that Qt >= 5.9 is required
Fixes: #17
2019-12-01 13:47:30 +00:00
Sergio Martins
945dbc1f27 Fix build with namespaced Qt
Fixes #18
2019-11-27 16:17:48 +00:00
147 changed files with 6717 additions and 638 deletions

2
.gitignore vendored
View File

@@ -40,3 +40,5 @@ kddockwidgets_basic_quick
/src/KDDockWidgetsConfigVersion.cmake
build-*
/examples/dockwidgets/kddockwidgets_example
.cmake
CTestTestfile.cmake

8
.krazy
View File

@@ -1,4 +1,4 @@
CHECKSETS qt5,c++,foss
CHECKSETS qt5,c++
#KDAB-specific checks
EXTRA kdabcopyright
@@ -7,10 +7,12 @@ EXTRA kdabcopyright
#EXTRA defines,null
#exclude checks now being done by clazy or clang-tools
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects
EXCLUDE strings,explicit,normalize,passbyvalue,operators,nullstrcompare,nullstrassign,doublequote_chars,qobject,sigsandslots,staticobjects,dpointer,inline
#exclude more checks
EXCLUDE style
EXCLUDE style
#skip CMake files
SKIP /KDDockWidgetsConfig.cmake.in
#skip the borrowed code in the cmake subdir
SKIP /cmake/Qt5Portability.cmake|/cmake/ECM/

View File

@@ -1,16 +1,21 @@
cmake_minimum_required(VERSION 3.3)
cmake_minimum_required(VERSION 3.7)
if (${CMAKE_VERSION} VERSION_LESS "3.12.0")
project(KDDockWidgets VERSION 0.1 LANGUAGES CXX)
else()
project(KDDockWidgets
VERSION 0.1
DESCRIPTION "An advanced docking system for Qt"
HOMEPAGE_URL "https://github.com/KDAB/KDDockWidgets"
LANGUAGES CXX)
endif()
project(KDDockWidgets
VERSION 0.1
DESCRIPTION "An advanced docking system for Qt"
HOMEPAGE_URL "https://github.com/KDAB/KDDockWidgets"
LANGUAGES CXX)
cmake_policy(SET CMP0020 NEW)
cmake_policy(SET CMP0042 NEW)
option(OPTION_DEVELOPER_MODE "Developer Mode" OFF)
option(OPTION_ASAN_SUPPORT "Activate support for using ASAN" OFF)
option(OPTION_SANITIZER_SUPPORT "Activate support for using sanitizers" OFF)
# option(OPTION_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
find_package(Qt5Widgets)
@@ -22,7 +27,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if (OPTION_ASAN_SUPPORT)
if (OPTION_SANITIZER_SUPPORT)
include(ECMEnableSanitizers)
endif()
@@ -41,9 +46,11 @@ else()
endif()
add_subdirectory(src)
add_subdirectory(examples/dockwidgets)
if (OPTION_DEVELOPER_MODE)
if (NOT OPTION_QTQUICK)
enable_testing()
add_subdirectory(tests)
endif()

10
Changelog Normal file
View File

@@ -0,0 +1,10 @@
* v1.0 (November 4th, 2019)
- Initial Release
* v1.1 (, 2020)
Features:
- Allow tab re-ordering with mouse, via KDDockWidgets::Config::Flag_AllowReorderTabs.
- Allow tabs to have a close button, via KDDockWidgets::Config::Flag_TabsHaveCloseButton.
- Add support for lazy resize, via KDDockWidgets::Config::Flag_LazyResize. Resize is only done when mouse is released.
Fixes:
- Fix dock widgets not filling their complete available space

View File

@@ -1,4 +1,4 @@
The KDDockWidgets software is Copyright (C) 2018-2019 Klaralvdalens Datakonsult AB.
The KDDockWidgets software is Copyright (C) 2018-2020 Klaralvdalens Datakonsult AB.
You may use, distribute and copy the KDDockWidgets software under the terms of
the GNU General Public License version 2 or under the terms of GNU General
@@ -354,7 +354,7 @@ Public License instead of this License.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -998,7 +998,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -1017,13 +1017,13 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/philosophy/why-not-lgpl.html>.
-------------------------------------------------------------------------

View File

@@ -60,9 +60,14 @@ $ ./kddockwidgets_example
```
Supported Qt versions
======================
KDDockWidgets requires Qt >= 5.9
Licensing
=========
KDDockWidgets is (C) 2018-2019, Klarälvdalens Datakonsult AB, and is available
KDDockWidgets is (C) 2018-2020, Klarälvdalens Datakonsult AB, and is available
under the terms of the GPLv2 (see LICENSE.GPL.txt).
Contact KDAB at <info@kdab.com> if you need different licensing options.
@@ -75,6 +80,9 @@ contributions will require a signed Contributor License Agreement
Contact info@kdab.com for more information.
Please submit your contributions or issue reports from our GitHub space at
https://github.com/KDAB/KDDockWidgets
About KDAB
==========
KDDockWidgets is supported and maintained by Klarälvdalens Datakonsult AB (KDAB).
@@ -88,4 +96,4 @@ We continue to help develop parts of Qt and are one of the major contributors
to the Qt Project. We can give advanced or standard trainings anywhere
around the globe on Qt as well as C++, OpenGL, 3D and more.
Please visit http://www.kdab.com to meet the people who write code like this.
Please visit https://www.kdab.com to meet the people who write code like this.

View File

@@ -5,18 +5,21 @@ project(kddockwidgets_example)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific version for instance.
find_package(KDDockWidgets REQUIRED)
qt5_add_resources(RESOURCES_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
if (NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
add_executable(kddockwidgets_example
main.cpp
MyFrameworkWidgetFactory.cpp
MyMainWindow.cpp
MyWidget.cpp
${RESOURCES_SRC}
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_example

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <kddockwidgets/FrameworkWidgetFactory.h>
#include <kddockwidgets/private/TitleBar_p.h>
#include <QPainter>

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -22,7 +22,6 @@
#include "MyWidget.h"
#include <kddockwidgets/LayoutSaver.h>
#include <kddockwidgets/DockWidget.h>
#include <QMenu>
#include <QMenuBar>
@@ -36,7 +35,7 @@
static MyWidget *newMyWidget()
{
const int randomNumber = rand() % 100 + 1;
const int randomNumber = qrand() % 100 + 1;
if (randomNumber < 50) {
if (randomNumber < 33) {
@@ -49,12 +48,17 @@ static MyWidget *newMyWidget()
}
}
MyMainWindow::MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *parent)
: MainWindow(QStringLiteral("MyMainWindow"), options, parent)
MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
const QString &affinityName, QWidget *parent)
: MainWindow(uniqueName, options, parent)
, m_dockWidget0IsNonClosable(dockWidget0IsNonClosable)
, m_dockWidget9IsNonDockable(nonDockableDockWidget9)
, m_restoreIsRelative(restoreIsRelative)
{
// qApp->installEventFilter(this);
srand(time(nullptr));
qsrand(time(nullptr));
auto menubar = menuBar();
auto fileMenu = new QMenu(QStringLiteral("File"));
@@ -78,58 +82,96 @@ MyMainWindow::MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *pa
auto saveLayoutAction = fileMenu->addAction(QStringLiteral("Save Layout"));
connect(saveLayoutAction, &QAction::triggered, this, [] {
KDDockWidgets::LayoutSaver saver;
const bool result = saver.saveToDisk();
const bool result = saver.saveToFile(QStringLiteral("mylayout.json"));
qDebug() << "Saving layout to disk. Result=" << result;
});
auto restoreLayoutAction = fileMenu->addAction(QStringLiteral("Restore Layout"));
connect(restoreLayoutAction, &QAction::triggered, this, [] {
KDDockWidgets::LayoutSaver saver;
saver.restoreFromDisk();
connect(restoreLayoutAction, &QAction::triggered, this, [this] {
KDDockWidgets::RestoreOptions options = KDDockWidgets::RestoreOption_None;
if (m_restoreIsRelative)
options |= KDDockWidgets::RestoreOption_RelativeToMainWindow;
KDDockWidgets::LayoutSaver saver(options);
saver.restoreFromFile(QStringLiteral("mylayout.json"));
});
auto closeAllAction = fileMenu->addAction(QStringLiteral("Close all"));
connect(closeAllAction, &QAction::triggered, this, [this] {
for (auto dw : m_dockwidgets)
dw->close();
});
auto quitAction = fileMenu->addAction(QStringLiteral("Quit"));
connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
setAffinityName(affinityName);
createDockWidgets();
}
void MyMainWindow::createDockWidgets()
{
KDDockWidgets::DockWidget::List dockwidgets;
Q_ASSERT(m_dockwidgets.isEmpty());
const int numDockWidgets = m_dockWidget9IsNonDockable ? 10 : 9;
// Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget instances)
for (int i = 0; i < 9; i++)
dockwidgets << newDockWidget();
for (int i = 0; i < numDockWidgets; i++)
m_dockwidgets << newDockWidget();
// MainWindow::addDockWidget() attaches a dock widget to the main window:
addDockWidget(dockwidgets[0], KDDockWidgets::Location_OnTop);
addDockWidget(m_dockwidgets[0], KDDockWidgets::Location_OnTop);
// Here, for finer granularity we specify right of dockwidgets[0]:
addDockWidget(dockwidgets[1], KDDockWidgets::Location_OnRight, dockwidgets[0]);
addDockWidget(m_dockwidgets[1], KDDockWidgets::Location_OnRight, m_dockwidgets[0]);
addDockWidget(dockwidgets[2], KDDockWidgets::Location_OnLeft);
addDockWidget(dockwidgets[3], KDDockWidgets::Location_OnBottom);
addDockWidget(dockwidgets[4], KDDockWidgets::Location_OnBottom);
addDockWidget(m_dockwidgets[2], KDDockWidgets::Location_OnLeft);
addDockWidget(m_dockwidgets[3], KDDockWidgets::Location_OnBottom);
addDockWidget(m_dockwidgets[4], KDDockWidgets::Location_OnBottom);
// Tab two dock widgets toghether
dockwidgets[3]->addDockWidgetAsTab(dockwidgets[5]);
m_dockwidgets[3]->addDockWidgetAsTab(m_dockwidgets[5]);
// 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
// and we tab 7 with it.
dockwidgets[6]->addDockWidgetAsTab(dockwidgets[7]);
m_dockwidgets[6]->addDockWidgetAsTab(m_dockwidgets[7]);
// Floating windows also support nesting, here we add 8 to the bottom of the group
dockwidgets[6]->addDockWidgetToContainingWindow(dockwidgets[8], KDDockWidgets::Location_OnBottom);
m_dockwidgets[6]->addDockWidgetToContainingWindow(m_dockwidgets[8], KDDockWidgets::Location_OnBottom);
auto floatingWindow = m_dockwidgets[6]->window();
floatingWindow->move(100, 100);
}
KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget()
{
static int count = 0;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count));
// Passing options is optional, we just want to illustrate Option_NotClosable here
KDDockWidgets::DockWidget::Options options = KDDockWidgets::DockWidget::Option_None;
if (count == 0 && m_dockWidget0IsNonClosable)
options |= KDDockWidgets::DockWidget::Option_NotClosable;
if (count == 9 && m_dockWidget9IsNonDockable)
options |= KDDockWidgets::DockWidget::Option_NotDockable;
auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options);
dock->setAffinityName(affinityName()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets
if (count == 1)
dock->setIcon(QIcon::fromTheme(QStringLiteral("mail-message")));
auto myWidget = newMyWidget();
dock->setWidget(myWidget);
dock->setTitle(QStringLiteral("DockWidget #%1").arg(count));
if (dock->options() & KDDockWidgets::DockWidget::Option_NotDockable) {
dock->setTitle(QStringLiteral("DockWidget #%1 (%2)").arg(count).arg("non dockable"));
} else {
dock->setTitle(QStringLiteral("DockWidget #%1").arg(count));
}
dock->resize(600, 600);
dock->show();
m_toggleMenu->addAction(dock->toggleAction());

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,26 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <kddockwidgets/DockWidget.h>
#include <kddockwidgets/MainWindow.h>
class MyMainWindow : public KDDockWidgets::MainWindow
{
Q_OBJECT
public:
explicit MyMainWindow(KDDockWidgets::MainWindowOptions options, QWidget *parent = nullptr);
explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative,
const QString &affinityName = {}, // Usually not needed. Just here to show the feature.
QWidget *parent = nullptr);
private:
void createDockWidgets();
KDDockWidgets::DockWidgetBase* newDockWidget();
QMenu *m_toggleMenu = nullptr;
const bool m_dockWidget0IsNonClosable;
const bool m_dockWidget9IsNonDockable;
const bool m_restoreIsRelative;
KDDockWidgets::DockWidget::List m_dockwidgets;
};

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -21,9 +21,13 @@
#ifndef EXAMPLEDOCKABLEWIDGET_H
#define EXAMPLEDOCKABLEWIDGET_H
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
class QPainter;
QT_END_NAMESPACE
class MyWidget : public QWidget
{

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -49,11 +49,41 @@ int main(int argc, char **argv)
QCommandLineOption customStyle("p", QCoreApplication::translate("main", "Shows how to style framework internals via FrameworkWidgetFactory"));
parser.addOption(customStyle);
QCommandLineOption reorderTabsOption("r", QCoreApplication::translate("main", "Support re-ordering tabs with mouse"));
parser.addOption(reorderTabsOption);
QCommandLineOption noTitleBars("t", QCoreApplication::translate("main", "Never show titlebars"));
parser.addOption(noTitleBars);
QCommandLineOption lazyResizeOption("l", QCoreApplication::translate("main", "Use lazy resize"));
parser.addOption(lazyResizeOption);
QCommandLineOption multipleMainWindows("m", QCoreApplication::translate("main", "Shows two multiple main windows"));
parser.addOption(multipleMainWindows);
QCommandLineOption incompatibleMainWindows("i", QCoreApplication::translate("main", "Only usable with -m. Make the two main windows incompatible with each other. (Illustrates (MainWindowBase::setAffinityName))"));
parser.addOption(incompatibleMainWindows);
QCommandLineOption tabsHaveCloseButton("c", QCoreApplication::translate("main", "Tabs have a close button"));
parser.addOption(tabsHaveCloseButton);
QCommandLineOption nonClosableDockWidget("n", QCoreApplication::translate("main", "DockWidget #0 will be non-closable"));
parser.addOption(nonClosableDockWidget);
QCommandLineOption relativeRestore("s", QCoreApplication::translate("main", "Don't restore main window geometry, restore dock widgets in relative sizes"));
parser.addOption(relativeRestore);
QCommandLineOption doubleClickMaximize("x", QCoreApplication::translate("main", "Double clicking a title bar will maximize a floating window"));
parser.addOption(doubleClickMaximize);
QCommandLineOption nonDockable("d", QCoreApplication::translate("main", "DockWidget #9 will be non-dockable"));
parser.addOption(nonDockable);
QCommandLineOption maximizeButton("b", QCoreApplication::translate("main", "DockWidgets have maximize/restore buttons instead of float/dock button"));
parser.addOption(maximizeButton);
#if defined(DOCKS_DEVELOPER_MODE)
QCommandLineOption noCentralFrame("c", QCoreApplication::translate("main", "No central frame"));
QCommandLineOption noCentralFrame("f", QCoreApplication::translate("main", "No central frame"));
parser.addOption(noCentralFrame);
#endif
@@ -72,12 +102,58 @@ int main(int argc, char **argv)
: MainWindowOption_HasCentralFrame;
#endif
auto flags = KDDockWidgets::Config::self().flags();
if (parser.isSet(noTitleBars))
KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flags() | KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | KDDockWidgets::Config::Flag_AlwaysShowTabs);
flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible | KDDockWidgets::Config::Flag_AlwaysShowTabs;
MyMainWindow mainWindow(options);
if (parser.isSet(reorderTabsOption))
flags |= KDDockWidgets::Config::Flag_AllowReorderTabs;
if (parser.isSet(maximizeButton))
flags |= KDDockWidgets::Config::Flag_TitleBarHasMaximizeButton;
if (parser.isSet(lazyResizeOption))
flags |= KDDockWidgets::Config::Flag_LazyResize;
if (parser.isSet(tabsHaveCloseButton))
flags |= KDDockWidgets::Config::Flag_TabsHaveCloseButton;
if (parser.isSet(doubleClickMaximize))
flags |= KDDockWidgets::Config::Flag_DoubleClickMaximizes;
if (parser.isSet(incompatibleMainWindows) && !parser.isSet(multipleMainWindows)) {
qWarning() << "Error: Argument -i requires -m";
return 1;
}
KDDockWidgets::Config::self().setFlags(flags);
const bool nonClosableDockWidget0 = parser.isSet(nonClosableDockWidget);
const bool restoreIsRelative = parser.isSet(relativeRestore);
const bool nonDockableDockWidget9 = parser.isSet(nonDockable);
MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative);
mainWindow.setWindowTitle("Main Window 1");
mainWindow.resize(1200, 1200);
mainWindow.show();
if (parser.isSet(multipleMainWindows)) {
// By default a dock widget can dock into any main window.
// By setting an affinity name we can prevent that. Dock widgets of different affinities are incompatible.
const QString affinity = parser.isSet(incompatibleMainWindows) ? QStringLiteral("affinity1")
: QString();
auto mainWindow2 = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options,
nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative, affinity);
if (affinity.isEmpty())
mainWindow2->setWindowTitle("Main Window 2");
else
mainWindow2->setWindowTitle("Main Window 2 (different affinity)");
mainWindow2->resize(1200, 1200);
mainWindow2->show();
}
return app.exec();
}

View File

@@ -122,12 +122,14 @@ endif()
qt5_add_resources(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
add_library(kddockwidgets SHARED ${DOCKSLIBS_SRCS} ${DOCKS_INSTALLABLE_INCLUDES} ${RESOURCES} ${RESOURCES_QUICK})
add_library(KDAB::kddockwidgets ALIAS kddockwidgets)
target_include_directories(kddockwidgets
PUBLIC
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:include/kddockwidgets>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/fwd_headers>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/private

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,9 @@
#include "docks_export.h"
QT_BEGIN_NAMESPACE
class QQmlEngine;
QT_END_NAMESPACE
namespace KDDockWidgets
{
@@ -62,7 +64,12 @@ public:
Flag_NativeTitleBar = 1, ///> Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise. This is mutually exclusive with Flag_AeroSnap
Flag_AeroSnapWithClientDecos = 2, ///> Enables AeroSnap even if we're not using the native title bar. Only supported on Windows 10.
Flag_HideTitleBarWhenTabsVisible = 8, ///> Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one,
Flag_AllowReorderTabs = 32, /// Allows user to re-order tabs by dragging them
Flag_LazyResize = 32, /// The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
Flag_TabsHaveCloseButton = 64, /// Tabs will have a close button. Equivalent to QTabWidget::setTabsClosable(true).
Flag_DoubleClickMaximizes = 128, /// Double clicking the titlebar will maximize a floating window instead of re-docking it
Flag_TitleBarHasMaximizeButton = 256, /// The title bar will have a maximize/restore button when floating. This is mutually-exclusive with the floating button (since many apps behave that way).
Flag_Default = Flag_AeroSnapWithClientDecos ///> The defaults
};
Q_DECLARE_FLAGS(Flags, Flag)

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,9 @@
#include "DockWidgetBase.h"
QT_BEGIN_NAMESPACE
class QCloseEvent;
QT_END_NAMESPACE
namespace KDDockWidgets {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -57,16 +57,27 @@ public:
, q(qq)
, options(options_)
, toggleAction(new QAction(q))
, floatAction(new QAction(q))
{
q->connect(q, &DockWidgetBase::shown, q, [this] { onDockWidgetShown(); } );
q->connect(q, &DockWidgetBase::hidden, q, [this] { onDockWidgetHidden(); } );
q->connect(toggleAction, &QAction::toggled, q, [this] (bool enabled) {
if (!m_updatingToggleAction) // guard against recursiveness
if (!m_updatingToggleAction) { // guard against recursiveness
toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock widget is inserted into a tab widget it might get hide events, ignore those. The Dock Widget is open.
toggle(enabled);
toggleAction->blockSignals(false);
}
});
q->connect(floatAction, &QAction::toggled, q, [this] (bool enabled) {
if (!m_updatingFloatAction) { // guard against recursiveness
q->setFloating(enabled);
}
});
toggleAction->setCheckable(true);
floatAction->setCheckable(true);
}
void init()
@@ -74,10 +85,13 @@ public:
updateTitle();
}
QPoint defaultCenterPosForFloating();
void updateTitle();
void updateIcon();
void toggle(bool enabled);
void updateToggleAction();
void updateFloatAction();
void onDockWidgetShown();
void onDockWidgetHidden();
TabWidget *parentTabWidget() const;
@@ -94,14 +108,17 @@ public:
void saveTabIndex();
const QString name;
QString affinityName;
QString title;
QIcon icon;
QWidget *widget = nullptr;
DockWidgetBase *const q;
const DockWidgetBase::Options options;
DockWidgetBase::Options options;
QAction *const toggleAction;
QAction *const floatAction;
LastPosition m_lastPosition;
bool m_updatingToggleAction = false;
bool m_updatingFloatAction = false;
bool m_isForceClosing = false;
};
@@ -125,7 +142,7 @@ DockWidgetBase::~DockWidgetBase()
delete d;
}
void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, AddingOption addingOption)
{
qCDebug(addwidget) << Q_FUNC_INFO << other;
if (other == this) {
@@ -138,6 +155,18 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
return;
}
if (other->affinityName() != affinityName()) {
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
<< other->affinityName() << affinityName();
return;
}
if ((other->options() & DockWidgetBase::Option_NotDockable) ||
(options() & DockWidgetBase::Option_NotDockable)) {
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other;
return;
}
Frame *frame = this->frame();
if (frame) {
@@ -155,7 +184,7 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other)
Q_ASSERT(frame);
other->setParent(nullptr);
frame->addWidget(other);
frame->addWidget(other, addingOption);
}
void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Location location, DockWidgetBase *relativeTo)
@@ -166,6 +195,18 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Loca
return;
}
if (other->affinityName() != affinityName()) {
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
<< other->affinityName() << affinityName();
return;
}
if ((other->options() & DockWidgetBase::Option_NotDockable) ||
(options() & DockWidgetBase::Option_NotDockable)) {
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other;
return;
}
if (isWindow())
morphIntoFloatingWindow();
@@ -239,6 +280,11 @@ QAction *DockWidgetBase::toggleAction() const
return d->toggleAction;
}
QAction *DockWidgetBase::floatAction() const
{
return d->floatAction;
}
QString DockWidgetBase::uniqueName() const
{
return d->name;
@@ -263,6 +309,21 @@ DockWidgetBase::Options DockWidgetBase::options() const
return d->options;
}
void DockWidgetBase::setOptions(Options options)
{
if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) {
qWarning() << Q_FUNC_INFO << "Option_NotDockable not allowed to change. Pass via ctor only.";
return;
}
if (options != d->options) {
d->options = options;
Q_EMIT optionsChanged(options);
if (auto tb = titleBar())
tb->updateCloseButton();
}
}
bool DockWidgetBase::isTabbed() const
{
if (TabWidget* tabWidget = d->parentTabWidget()) {
@@ -315,6 +376,55 @@ TitleBar *DockWidgetBase::titleBar() const
return nullptr;
}
bool DockWidgetBase::isOpen() const
{
return d->toggleAction->isChecked();
}
QString DockWidgetBase::affinityName() const
{
return d->affinityName;
}
void DockWidgetBase::show()
{
if (isWindow() && (lastPosition()->m_wasFloating || !lastPosition()->isValid())) {
// Create the FloatingWindow already, instead of waiting for the show event.
// This reduces flickering on some platforms
morphIntoFloatingWindow();
} else {
QWidget::show();
}
}
void DockWidgetBase::raise()
{
if (!isOpen())
return;
setAsCurrentTab();
if (auto fw = qobject_cast<FloatingWindow*>(window())) {
fw->raise();
fw->activateWindow();
}
}
void DockWidgetBase::setAffinityName(const QString &name)
{
if (d->affinityName == name)
return;
if (!d->affinityName.isEmpty()) {
qWarning() << Q_FUNC_INFO
<< "Affinity is already set, refusing to change."
<< "Submit a feature request with a good justification.";
return;
}
d->affinityName = name;
}
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
{
qCDebug(creation) << "DockWidget::morphIntoFloatingWindow() this=" << this
@@ -325,8 +435,12 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
if (isWindow()) {
QRect geo = lastPosition()->lastFloatingGeometry();
if (geo.isNull())
if (geo.isNull()) {
geo = geometry();
const QPoint center = d->defaultCenterPosForFloating();
if (!center.isNull())
geo.moveCenter(center);
}
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(this);
@@ -374,6 +488,17 @@ LastPosition *DockWidgetBase::lastPosition() const
return &d->m_lastPosition;
}
QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
{
MainWindowBase::List mainWindows = DockRegistry::self()->mainwindows();
// We don't care about multiple mainwindows yet. Or, let's just say that the first one is more main than the others
MainWindowBase *mw = mainWindows.isEmpty() ? nullptr : mainWindows.constFirst();
if (!mw || !q->isFloating())
return {};
return mw->geometry().center();
}
void DockWidgetBase::Private::updateTitle()
{
if (q->isFloating())
@@ -408,15 +533,32 @@ void DockWidgetBase::Private::updateToggleAction()
}
}
void DockWidgetBase::Private::updateFloatAction()
{
QScopedValueRollback<bool> recursionGuard(m_updatingFloatAction, true); // Guard against recursiveness
if (q->isFloating()) {
floatAction->setEnabled(m_lastPosition.isValid());
floatAction->setChecked(true);
floatAction->setToolTip(tr("Dock"));
} else {
floatAction->setEnabled(true);
floatAction->setChecked(false);
floatAction->setToolTip(tr("Detach"));
}
}
void DockWidgetBase::Private::onDockWidgetShown()
{
updateToggleAction();
updateFloatAction();
qCDebug(hiding) << Q_FUNC_INFO << "parent=" << q->parentWidget();
}
void DockWidgetBase::Private::onDockWidgetHidden()
{
updateToggleAction();
updateFloatAction();
qCDebug(hiding) << Q_FUNC_INFO << "parent=" << q->parentWidget();
}
@@ -430,7 +572,8 @@ TabWidget *DockWidgetBase::Private::parentTabWidget() const
void DockWidgetBase::Private::close()
{
if (!m_isForceClosing && q->isFloating()) { // only user-closing is interesting to save the geometry
if (!m_isForceClosing && q->isFloating() && q->isVisible()) { // only user-closing is interesting to save the geometry
// We check for isVisible so we don't save geometry if you call close() on an already closed dock widget
m_lastPosition.setLastFloatingGeometry(q->window()->geometry());
}
@@ -508,6 +651,7 @@ void DockWidgetBase::onParentChanged()
{
Q_EMIT parentChanged();
d->updateToggleAction();
d->updateFloatAction();
}
void DockWidgetBase::onShown(bool spontaneous)
@@ -561,6 +705,13 @@ DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &
if (QWidget *w = dw->widget())
w->setVisible(true);
dw->setProperty("kddockwidget_was_restored", true);
if (dw->affinityName() != saved->affinityName) {
qWarning() << Q_FUNC_INFO << "Affinity name changed from" << dw->affinityName()
<< "; to" << saved->affinityName;
dw->d->affinityName = saved->affinityName;
}
} else {
qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << saved->uniqueName;
}
@@ -570,5 +721,8 @@ DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &
LayoutSaver::DockWidget::Ptr DockWidgetBase::serialize() const
{
return LayoutSaver::DockWidget::dockWidgetForName(uniqueName());
auto ptr = LayoutSaver::DockWidget::dockWidgetForName(uniqueName());
ptr->affinityName = affinityName();
return ptr;
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -36,7 +36,9 @@
#include <QVector>
#include <QWidget>
QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE
namespace KDDockWidgets {
@@ -49,6 +51,7 @@ class DockRegistry;
class LayoutSaver;
class TabWidget;
class TitleBar;
class MainWindowBase;
/**
* @brief The DockWidget base-class. DockWidget and DockWidgetBase are only
@@ -66,7 +69,8 @@ public:
///@brief DockWidget options to pass at construction time
enum Option {
Option_None = 0, ///< No option, the default
Option_NotClosable = 1 /// The DockWidget can't be closed on the [x], only programatically
Option_NotClosable = 1, /// The DockWidget can't be closed on the [x], only programatically
Option_NotDockable = 2 ///< The DockWidget can't be docked, it's always floating
};
Q_DECLARE_FLAGS(Options, Option)
@@ -97,9 +101,12 @@ public:
/**
* @brief docks @p other widget into this one. Tabs will be shown if not already.
* @param other The other dock widget to dock into this one.
* @param addingOption Allows to specify an AddingOption. Which is useful to add the dock widget
* as hidden, recording only a placeholder in the tab. So it's restored to tabbed when eventually
* shown.
* @sa MainWindow::addDockWidget(), DockWidget::addDockWidgetToContainingWindow()
*/
void addDockWidgetAsTab(DockWidgetBase *other);
void addDockWidgetAsTab(DockWidgetBase *other, AddingOption addingOption = AddingOption_None);
/**
* @brief docks @p other widget into the window that contains this one.
@@ -147,6 +154,12 @@ public:
*/
QAction *toggleAction() const;
/**
* @brief Returns the QAction that allows to dock/undock the dock widget
* Useful to put in menus.
*/
QAction *floatAction() const;
/**
* @brief the dock widget's unique name.
* @internal
@@ -168,11 +181,20 @@ public:
void setTitle(const QString &title);
/**
* @brief Returns the dock widget's options which control behaviour
* These options were passed at construction time and are immutable.
* @brief Returns the dock widget's options which control behaviour.
* @sa setOptions(), optionsChanged()
*/
Options options() const;
/**
* @brief Setter for the options.
* Only Option_NotClosable is allowed to change after construction. For the other options use
* the constructor only.
*
* @sa options(), optionsChanged()
*/
void setOptions(Options);
/**
* @brief returns if this dock widget is tabbed into another
*
@@ -221,6 +243,48 @@ public:
*/
TitleBar *titleBar() const;
/**
* @brief Returns whether this dock widget is open.
* Equivalent to calling toggleAction().isChecked() or isVisible()
*/
bool isOpen() const;
/**
* @brief Sets the affinity name. Dock widgets can only dock into dock widgets of the same affinity.
*
* By default the affinity is empty and a dock widget can dock into any main window and into any
* floating window. Usually you won't ever need to call
* this function, unless you have requirements where certain dock widgets can only dock into
* certain other dock widgets and main windows. @sa MainWindowBase::setAffinityName().
*
* Note: Call this function right after creating your dock widget, before adding to a main window and
* before restoring any layout.
*
* Note: Currently you can only call this function once, to keep the code simple and avoid
* edge cases. This will only be changed if a good use case comes up that requires changing
* affinities multiple times.
*
* @p name The affinity name.
*/
void setAffinityName(const QString &name);
/**
* @brief Returns the affinity name. Empty by default.
*/
QString affinityName() const;
/// @brief Equivalent to QWidget::show(), but it's optimized to reduce flickering on some platforms
void show();
/// @brief Brings the dock widget to the front.
///
/// This means:
/// - If the dock widget is tabbed with other dock widgets but its tab is not current, it's made current.
/// - If the dock widget is floating, QWindow::raise() is called.
///
/// This only applies if the dock widget is already open. If closed, does nothing.
void raise();
Q_SIGNALS:
///@brief signal emitted when the parent changed
void parentChanged();
@@ -240,6 +304,10 @@ Q_SIGNALS:
///@brief emitted when the hosted widget changed
void widgetChanged(QWidget*);
///@brief emitted when the options change
///@sa setOptions(), options()
void optionsChanged(Options);
protected:
void onParentChanged();
void onShown(bool spontaneous);

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -81,12 +81,12 @@ Separator *DefaultWidgetFactory::createSeparator(Anchor *anchor, QWidgetAdapter
return new SeparatorWidget(anchor, parent);
}
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(QWidget *parent) const
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(MainWindowBase *parent) const
{
return new FloatingWindowWidget(parent);
}
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, QWidget *parent) const
FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWindowBase *parent) const
{
return new FloatingWindowWidget(frame, parent);
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -34,6 +34,7 @@
namespace KDDockWidgets {
class MainWindowBase;
class DropIndicatorOverlayInterface;
class Separator;
class FloatingWindow;
@@ -108,14 +109,14 @@ public:
/// Override to provide your own FloatingWindow sub-class. If overridden then
/// you also need to override the overloads below.
///@param parent Just forward to FloatingWindow's constructor.
virtual FloatingWindow *createFloatingWindow(QWidgetOrQuick *parent = nullptr) const = 0;
virtual FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const = 0;
///@brief Called internally by the framework to create a FloatingWindow
/// Override to provide your own FloatingWindow sub-class. If overridden then
/// you also need to override the overloads above.
///@param frame Just forward to FloatingWindow's constructor.
///@param parent Just forward to FloatingWindow's constructor.
virtual FloatingWindow *createFloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr) const = 0;
virtual FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const = 0;
///@brief Called internally by the framework to create a DropIndicatorOverlayInterface
/// Override to provide your own DropIndicatorOverlayInterface sub-class.
@@ -135,8 +136,8 @@ public:
TabBar *createTabBar(TabWidget *parent) const override;
TabWidget *createTabWidget(Frame *parent) const override;
Separator *createSeparator(Anchor *anchor, QWidgetAdapter *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(QWidgetOrQuick *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override;
};

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -59,6 +59,13 @@ namespace KDDockWidgets
};
Q_DECLARE_FLAGS(FrameOptions, FrameOption)
enum RestoreOption {
RestoreOption_None = 0,
RestoreOption_RelativeToMainWindow = 1, ///< Skips restoring the main window geometry and the restored dock widgets will use relative sizing.
///< Loading layouts won't change the main window geometry and just use whatever the user has at the moment.
};
Q_DECLARE_FLAGS(RestoreOptions, RestoreOption)
///@internal
inline Location oppositeLocation(Location loc)
{

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -38,15 +38,52 @@
#include "multisplitter/Item_p.h"
#include "FrameworkWidgetFactory.h"
#include <qmath.h>
#include <QDebug>
#include <QSettings>
#include <QApplication>
#include <QFile>
#include <memory>
using namespace KDDockWidgets;
QHash<QString, LayoutSaver::DockWidget::Ptr> LayoutSaver::DockWidget::s_dockWidgets;
LayoutSaver::Layout* LayoutSaver::Layout::s_currentLayoutBeingRestored = nullptr;
static QVariantMap sizeToMap(QSize sz)
{
QVariantMap map;
map.insert(QStringLiteral("width"), sz.width());
map.insert(QStringLiteral("height"), sz.height());
return map;
}
static QVariantMap rectToMap(QRect rect)
{
QVariantMap map;
map.insert(QStringLiteral("x"), rect.x());
map.insert(QStringLiteral("y"), rect.y());
map.insert(QStringLiteral("width"), rect.width());
map.insert(QStringLiteral("height"), rect.height());
return map;
}
static QSize mapToSize(const QVariantMap &map)
{
return { map.value(QStringLiteral("width")).toInt(),
map.value(QStringLiteral("height")).toInt() };
}
static QRect mapToRect(const QVariantMap &map)
{
return QRect(map.value(QStringLiteral("x")).toInt(),
map.value(QStringLiteral("y")).toInt(),
map.value(QStringLiteral("width")).toInt(),
map.value(QStringLiteral("height")).toInt());
}
class KDDockWidgets::LayoutSaver::Private
{
@@ -66,12 +103,15 @@ public:
Q_DISABLE_COPY(RAIIIsRestoring)
};
Private()
Private(RestoreOptions options)
: m_dockRegistry(DockRegistry::self())
, m_restoreOptions(options)
{
}
void serializeWindowGeometry(QDataStream &ds, QWidgetOrQuick *topLevel);
bool matchesAffinity(const QString &affinityName) const {
return m_affinityNames.isEmpty() || affinityName.isEmpty() || m_affinityNames.contains(affinityName);
}
template <typename T>
void deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel);
@@ -80,13 +120,15 @@ public:
std::unique_ptr<QSettings> settings() const;
DockRegistry *const m_dockRegistry;
const RestoreOptions m_restoreOptions;
QStringList m_affinityNames;
static bool s_restoreInProgress;
};
bool LayoutSaver::Private::s_restoreInProgress = false;
LayoutSaver::LayoutSaver()
: d(new Private())
LayoutSaver::LayoutSaver(RestoreOptions options)
: d(new Private(options))
{
}
@@ -95,30 +137,38 @@ LayoutSaver::~LayoutSaver()
delete d;
}
bool LayoutSaver::saveToDisk()
bool LayoutSaver::saveToFile(const QString &jsonFilename)
{
if (qApp->organizationName().isEmpty() || qApp->applicationName().isEmpty()) {
qWarning() << Q_FUNC_INFO
<< "Cannot save. Either organization name or application name is empty.";
const QByteArray data = serializeLayout();
QFile f(jsonFilename);
if (!f.open(QIODevice::WriteOnly)) {
qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
return false;
}
const QByteArray data = serializeLayout();
d->settings()->setValue(QStringLiteral("data"), data);
f.write(data);
return true;
}
bool LayoutSaver::restoreFromDisk()
bool LayoutSaver::restoreFromFile(const QString &jsonFilename)
{
const QByteArray data = d->settings()->value(QStringLiteral("data")).toByteArray();
QFile f(jsonFilename);
if (!f.open(QIODevice::ReadOnly)) {
qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
return false;
}
const QByteArray data = f.readAll();
const bool result = restoreLayout(data);
return result;
}
QByteArray LayoutSaver::serializeLayout() const
{
if (!d->m_dockRegistry->isSane()) {
qWarning() << Q_FUNC_INFO << "Refusing to serialize insane layout";
qWarning() << Q_FUNC_INFO << "Refusing to serialize this layout. Check previous warnings.";
return {};
}
@@ -128,37 +178,41 @@ QByteArray LayoutSaver::serializeLayout() const
d->m_dockRegistry->ensureAllFloatingWidgetsAreMorphed();
const MainWindowBase::List mainWindows = d->m_dockRegistry->mainwindows();
layout.mainWindows.reserve(mainWindows.size());
for (MainWindowBase *mainWindow : mainWindows) {
layout.mainWindows.push_back(mainWindow->serialize());
if (d->matchesAffinity(mainWindow->affinityName()))
layout.mainWindows.push_back(mainWindow->serialize());
}
const QVector<KDDockWidgets::FloatingWindow*> floatingWindows = d->m_dockRegistry->nestedwindows();
layout.floatingWindows.reserve(floatingWindows.size());
for (KDDockWidgets::FloatingWindow *floatingWindow : floatingWindows) {
layout.floatingWindows.push_back(floatingWindow->serialize());
if (d->matchesAffinity(floatingWindow->affinityName()))
layout.floatingWindows.push_back(floatingWindow->serialize());
}
// Closed dock widgets also have interesting things to save, like geometry and placeholder info
const DockWidgetBase::List closedDockWidgets = d->m_dockRegistry->closedDockwidgets();
layout.closedDockWidgets.reserve(closedDockWidgets.size());
for (DockWidgetBase *dockWidget : closedDockWidgets) {
layout.closedDockWidgets.push_back(dockWidget->serialize());
if (d->matchesAffinity(dockWidget->affinityName()))
layout.closedDockWidgets.push_back(dockWidget->serialize());
}
// Save the placeholder info. We do it last, as we also restore it last, since we need all items to be created
// before restoring the placeholders
const DockWidgetBase::List dockWidgets = d->m_dockRegistry->dockwidgets();
layout.allDockWidgets.reserve(dockWidgets.size());
for (DockWidgetBase *dockWidget : dockWidgets) {
auto dw = dockWidget->serialize();
dw->lastPosition = dockWidget->lastPosition()->serialize();
layout.allDockWidgets.push_back(dw);
if (d->matchesAffinity(dockWidget->affinityName())) {
auto dw = dockWidget->serialize();
dw->lastPosition = dockWidget->lastPosition()->serialize();
layout.allDockWidgets.push_back(dw);
}
}
QByteArray result;
QDataStream ds(&result, QIODevice::WriteOnly);
ds << &layout;
return result;
return layout.toJson();
}
bool LayoutSaver::restoreLayout(const QByteArray &data)
@@ -167,10 +221,36 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
if (data.isEmpty())
return true;
struct EnsureItemsAtCorrectPlace {
EnsureItemsAtCorrectPlace(LayoutSaver *ls)
: layoutSaver(ls)
{
}
~EnsureItemsAtCorrectPlace()
{
// When using RestoreOption_RelativeToMainWindow we'll have many rounding errors so the layout won't be exact.
// Make sure to run a relayout at the end
// (Using RAII to make sure it runs after Private::RAIIIsRestoring went out of scope, since "isRestoring= true" inhibits relayout
if (ensure) {
for (auto layout : DockRegistry::self()->layouts()) {
if (layoutSaver->d->matchesAffinity(layout->affinityName()))
layout->redistributeSpace();
}
}
}
bool ensure = false;
LayoutSaver *const layoutSaver;
};
EnsureItemsAtCorrectPlace ensureItemsAtCorrectPlace(this);
Private::RAIIIsRestoring isRestoring;
struct FrameCleanup {
FrameCleanup(LayoutSaver *saver) : m_saver(saver)
FrameCleanup(LayoutSaver *saver)
: m_saver(saver)
{
}
@@ -180,17 +260,20 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
}
LayoutSaver *const m_saver;
};
FrameCleanup cleanup(this);
LayoutSaver::Layout layout;
if (!layout.fillFrom(data))
if (!layout.fromJson(data)) {
qWarning() << Q_FUNC_INFO << "Failed to parse json data";
return false;
}
if (d->m_restoreOptions & RestoreOption_RelativeToMainWindow)
layout.scaleSizes();
// Hide all dockwidgets and unparent them from any layout before starting restore
d->m_dockRegistry->clear(/*deleteStaticAnchors=*/true);
d->m_dockRegistry->clear(d->m_affinityNames, /*deleteStaticAnchors=*/true);
// 1. Restore main windows
for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) {
@@ -200,7 +283,11 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
return false;
}
d->deserializeWindowGeometry(mw, mainWindow->window()); // window() as the MainWindow can be embedded
if (!d->matchesAffinity(mainWindow->affinityName()))
continue;
if (!(d->m_restoreOptions & RestoreOption_RelativeToMainWindow))
d->deserializeWindowGeometry(mw, mainWindow->window()); // window(), as the MainWindow can be embedded
if (!mainWindow->deserialize(mw))
return false;
@@ -208,8 +295,11 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
// 2. Restore FloatingWindows
for (const LayoutSaver::FloatingWindow &fw : qAsConst(layout.floatingWindows)) {
QWidget *parent = fw.parentIndex == -1 ? nullptr
: DockRegistry::self()->mainwindows().at(fw.parentIndex);
if (!d->matchesAffinity(fw.affinityName))
continue;
MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
: DockRegistry::self()->mainwindows().at(fw.parentIndex);
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
d->deserializeWindowGeometry(fw, floatingWindow);
@@ -220,11 +310,16 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
// 3. Restore closed dock widgets. They remain closed but acquire geometry and placeholder properties
for (const auto &dw : qAsConst(layout.closedDockWidgets)) {
DockWidgetBase::deserialize(dw);
if (d->matchesAffinity(dw->affinityName)) {
DockWidgetBase::deserialize(dw);
}
}
// 4. Restore the placeholder info, now that the Items have been created
for (const auto &dw : qAsConst(layout.allDockWidgets)) {
if (!d->matchesAffinity(dw->affinityName))
continue;
if (DockWidgetBase *dockWidget = d->m_dockRegistry->dockByName(dw->uniqueName)) {
dockWidget->lastPosition()->deserialize(dw->lastPosition);
} else {
@@ -232,9 +327,21 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
}
}
// our raii class will run when
ensureItemsAtCorrectPlace.ensure = d->m_restoreOptions & RestoreOption_RelativeToMainWindow;
return true;
}
void LayoutSaver::setAffinityNames(const QStringList &affinityNames)
{
d->m_affinityNames = affinityNames;
if (affinityNames.contains(QString())) {
// Any window with empty affinity will also be subject to save/restore
d->m_affinityNames << QString();
}
}
DockWidgetBase::List LayoutSaver::restoredDockWidgets() const
{
const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
@@ -314,15 +421,103 @@ bool LayoutSaver::Layout::fillFrom(const QByteArray &serialized)
QDataStream ds(serialized);
ds >> this;
if (serializationVersion != KDDOCKWIDGETS_SERIALIZATION_VERSION) {
if (serializationVersion > KDDOCKWIDGETS_SERIALIZATION_VERSION) {
qWarning() << "Unsupported serialization version. Got=" << serializationVersion
<< "; expected=" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
<< "; expected equal or less than" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
return false;
}
return true;
}
QByteArray LayoutSaver::Layout::toJson() const
{
QJsonDocument doc = QJsonDocument::fromVariant(toVariantMap());
return doc.toJson();
}
bool LayoutSaver::Layout::fromJson(const QByteArray &jsonData)
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
if (error.error == QJsonParseError::NoError) {
fromVariantMap(doc.toVariant().toMap());
return true;
}
return false;
}
QVariantMap LayoutSaver::Layout::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("serializationVersion"), serializationVersion);
map.insert(QStringLiteral("mainWindows"), toVariantList<LayoutSaver::MainWindow>(mainWindows));
map.insert(QStringLiteral("floatingWindows"), toVariantList<LayoutSaver::FloatingWindow>(floatingWindows));
map.insert(QStringLiteral("closedDockWidgets"), dockWidgetNames(closedDockWidgets));
map.insert(QStringLiteral("allDockWidgets"), toVariantList(allDockWidgets));
map.insert(QStringLiteral("screenInfo"), toVariantList<LayoutSaver::ScreenInfo>(screenInfo));
return map;
}
void LayoutSaver::Layout::fromVariantMap(const QVariantMap &map)
{
allDockWidgets.clear();
const QVariantList dockWidgetsV = map.value(QStringLiteral("allDockWidgets")).toList();
for (const QVariant &v : dockWidgetsV) {
const QVariantMap dwV = v.toMap();
const QString name = dwV.value(QStringLiteral("uniqueName")).toString();
auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
dw->fromVariantMap(dwV);
allDockWidgets.push_back(dw);
}
closedDockWidgets.clear();
const QVariantList closedDockWidgetsV = map.value(QStringLiteral("closedDockWidgets")).toList();
closedDockWidgets.reserve(closedDockWidgetsV.size());
for (const QVariant &v : closedDockWidgetsV) {
closedDockWidgets.push_back(LayoutSaver::DockWidget::dockWidgetForName(v.toString()));
}
serializationVersion = map.value(QStringLiteral("serializationVersion")).toInt();
mainWindows = fromVariantList<LayoutSaver::MainWindow>(map.value(QStringLiteral("mainWindows")).toList());
floatingWindows = fromVariantList<LayoutSaver::FloatingWindow>(map.value(QStringLiteral("floatingWindows")).toList());
screenInfo = fromVariantList<LayoutSaver::ScreenInfo>(map.value(QStringLiteral("screenInfo")).toList());
}
void LayoutSaver::Layout::scaleSizes()
{
if (mainWindows.isEmpty())
return;
for (auto &mw : mainWindows)
mw.scaleSizes();
for (auto &fw : floatingWindows) {
LayoutSaver::MainWindow mw = mainWindowForIndex(fw.parentIndex);
if (mw.scalingInfo.isValid())
fw.scaleSizes(mw.scalingInfo);
}
const ScalingInfo firstScalingInfo = mainWindows.constFirst().scalingInfo;
if (firstScalingInfo.isValid()) {
for (auto &dw : allDockWidgets) {
// TODO: Determine the best main window. This only interesting for closed dock widget geometry
// which was previously floating. But they still have some other main window as parent.
dw->scaleSizes(firstScalingInfo);
}
}
}
LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
{
if (index < 0 || index >= mainWindows.size())
return {};
return mainWindows.at(index);
}
bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
{
if (!frame.isValid())
@@ -331,9 +526,9 @@ bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout)
const int numAnchors = layout.anchors.size();
if (indexOfLeftAnchor < 0 || indexOfTopAnchor < 0 ||
indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 ||
indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors ||
indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) {
indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 ||
indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors ||
indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) {
qWarning() << Q_FUNC_INFO << "Invalid anchor indexes"
<< indexOfLeftAnchor << indexOfTopAnchor
<< indexOfBottomAnchor << indexOfRightAnchor;
@@ -343,6 +538,43 @@ bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout)
return true;
}
void LayoutSaver::Item::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(geometry);
if (!frame.isNull)
frame.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::Item::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("objectName"), objectName);
map.insert(QStringLiteral("isPlaceholder"), isPlaceholder);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
map.insert(QStringLiteral("indexOfLeftAnchor"), indexOfLeftAnchor);
map.insert(QStringLiteral("indexOfTopAnchor"), indexOfTopAnchor);
map.insert(QStringLiteral("indexOfRightAnchor"), indexOfRightAnchor);
map.insert(QStringLiteral("indexOfBottomAnchor"), indexOfBottomAnchor);
if (!frame.isNull)
map.insert(QStringLiteral("frame"), frame.toVariantMap());
return map;
}
void LayoutSaver::Item::fromVariantMap(const QVariantMap &map)
{
objectName = map.value(QStringLiteral("objectName")).toString();
isPlaceholder = map.value(QStringLiteral("isPlaceholder")).toBool();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
indexOfLeftAnchor = map.value(QStringLiteral("indexOfLeftAnchor")).toInt();
indexOfTopAnchor = map.value(QStringLiteral("indexOfTopAnchor")).toInt();
indexOfRightAnchor = map.value(QStringLiteral("indexOfRightAnchor")).toInt();
indexOfBottomAnchor = map.value(QStringLiteral("indexOfBottomAnchor")).toInt();
frame.fromVariantMap(map.value(QStringLiteral("frame"), QVariantMap()).toMap());
}
bool LayoutSaver::Frame::isValid() const
{
if (!isNull)
@@ -353,7 +585,7 @@ bool LayoutSaver::Frame::isValid() const
return false;
}
if (options < 0 || options > 3) {
if (options > 3) {
qWarning() << Q_FUNC_INFO << "Invalid options" << options;
return false;
}
@@ -371,11 +603,77 @@ bool LayoutSaver::Frame::isValid() const
return true;
}
void LayoutSaver::Frame::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(geometry);
}
QVariantMap LayoutSaver::Frame::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("isNull"), isNull);
map.insert(QStringLiteral("objectName"), objectName);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("options"), options);
map.insert(QStringLiteral("currentTabIndex"), currentTabIndex);
map.insert(QStringLiteral("dockWidgets"), dockWidgetNames(dockWidgets));
return map;
}
void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
{
if (map.isEmpty()) {
isNull = true;
dockWidgets.clear();
return;
}
isNull = map.value(QStringLiteral("isNull")).toBool();
objectName = map.value(QStringLiteral("objectName")).toString();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
options = map.value(QStringLiteral("options")).toUInt();
currentTabIndex = map.value(QStringLiteral("currentTabIndex")).toInt();
QVariantList dockWidgetsV = map.value(QStringLiteral("dockWidgets")).toList();
dockWidgets.clear();
dockWidgets.reserve(dockWidgetsV.size());
for (const auto &variant : dockWidgetsV) {
DockWidget::Ptr dw = DockWidget::dockWidgetForName(variant.toString());
dockWidgets.push_back(dw);
}
}
bool LayoutSaver::DockWidget::isValid() const
{
return !uniqueName.isEmpty();
}
void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
{
lastPosition.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::DockWidget::toVariantMap() const
{
QVariantMap map;
if (!affinityName.isEmpty())
map.insert(QStringLiteral("affinityName"), affinityName);
map.insert(QStringLiteral("uniqueName"), uniqueName);
map.insert(QStringLiteral("lastPosition"), lastPosition.toVariantMap());
return map;
}
void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map)
{
affinityName = map.value(QStringLiteral("affinityName")).toString();
uniqueName = map.value(QStringLiteral("uniqueName")).toString();
lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap());
}
bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout) const
{
const bool isStatic = type != KDDockWidgets::Anchor::Type_None;
@@ -423,6 +721,73 @@ bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout
return true;
}
QVariantMap LayoutSaver::Anchor::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("objectName"), objectName);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("orientation"), orientation);
map.insert(QStringLiteral("type"), type);
map.insert(QStringLiteral("indexOfFrom"), indexOfFrom);
map.insert(QStringLiteral("indexOfTo"), indexOfTo);
map.insert(QStringLiteral("indexOfFollowee"), indexOfFollowee);
map.insert(QStringLiteral("positionPercentage"), positionPercentage);
QVariantList side1ItemsV;
QVariantList side2ItemsV;
side1ItemsV.reserve(side1Items.size());
side2ItemsV.reserve(side2Items.size());
for (int index : qAsConst(side1Items))
side1ItemsV.push_back(index);
for (int index : qAsConst(side2Items))
side2ItemsV.push_back(index);
map.insert(QStringLiteral("side1Items"), side1ItemsV);
map.insert(QStringLiteral("side2Items"), side2ItemsV);
return map;
}
void LayoutSaver::Anchor::fromVariantMap(const QVariantMap &map)
{
objectName = map.value(QStringLiteral("objectName")).toString();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
orientation = map.value(QStringLiteral("orientation")).toInt();
type = map.value(QStringLiteral("type")).toInt();
indexOfFrom = map.value(QStringLiteral("indexOfFrom")).toInt();
indexOfTo = map.value(QStringLiteral("indexOfTo")).toInt();
indexOfFollowee = map.value(QStringLiteral("indexOfFollowee")).toInt();
positionPercentage = map.value(QStringLiteral("positionPercentage")).toDouble();
side1Items.clear();
side2Items.clear();
const QVariantList side1ItemsV = map.value(QStringLiteral("side1Items")).toList();
const QVariantList side2ItemsV = map.value(QStringLiteral("side2Items")).toList();
side1Items.reserve(side1ItemsV.size());
side2Items.reserve(side2ItemsV.size());
for (const QVariant &v : side1ItemsV)
side1Items.push_back(v.toInt());
for (const QVariant &v : side2ItemsV)
side2Items.push_back(v.toInt());
}
void LayoutSaver::Anchor::scaleSizes(const ScalingInfo &scalingInfo)
{
const QPoint pos = geometry.topLeft();
if (isVertical()) {
geometry.moveLeft(int(pos.x() * scalingInfo.widthFactor));
} else {
geometry.moveTop(int(pos.y() * scalingInfo.heightFactor));
}
}
bool LayoutSaver::Anchor::isVertical() const
{
return orientation == Qt::Vertical;
}
bool LayoutSaver::FloatingWindow::isValid() const
{
if (!multiSplitterLayout.isValid())
@@ -436,6 +801,39 @@ bool LayoutSaver::FloatingWindow::isValid() const
return true;
}
void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(/*by-ref*/geometry);
multiSplitterLayout.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
map.insert(QStringLiteral("parentIndex"), parentIndex);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("screenIndex"), screenIndex);
map.insert(QStringLiteral("screenSize"), sizeToMap(screenSize));
map.insert(QStringLiteral("isVisible"), isVisible);
if (!affinityName.isEmpty())
map.insert(QStringLiteral("affinityName"), affinityName);
return map;
}
void LayoutSaver::FloatingWindow::fromVariantMap(const QVariantMap &map)
{
multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
parentIndex = map.value(QStringLiteral("parentIndex")).toInt();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
screenSize = mapToSize(map.value(QStringLiteral("screenSize")).toMap());
isVisible = map.value(QStringLiteral("isVisible")).toBool();
affinityName = map.value(QStringLiteral("affinityName")).toString();
}
bool LayoutSaver::MainWindow::isValid() const
{
if (!multiSplitterLayout.isValid())
@@ -449,6 +847,50 @@ bool LayoutSaver::MainWindow::isValid() const
return true;
}
void LayoutSaver::MainWindow::scaleSizes()
{
if (scalingInfo.isValid()) {
// Doesn't happen, it's called only once
Q_ASSERT(false);
return;
}
scalingInfo = ScalingInfo(uniqueName, geometry);
if (scalingInfo.isValid())
multiSplitterLayout.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::MainWindow::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("options"), int(options));
map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
map.insert(QStringLiteral("uniqueName"), uniqueName);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("screenIndex"), screenIndex);
map.insert(QStringLiteral("screenSize"), sizeToMap(screenSize));
map.insert(QStringLiteral("isVisible"), isVisible);
if (!affinityName.isEmpty())
map.insert(QStringLiteral("affinityName"), affinityName);
return map;
}
void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
{
options = KDDockWidgets::MainWindowOptions(map.value(QStringLiteral("options")).toInt());
multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
uniqueName = map.value(QStringLiteral("uniqueName")).toString();
affinityName = map.value(QStringLiteral("affinityName")).toString();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
screenSize = mapToSize(map.value(QStringLiteral("screenSize")).toMap());
isVisible = map.value(QStringLiteral("isVisible")).toBool();
}
bool LayoutSaver::MultiSplitterLayout::isValid() const
{
for (auto &item : items) {
@@ -468,3 +910,161 @@ bool LayoutSaver::MultiSplitterLayout::isValid() const
return true;
}
void LayoutSaver::MultiSplitterLayout::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(/*by-ref*/size);
for (LayoutSaver::Anchor &anchor : anchors)
anchor.scaleSizes(scalingInfo);
for (LayoutSaver::Item &item : items)
item.scaleSizes(scalingInfo);
}
QVariantMap LayoutSaver::MultiSplitterLayout::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("anchors"), toVariantList<LayoutSaver::Anchor>(anchors));
map.insert(QStringLiteral("items"), toVariantList<LayoutSaver::Item>(items));
map.insert(QStringLiteral("minSize"), sizeToMap(minSize));
map.insert(QStringLiteral("size"), sizeToMap(size));
return map;
}
void LayoutSaver::MultiSplitterLayout::fromVariantMap(const QVariantMap &map)
{
anchors = fromVariantList<LayoutSaver::Anchor>(map.value(QStringLiteral("anchors")).toList());
items = fromVariantList<LayoutSaver::Item>(map.value(QStringLiteral("items")).toList());
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
size = mapToSize(map.value(QStringLiteral("size")).toMap());
}
void LayoutSaver::LastPosition::scaleSizes(const ScalingInfo &scalingInfo)
{
scalingInfo.applyFactorsTo(/*by-ref*/lastFloatingGeometry);
}
QVariantMap LayoutSaver::LastPosition::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("lastFloatingGeometry"), rectToMap(lastFloatingGeometry));
map.insert(QStringLiteral("tabIndex"), tabIndex);
map.insert(QStringLiteral("wasFloating"), wasFloating);
map.insert(QStringLiteral("placeholders"), toVariantList<LayoutSaver::Placeholder>(placeholders));
return map;
}
void LayoutSaver::LastPosition::fromVariantMap(const QVariantMap &map)
{
lastFloatingGeometry = mapToRect(map.value(QStringLiteral("lastFloatingGeometry")).toMap());
tabIndex = map.value(QStringLiteral("tabIndex")).toInt();
wasFloating = map.value(QStringLiteral("wasFloating")).toBool();
placeholders = fromVariantList<LayoutSaver::Placeholder>(map.value(QStringLiteral("placeholders")).toList());
}
QVariantMap LayoutSaver::ScreenInfo::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("index"), index);
map.insert(QStringLiteral("geometry"), rectToMap(geometry));
map.insert(QStringLiteral("name"), name);
map.insert(QStringLiteral("devicePixelRatio"), devicePixelRatio);
return map;
}
void LayoutSaver::ScreenInfo::fromVariantMap(const QVariantMap &map)
{
index = map.value(QStringLiteral("index")).toInt();
geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap());
name = map.value(QStringLiteral("name")).toString();
devicePixelRatio = map.value(QStringLiteral("devicePixelRatio")).toDouble();
}
QVariantMap LayoutSaver::Placeholder::toVariantMap() const
{
QVariantMap map;
map.insert(QStringLiteral("isFloatingWindow"), isFloatingWindow);
map.insert(QStringLiteral("itemIndex"), itemIndex);
if (isFloatingWindow)
map.insert(QStringLiteral("indexOfFloatingWindow"), indexOfFloatingWindow);
else
map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
return map;
}
void LayoutSaver::Placeholder::fromVariantMap(const QVariantMap &map)
{
isFloatingWindow = map.value(QStringLiteral("isFloatingWindow")).toBool();
indexOfFloatingWindow = map.value(QStringLiteral("indexOfFloatingWindow"), -1).toInt();
itemIndex = map.value(QStringLiteral("itemIndex")).toInt();
mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
}
LayoutSaver::ScalingInfo::ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo)
{
auto mainWindow = DockRegistry::self()->mainWindowByName(mainWindowId);
if (!mainWindow) {
qWarning() << Q_FUNC_INFO << "Failed to find main window with name" << mainWindowName;
return;
}
if (!savedMainWindowGeo.isValid() || savedMainWindowGeo.isNull()) {
qWarning() << Q_FUNC_INFO << "Invalid saved main window geometry" << savedMainWindowGeo;
return;
}
if (!mainWindow->geometry().isValid() || mainWindow->geometry().isNull()) {
qWarning() << Q_FUNC_INFO << "Invalid main window geometry" << mainWindow->geometry();
return;
}
this->mainWindowName = mainWindowId;
this->savedMainWindowGeometry = savedMainWindowGeo;
realMainWindowGeometry = mainWindow->window()->geometry(); // window() as our main window might be embedded
widthFactor = double(realMainWindowGeometry.width()) / savedMainWindowGeo.width();
heightFactor = double(realMainWindowGeometry.height()) / savedMainWindowGeo.height();
}
void LayoutSaver::ScalingInfo::translatePos(QPoint &pt) const
{
const int deltaX = pt.x() - savedMainWindowGeometry.x();
const int deltaY = pt.y() - savedMainWindowGeometry.y();
const double newDeltaX = deltaX * widthFactor;
const double newDeltaY = deltaY * heightFactor;
pt.setX(qCeil(savedMainWindowGeometry.x() + newDeltaX));
pt.setY(qCeil(savedMainWindowGeometry.y() + newDeltaY));
}
void LayoutSaver::ScalingInfo::applyFactorsTo(QPoint &pt) const
{
pt.setX(qCeil(pt.x() * widthFactor));
pt.setY(qCeil(pt.y() * heightFactor));
}
void LayoutSaver::ScalingInfo::applyFactorsTo(QSize &sz) const
{
sz.setWidth(int(widthFactor * sz.width()));
sz.setHeight(int(heightFactor * sz.height()));
}
void LayoutSaver::ScalingInfo::applyFactorsTo(QRect &rect) const
{
if (rect.isEmpty())
return;
QPoint pos = rect.topLeft();
QSize size = rect.size();
applyFactorsTo(/*by-ref*/size);
applyFactorsTo(/*by-ref*/pos);
rect.moveTopLeft(pos);
rect.setSize(size);
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,11 @@
#include "docks_export.h"
#include "KDDockWidgets.h"
QT_BEGIN_NAMESPACE
class QByteArray;
QT_END_NAMESPACE
namespace KDDockWidgets {
@@ -40,7 +44,7 @@ class DOCKS_EXPORT LayoutSaver
{
public:
///@brief Constructor. Construction on the stack is suggested.
LayoutSaver();
explicit LayoutSaver(RestoreOptions options = RestoreOption_None);
///@brief Destructor.
~LayoutSaver();
@@ -49,15 +53,18 @@ public:
static bool restoreInProgress();
/**
* @brief saves the layout to disk using QSettings
*/
bool saveToDisk();
/**
* @brief restores the layout from disk using QSettings.
* @brief saves the layout to JSON file
* @brief jsonFilename the filename where the layout will be saved to
* @return true on success
*/
bool restoreFromDisk();
bool saveToFile(const QString &jsonFilename);
/**
* @brief restores the layout from a JSON file
* @brief jsonFilename the filename containing a saved layout
* @return true on success
*/
bool restoreFromFile(const QString &jsonFilename);
/**
* @brief saves the layout into a byte array
@@ -86,6 +93,15 @@ public:
*/
QVector<DockWidgetBase *> restoredDockWidgets() const;
/**
* @brief Sets the list of affinity names for which restore and save will be applied on.
* Allows to save/restore only a subset of the windows.
* Empty by default, all windows are subject to save/restore.
* Any window with empty affinity will also be subject to save/restore, regardless of @p affinityNames.
*/
void setAffinityNames(const QStringList &affinityNames);
struct Layout;
struct MainWindow;
struct FloatingWindow;
@@ -96,6 +112,8 @@ public:
struct Anchor;
struct Frame;
struct Placeholder;
struct ScalingInfo;
struct ScreenInfo;
private:
friend class TestDocks;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -27,25 +27,86 @@
#include <QRect>
#include <QDataStream>
#include <QDebug>
#include <QScreen>
#include <QApplication>
#include <QJsonDocument>
#include <memory>
#define ANCHOR_MAGIC_MARKER "e520c60e-cf5d-4a30-b1a7-588d2c569851"
#define MULTISPLITTER_LAYOUT_MAGIC_MARKER "bac9948e-5f1b-4271-acc5-07f1708e2611"
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 1
/**
* Bump whenever the format changes, so we can still load old layouts.
* version 1: Initial version
* version 2: Introduced MainWindow::screenSize and FloatingWindow::screenSize
*/
#define KDDOCKWIDGETS_SERIALIZATION_VERSION 2
namespace KDDockWidgets {
template <typename T>
typename T::List fromVariantList(const QVariantList &listV)
{
typename T::List result;
result.reserve(listV.size());
for (const QVariant &v : listV) {
T t;
t.fromVariantMap(v.toMap());
result.push_back(t);
}
return result;
}
template <typename T>
QVariantList toVariantList(const typename T::List &list)
{
QVariantList result;
result.reserve(list.size());
for (const T &v : list)
result.push_back(v.toVariantMap());
return result;
}
struct LayoutSaver::Placeholder
{
typedef QVector<LayoutSaver::Placeholder> List;
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
bool isFloatingWindow;
int indexOfFloatingWindow;
int itemIndex;
QString mainWindowUniqueName;
};
///@brief contains info about how a main window is scaled.
///Used for RestoreOption_RelativeToMainWindow
struct LayoutSaver::ScalingInfo
{
ScalingInfo() = default;
explicit ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo);
bool isValid() const {
return heightFactor > 0 && widthFactor > 0 && !((qFuzzyCompare(widthFactor, 1) && qFuzzyCompare(heightFactor, 1)));
}
void translatePos(QPoint &) const;
void applyFactorsTo(QPoint &) const;
void applyFactorsTo(QSize &) const;
void applyFactorsTo(QRect &) const;
QString mainWindowName;
QRect savedMainWindowGeometry;
QRect realMainWindowGeometry;
double heightFactor = -1;
double widthFactor = -1;
};
struct LayoutSaver::LastPosition
{
@@ -53,6 +114,12 @@ struct LayoutSaver::LastPosition
int tabIndex;
bool wasFloating;
LayoutSaver::Placeholder::List placeholders;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
};
struct DOCKS_EXPORT LayoutSaver::DockWidget
@@ -64,6 +131,9 @@ struct DOCKS_EXPORT LayoutSaver::DockWidget
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
static Ptr dockWidgetForName(const QString &name)
{
auto dw = s_dockWidgets.value(name);
@@ -71,26 +141,58 @@ struct DOCKS_EXPORT LayoutSaver::DockWidget
return dw;
dw = Ptr(new LayoutSaver::DockWidget);
s_dockWidgets.insert(name, dw);
dw->uniqueName = name;
return dw;
}
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
QString uniqueName;
QString affinityName;
LayoutSaver::LastPosition lastPosition;
private:
DockWidget() {}
};
inline QVariantList toVariantList(const LayoutSaver::DockWidget::List &list)
{
QVariantList result;
result.reserve(list.size());
for (const auto &dw : list)
result.push_back(dw->toVariantMap());
return result;
}
inline QVariantList dockWidgetNames(const LayoutSaver::DockWidget::List &list)
{
QVariantList result;
result.reserve(list.size());
for (auto &dw : list)
result.push_back(dw->uniqueName);
return result;
}
struct LayoutSaver::Frame
{
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
bool isNull = true;
QString objectName;
QRect geometry;
int options;
unsigned int options;
int currentTabIndex;
LayoutSaver::DockWidget::List dockWidgets;
@@ -101,17 +203,20 @@ struct LayoutSaver::Item
typedef QVector<LayoutSaver::Item> List;
bool isValid(const MultiSplitterLayout &) const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
QString objectName;
bool isPlaceholder;
QRect geometry;
QSize minSize;
int indexOfLeftAnchor;
int indexOfTopAnchor;
int indexOfRightAnchor;
int indexOfBottomAnchor;
LayoutSaver::Frame frame;
};
@@ -121,8 +226,15 @@ struct LayoutSaver::Anchor
bool isValid(const LayoutSaver::MultiSplitterLayout &layout) const;
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
void scaleSizes(const ScalingInfo &);
bool isVertical() const;
QString objectName;
QRect geometry;
double positionPercentage;
int orientation;
int type;
int indexOfFrom;
@@ -135,6 +247,11 @@ struct LayoutSaver::Anchor
struct LayoutSaver::MultiSplitterLayout
{
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &scalingInfo);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
LayoutSaver::Anchor::List anchors;
LayoutSaver::Item::List items;
@@ -148,9 +265,18 @@ struct LayoutSaver::FloatingWindow
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes(const ScalingInfo &);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
QString affinityName;
int parentIndex = -1;
QRect geometry;
int screenIndex;
QSize screenSize; // for relative-size restoring
bool isVisible = true;
};
@@ -161,36 +287,91 @@ public:
bool isValid() const;
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes();
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
KDDockWidgets::MainWindowOptions options;
LayoutSaver::MultiSplitterLayout multiSplitterLayout;
QString uniqueName;
QString affinityName;
QRect geometry;
int screenIndex;
QSize screenSize; // for relative-size restoring
bool isVisible;
ScalingInfo scalingInfo;
};
///@brief we serialize some info about screens, so eventually we can make restore smarter when switching screens
///Not used currently, but nice to have in the json already
struct LayoutSaver::ScreenInfo
{
typedef QVector<LayoutSaver::ScreenInfo> List;
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
int index;
QRect geometry;
QString name;
double devicePixelRatio;
};
struct LayoutSaver::Layout
{
public:
Layout() {
s_currentLayoutBeingRestored = this;
const QList<QScreen*> screens = qApp->screens();
for (int i = 0; i < screens.size(); ++i) {
ScreenInfo info;
info.index = i;
info.geometry = screens[i]->geometry();
info.name = screens[i]->name();
info.devicePixelRatio = screens[i]->devicePixelRatio();
screenInfo.push_back(info);
}
}
~Layout() {
s_currentLayoutBeingRestored = nullptr;
}
bool isValid() const;
bool fillFrom(const QByteArray &serialized);
QByteArray toJson() const;
bool fromJson(const QByteArray &jsonData);
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
/// Iterates throught the layout and patches all absolute sizes. See RestoreOption_RelativeToMainWindow.
void scaleSizes();
friend QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame);
static LayoutSaver::Layout* s_currentLayoutBeingRestored;
LayoutSaver::MainWindow mainWindowForIndex(int index) const;
int serializationVersion = KDDOCKWIDGETS_SERIALIZATION_VERSION;
LayoutSaver::MainWindow::List mainWindows;
LayoutSaver::FloatingWindow::List floatingWindows;
LayoutSaver::DockWidget::List closedDockWidgets;
LayoutSaver::DockWidget::List allDockWidgets;
ScreenInfo::List screenInfo;
};
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Placeholder *p)
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::ScreenInfo *info)
{
ds << p->isFloatingWindow;
if (p->isFloatingWindow)
ds << p->indexOfFloatingWindow;
else
ds << p->mainWindowUniqueName;
ds << p->itemIndex;
ds >> info->index;
ds >> info->geometry;
ds >> info->name;
ds >> info->devicePixelRatio;
return ds;
}
@@ -208,22 +389,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Placeholder *p)
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Anchor *a)
{
ds << QStringLiteral(ANCHOR_MAGIC_MARKER);
ds << a->objectName;
ds << a->geometry;
ds << a->orientation;
ds << a->type;
ds << a->indexOfFrom;
ds << a->indexOfTo;
ds << a->indexOfFollowee;
ds << a->side1Items;
ds << a->side2Items;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a)
{
QString marker;
@@ -245,21 +410,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a)
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Frame *frame)
{
ds << frame->objectName;
ds << frame->geometry;
ds << frame->options;
ds << frame->currentTabIndex;
ds << frame->dockWidgets.size();
for (auto &dock : frame->dockWidgets) {
ds << dock->uniqueName;
}
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
{
int numDockWidgets;
@@ -268,6 +418,12 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
ds >> frame->objectName;
ds >> frame->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
QSize sz;
ds >> sz; // deprecated field, just discard
}
ds >> frame->options;
ds >> frame->currentTabIndex;
ds >> numDockWidgets;
@@ -282,26 +438,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame)
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Item *item)
{
ds << item->objectName;
ds << item->isPlaceholder;
ds << item->geometry;
ds << item->minSize;
ds << item->indexOfLeftAnchor;
ds << item->indexOfTopAnchor;
ds << item->indexOfRightAnchor;
ds << item->indexOfBottomAnchor;
const bool hasFrame = !item->frame.isNull;
ds << hasFrame;
if (hasFrame)
ds << &item->frame;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
{
ds >> item->objectName;
@@ -326,24 +462,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item)
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
{
ds << QStringLiteral(MULTISPLITTER_LAYOUT_MAGIC_MARKER);
ds << l->size;
ds << l->minSize;
ds << l->items.size();
ds << l->anchors.size();
for (auto &item : l->items)
ds << &item;
for (auto &anchor : l->anchors)
ds << &anchor;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l)
{
int numItems;
@@ -377,21 +495,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::LastPosition *lp)
{
ds << lp->placeholders.size();
for (auto &p : lp->placeholders) {
ds << &p;
}
ds << lp->lastFloatingGeometry;
ds << lp->tabIndex;
ds << lp->wasFloating;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
{
int numPlaceholders;
@@ -411,71 +514,34 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::LastPosition *lp)
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
{
ds << fw->parentIndex;
ds << fw->geometry;
ds << fw->isVisible;
ds << &fw->multiSplitterLayout;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::FloatingWindow *fw)
{
ds >> fw->parentIndex;
ds >> fw->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> fw->screenIndex;
ds >> fw->screenSize;
}
ds >> fw->isVisible;
ds >> &fw->multiSplitterLayout;
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::MainWindow *m)
{
ds << m->uniqueName;
ds << m->geometry;
ds << m->isVisible;
ds << m->options;
ds << &m->multiSplitterLayout;
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MainWindow *m)
{
ds >> m->uniqueName;
ds >> m->geometry;
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> m->screenIndex;
ds >> m->screenSize;
}
ds >> m->isVisible;
ds >> m->options;
ds >> &m->multiSplitterLayout;
return ds;
}
inline QDataStream &operator<<(QDataStream &ds, LayoutSaver::Layout *l)
{
ds << l->serializationVersion;
ds << l->mainWindows.size();
for (auto &m: l->mainWindows) {
ds << &m;
}
ds << l->floatingWindows.size();
for (auto &fw: l->floatingWindows) {
ds << &fw;
}
ds << l->closedDockWidgets.size();
for (auto &dw: l->closedDockWidgets) {
ds << dw->uniqueName;
}
ds << l->allDockWidgets.size();
for (auto &dw: l->allDockWidgets) {
ds << dw->uniqueName;
ds << &dw->lastPosition;
}
return ds;
}
inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
{
LayoutSaver::DockWidget::s_dockWidgets.clear();
@@ -483,6 +549,7 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
int numFloatingWindows;
int numClosedDockWidgets;
int numAllDockWidgets;
int numScreenInfo;
ds >> l->serializationVersion;
@@ -522,6 +589,18 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Layout *l)
l->allDockWidgets.push_back(dw);
}
if (LayoutSaver::Layout::s_currentLayoutBeingRestored->serializationVersion >= 2) {
ds >> numScreenInfo;
l->screenInfo.clear();
l->screenInfo.reserve(numScreenInfo);
for (int i = 0; i < numScreenInfo; ++i) {
LayoutSaver::ScreenInfo info;
ds >> &info;
l->screenInfo.push_back(info);
}
}
return ds;
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
#include "DockRegistry_p.h"
#include "DropArea_p.h"
#include "Frame_p.h"
#include "Utils_p.h"
#include "Logging_p.h"
#include "DropAreaWithCentralFrame_p.h"
#include "multisplitter/MultiSplitterLayout_p.h"
@@ -51,6 +52,7 @@ public:
}
QString name;
QString affinityName;
const MainWindowOptions m_options;
};
@@ -73,6 +75,17 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
Q_ASSERT(widget);
qCDebug(addwidget) << Q_FUNC_INFO << widget;
if (widget->affinityName() != affinityName()) {
qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
<< widget->affinityName() << affinityName();
return;
}
if (widget->options() & DockWidgetBase::Option_NotDockable) {
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << widget;
return;
}
if (d->supportsCentralFrame()) {
dropArea()->m_centralFrame->addWidget(widget);
} else {
@@ -82,7 +95,12 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget)
void MainWindowBase::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBase *relativeTo, AddingOption option)
{
dropArea()->addDockWidget(dw, location, relativeTo, option);
if (dw->options() & DockWidgetBase::Option_NotDockable) {
qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << dw;
return;
}
dropArea()->addDockWidget(dw, location, relativeTo, option);
}
QString MainWindowBase::uniqueName() const
@@ -100,6 +118,26 @@ MultiSplitterLayout *MainWindowBase::multiSplitterLayout() const
return dropArea()->multiSplitterLayout();
}
void MainWindowBase::setAffinityName(const QString &name)
{
if (d->affinityName == name)
return;
if (!d->affinityName.isEmpty()) {
qWarning() << Q_FUNC_INFO
<< "Affinity is already set, refusing to change."
<< "Submit a feature request with a good justification.";
return;
}
d->affinityName = name;
}
QString MainWindowBase::affinityName() const
{
return d->affinityName;
}
void MainWindowBase::setUniqueName(const QString &uniqueName)
{
if (uniqueName.isEmpty())
@@ -122,6 +160,13 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
return false;
}
if (d->affinityName != mw.affinityName) {
qWarning() << Q_FUNC_INFO << "Affinty name changed from" << d->affinityName
<< "; to" << mw.affinityName;
d->affinityName = mw.affinityName;
}
return dropArea()->multiSplitterLayout()->deserialize(mw.multiSplitterLayout);
}
@@ -133,7 +178,10 @@ LayoutSaver::MainWindow MainWindowBase::serialize() const
m.geometry = window()->geometry(); // window() as the MainWindow can be embedded
m.isVisible = isVisible();
m.uniqueName = uniqueName();
m.screenIndex = screenNumberForWidget(this);
m.screenSize = screenSizeForWidget(this);
m.multiSplitterLayout = dropArea()->multiSplitterLayout()->serialize();
m.affinityName = d->affinityName;
return m;
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -100,6 +100,29 @@ public:
///@brief returns the MultiSplitterLayout.
MultiSplitterLayout* multiSplitterLayout() const;
/**
* @brief Sets the affinity name. Dock widgets can only dock into main windows of the same affinity.
*
* By default the affinity is empty and a dock widget can dock into any main window. Usually you
* won't ever need to call this function, unless you have requirements where certain dock widgets
* can only dock into certain main windows. @sa DockWidgetBase::setAffinityName().
*
* Note: Call this function right after creating your main window, before docking any dock widgets
* into a main window and before restoring any layout.
*
* Note: Currently you can only call this function once, to keep the code simple and avoid
* edge cases. This will only be changed if a good use case comes up that requires changing
* affinities multiple times.
*
* @p name The affinity name.
*/
void setAffinityName(const QString &name);
/**
* @brief Returns the affinity name. Empty by default.
*/
QString affinityName() const;
protected:
void setUniqueName(const QString &uniqueName);

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -0,0 +1 @@
#include "../../Config.h"

View File

@@ -0,0 +1 @@
#include "../../DockWidget.h"

View File

@@ -0,0 +1 @@
#include "../../FrameworkWidgetFactory.h"

View File

@@ -0,0 +1 @@
#include "../../LayoutSaver.h"

View File

@@ -0,0 +1 @@
#include "../../MainWindow.h"

View File

@@ -0,0 +1 @@
#include "../../../private/TitleBar_p.h"

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -36,12 +36,15 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QSpinBox>
#include <QMessageBox>
#include <QApplication>
#include <QMouseEvent>
#include <QWindow>
#include <QFileDialog>
#include <QAbstractNativeEventFilter>
#include <QTimer>
#ifdef Q_OS_WIN
# include <Windows.h>
@@ -113,6 +116,25 @@ DebugWindow::DebugWindow(QWidget *parent)
}
});
hlay = new QHBoxLayout();
layout->addLayout(hlay);
button = new QPushButton(this);
auto lineedit = new QLineEdit(this);
lineedit->setPlaceholderText(tr("DockWidget unique name"));
button->setText(QStringLiteral("Show"));
hlay->addWidget(button);
hlay->addWidget(lineedit);
connect(button, &QPushButton::clicked, this, [lineedit] {
auto dw = DockRegistry::self()->dockByName(lineedit->text());
if (dw) {
dw->show();
} else {
QMessageBox::warning(nullptr, QStringLiteral("Could not find"),
QStringLiteral("Could not find DockWidget with name %1").arg(lineedit->text()));
}
});
button = new QPushButton(this);
button->setText(QStringLiteral("Float all visible docks"));
layout->addWidget(button);
@@ -129,8 +151,8 @@ DebugWindow::DebugWindow(QWidget *parent)
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
LayoutSaver saver;
QString message = saver.saveToDisk() ? QStringLiteral("Saved!")
: QStringLiteral("Error!");
QString message = saver.saveToFile(QStringLiteral("layout.json")) ? QStringLiteral("Saved!")
: QStringLiteral("Error!");
qDebug() << message;
});
@@ -139,8 +161,8 @@ DebugWindow::DebugWindow(QWidget *parent)
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
LayoutSaver saver;
QString message = saver.restoreFromDisk() ? QStringLiteral("Restored!")
: QStringLiteral("Error!");
QString message = saver.restoreFromFile(QStringLiteral("layout.json")) ? QStringLiteral("Restored!")
: QStringLiteral("Error!");
qDebug() << message;
});
@@ -186,7 +208,6 @@ DebugWindow::DebugWindow(QWidget *parent)
}
});
button = new QPushButton(this);
button->setText(QStringLiteral("Detach central widget"));
layout->addWidget(button);
@@ -214,30 +235,85 @@ DebugWindow::DebugWindow(QWidget *parent)
button->setText(QStringLiteral("EnsureAnchorsBounded"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
const auto mainWindows = DockRegistry::self()->mainwindows();
if (mainWindows.isEmpty())
return;
mainWindows.at(0)->multiSplitterLayout()->ensureAnchorsBounded();
const auto layouts = DockRegistry::self()->layouts();
for (auto l : layouts)
l->ensureAnchorsBounded();
});
button = new QPushButton(this);
button->setText(QStringLiteral("RedistributeSpace"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
const auto layouts = DockRegistry::self()->layouts();
for (auto l : layouts)
l->redistributeSpace();
});
button = new QPushButton(this);
button->setText(QStringLiteral("resize by 1x1"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
const auto layouts = DockRegistry::self()->layouts();
for (auto l : layouts) {
QWidget *tlw = l->multiSplitter()->window();
tlw->resize(tlw->size() + QSize(1, 1));
}
});
button = new QPushButton(this);
button->setText(QStringLiteral("PositionStaticAnchors()"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
const auto mainWindows = DockRegistry::self()->mainwindows();
if (mainWindows.isEmpty())
return;
mainWindows.at(0)->multiSplitterLayout()->positionStaticAnchors();
const auto layouts = DockRegistry::self()->layouts();
for (auto l : layouts)
l->positionStaticAnchors();
});
button = new QPushButton(this);
button->setText(QStringLiteral("UpdateAnchorFollowing"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [] {
const auto mainWindows = DockRegistry::self()->mainwindows();
if (mainWindows.isEmpty())
const auto layouts = DockRegistry::self()->layouts();
for (auto l : layouts)
l->updateAnchorFollowing();
});
button = new QPushButton(this);
button->setText(QStringLiteral("Raise #0 (after 3s timeout)"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] {
QTimer::singleShot(3000, this, [] {
const auto docks = DockRegistry::self()->dockwidgets();
if (!docks.isEmpty())
docks.constFirst()->raise();
});
});
button = new QPushButton(this);
button->setText(QStringLiteral("Convert old layout to JSON"));
layout->addWidget(button);
connect(button, &QPushButton::clicked, this, [this] {
const QString filename = QFileDialog::getOpenFileName(this);
if (filename.isEmpty())
return;
mainWindows.at(0)->multiSplitterLayout()->updateAnchorFollowing();
QFile f(filename);
if (!f.open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open file" << filename;
return;
}
const QByteArray oldData = f.readAll();
LayoutSaver::Layout savedLayout;
savedLayout.fillFrom(oldData);
const QByteArray jsonData = savedLayout.toJson();
QFile f2(QStringLiteral("%1.json").arg(filename));
if (!f2.open(QIODevice::WriteOnly)) {
qWarning() << "Failed to open file for writing" << filename;
return;
}
f2.write(jsonData);
});
#ifdef Q_OS_WIN
@@ -319,6 +395,6 @@ void DebugWindow::mousePressEvent(QMouseEvent *event)
<< (w ? w->parentWidget() : nullptr) << "; geometry="
<< (w ? w->geometry() : QRect());
m_isPickingWidget->quit();
if (m_isPickingWidget)
m_isPickingWidget->quit();
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,9 @@
#include "ObjectViewer_p.h"
#include <QWidget>
QT_BEGIN_NAMESPACE
class QEventLoop;
QT_END_NAMESPACE
namespace KDDockWidgets {
namespace Debug {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -72,6 +72,11 @@ void DockRegistry::checkSanityAll()
layout->checkSanity();
}
bool DockRegistry::isProcessingAppQuitEvent() const
{
return m_isProcessingAppQuitEvent;
}
DockRegistry *DockRegistry::self()
{
static QPointer<DockRegistry> s_dockRegistry;
@@ -168,6 +173,19 @@ MainWindowBase *DockRegistry::mainWindowByName(const QString &name) const
return nullptr;
}
DockWidgetBase *DockRegistry::dockWidgetForGuest(QWidget *guest) const
{
if (!guest)
return nullptr;
for (DockWidgetBase *dw : m_dockWidgets) {
if (dw->widget() == guest)
return dw;
}
return nullptr;
}
bool DockRegistry::isSane() const
{
QSet<QString> names;
@@ -257,6 +275,26 @@ FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) con
return nullptr;
}
QVector<QWidget *> DockRegistry::topLevels(bool excludeFloatingDocks) const
{
QVector<QWidget *> windows;
windows.reserve(m_nestedWindows.size() + m_mainWindows.size());
if (!excludeFloatingDocks) {
for (FloatingWindow *fw : m_nestedWindows) {
if (fw->isVisible())
windows << fw;
}
}
for (MainWindowBase *m : m_mainWindows) {
if (m->isVisible())
windows << m->topLevelWidget();
}
return windows;
}
void DockRegistry::clear(bool deleteStaticAnchors)
{
for (auto dw : qAsConst(m_dockWidgets)) {
@@ -271,6 +309,31 @@ void DockRegistry::clear(bool deleteStaticAnchors)
<< "; nestedwindows=" << m_nestedWindows.size();
}
void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors)
{
if (affinities.isEmpty()) {
// Just clear everything
clear(deleteStaticAnchors);
return;
}
// empty affinity also matches and will be closed
affinities << QString();
for (auto dw : qAsConst(m_dockWidgets)) {
if (affinities.contains(dw->affinityName())) {
dw->forceClose();
dw->lastPosition()->removePlaceholders();
}
}
for (auto mw : qAsConst(m_mainWindows)) {
if (affinities.contains(mw->affinityName())) {
mw->multiSplitterLayout()->clear(deleteStaticAnchors);
}
}
}
void DockRegistry::ensureAllFloatingWidgetsAreMorphed()
{
for (DockWidgetBase *dw : qAsConst(m_dockWidgets)) {
@@ -281,17 +344,18 @@ void DockRegistry::ensureAllFloatingWidgetsAreMorphed()
bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
{
// This event filter is needed so we sort the floating windows by z-order
// When a FloatingWindow is exposed we put it at the end of the list
if (event->type() != QEvent::Expose)
return false;
if (auto windowHandle = qobject_cast<QWindow*>(watched)) {
if (FloatingWindow *fw = floatingWindowForHandle(windowHandle)) {
// This floating window was exposed
m_nestedWindows.removeOne(fw);
m_nestedWindows.append(fw);
if (event->type() == QEvent::Quit && !m_isProcessingAppQuitEvent) {
m_isProcessingAppQuitEvent = true;
qApp->sendEvent(qApp, event);
m_isProcessingAppQuitEvent = false;
return true;
} else if (event->type() == QEvent::Expose) {
if (auto windowHandle = qobject_cast<QWindow*>(watched)) {
if (FloatingWindow *fw = floatingWindowForHandle(windowHandle)) {
// This floating window was exposed
m_nestedWindows.removeOne(fw);
m_nestedWindows.append(fw);
}
}
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -59,6 +59,10 @@ public:
DockWidgetBase *dockByName(const QString &) const;
MainWindowBase *mainWindowByName(const QString &) const;
/// @brief returns the dock widget that hosts @p guest widget. Nullptr if there's none.
DockWidgetBase *dockWidgetForGuest(QWidget *guest) const;
bool isSane() const;
///@brief returns all DockWidget instances
@@ -83,12 +87,28 @@ public:
///@brief returns the FloatingWindow with handle @p windowHandle
FloatingWindow *floatingWindowForHandle(QWindow *windowHandle) const;
///@brief Returns the list with all visiblye top-level parents of our FloatingWindow and MainWindow instances.
///
/// Typically these are the FloatingWindows and MainWindows themselves. However, since a
/// MainWindow can be embedded into another widget (for whatever reason, like a QWinWidget),
/// it means that a top-level can be something else.
///
/// Every returned widget is either a FloatingWindow, MainWindow, or something that contains a MainWindow.
///
/// If @p excludeFloatingDocks is true then FloatingWindow won't be returned
QVector<QWidget*> topLevels(bool excludeFloatingDocks = false) const;
/**
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors.
* This is called before restoring a layout.
*/
void clear(bool deleteStaticAnchors = false);
/**
* @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors with the specified affinities.
*/
void clear(QStringList affinities, bool deleteStaticAnchors = false);
/**
* @brief Ensures that all floating DockWidgets have a FloatingWindow as a window.
*
@@ -110,11 +130,19 @@ public:
*/
void checkSanityAll();
/**
* @brief Returns whether we're processing a QEvent::Quit
*
* Used internally to know if we should let Qt close a NonClosable dock widget at shutdown time.
*/
bool isProcessingAppQuitEvent() const;
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
explicit DockRegistry(QObject *parent = nullptr);
void maybeDelete();
bool m_isProcessingAppQuitEvent = false;
DockWidgetBase::List m_dockWidgets;
MainWindowBase::List m_mainWindows;
Frame::List m_frames;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -153,19 +153,19 @@ void StatePreDrag::onEntry(QEvent *)
bool StatePreDrag::handleMouseMove(QPoint globalPos)
{
if ((globalPos - q->m_pressPos).manhattanLength() > QApplication::startDragDistance()) {
if (q->m_draggable->dragCanStart(q->m_pressPos, globalPos)) {
Q_EMIT q->manhattanLengthMove();
return true;
}
return true;
return false;
}
bool StatePreDrag::handleMouseButtonRelease(QPoint)
{
Q_EMIT q->dragCanceled();
return true;
return false;
}
StateDragging::StateDragging(DragController *parent)
: StateBase(parent)
{
@@ -197,6 +197,12 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
return true;
}
if (floatingWindow->anyNonDockable()) {
qCDebug(state) << "StateDragging: Ignoring floating window with non dockable widgets";
Q_EMIT q->dragCanceled();
return true;
}
if (q->m_currentDropArea) {
if (q->m_currentDropArea->drop(floatingWindow, globalPos)) {
Q_EMIT q->dropped();
@@ -213,21 +219,36 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos)
bool StateDragging::handleMouseMove(QPoint globalPos)
{
if (!q->m_windowBeingDragged->floatingWindow()) {
FloatingWindow *fw = q->m_windowBeingDragged->floatingWindow();
if (!fw) {
qCDebug(state) << "Canceling drag, window was deleted";
Q_EMIT q->dragCanceled();
return true;
}
if (!q->m_nonClientDrag)
q->m_windowBeingDragged->floatingWindow()->windowHandle()->setPosition(globalPos - q->m_offset);
fw->windowHandle()->setPosition(globalPos - q->m_offset);
if (fw->anyNonDockable()) {
qCDebug(state) << "StateDragging: Ignoring non dockable floating window";
return true;
}
DropArea *dropArea = q->dropAreaUnderCursor();
if (q->m_currentDropArea && dropArea != q->m_currentDropArea)
q->m_currentDropArea->removeHover();
if (dropArea)
dropArea->hover(q->m_windowBeingDragged->floatingWindow(), globalPos);
if (dropArea) {
if (FloatingWindow *targetFw = dropArea->floatingWindow()) {
if (targetFw->anyNonDockable()) {
qCDebug(state) << "StateDragging: Ignoring non dockable target floating window";
return false;
}
}
dropArea->hover(fw, globalPos);
}
q->m_currentDropArea = dropArea;
@@ -469,7 +490,7 @@ QWidgetOrQuick *DragController::qtTopLevelUnderCursor() const
if (auto tl = qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->nestedwindows(), m_windowBeingDragged->floatingWindow()))
return tl;
return qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->mainwindows(), static_cast<MainWindowBase*>(nullptr));
return qtTopLevelUnderCursor_impl(globalPos, DockRegistry::self()->topLevels(/*excludeFloating=*/true), static_cast<QWidget*>(nullptr));
}
#else
// QtQuick:

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,8 @@
#include "FloatingWindow_p.h"
#include "WidgetResizeHandler_p.h"
#include <QApplication>
using namespace KDDockWidgets;
class Draggable::Private
@@ -59,6 +61,11 @@ QWidgetOrQuick *Draggable::asWidget() const
return d->thisWidget;
}
bool Draggable::dragCanStart(QPoint pressPos, QPoint globalPos) const
{
return (globalPos - pressPos).manhattanLength() > QApplication::startDragDistance();
}
WidgetResizeHandler *Draggable::widgetResizeHandler() const
{
return d->widgetResizeHandler;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -59,9 +59,17 @@ public:
* @brief Returns whether point @p p is draggable.
*
* Because simply inheriting from Draggable doesn't mean you can click anywhere to drag.
* @param p is the point where the mouse press occurred
*/
virtual bool isPositionDraggable(QPoint p) const { Q_UNUSED(p) return true; }
/**
* @brief Returns whether a mouse move can start a drag or not.
* The default implementation just checks if the delta is bigger than
* QApplication::startDragDistance().
*/
virtual bool dragCanStart(QPoint pressPos, QPoint globalPos) const;
WidgetResizeHandler *widgetResizeHandler() const;
void setWidgetResizeHandler(WidgetResizeHandler *w);

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@
#include "Config.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
// #include "indicators/AnimatedIndicators_p.h"
#include "WindowBeingDragged_p.h"
@@ -109,6 +110,9 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
return;
}
if (!validateAffinity(dw))
return;
Frame *frame = nullptr;
Frame *relativeToFrame = relativeTo ? relativeTo->frame() : nullptr;
@@ -158,8 +162,22 @@ bool DropArea::contains(DockWidgetBase *dw) const
return dw->frame() && m_layout->contains(dw->frame());
}
QString DropArea::affinityName() const
{
if (auto mw = mainWindow()) {
return mw->affinityName();
} else if (auto fw = floatingWindow()) {
return fw->affinityName();
}
return QString();
}
void DropArea::hover(FloatingWindow *floatingWindow, QPoint globalPos)
{
if (!validateAffinity(floatingWindow))
return;
Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow);
m_dropIndicatorOverlay->setHoveredFrame(frame);
@@ -220,6 +238,8 @@ bool DropArea::drop(FloatingWindow *droppedWindow, QPoint globalPos)
break;
case DropIndicatorOverlayInterface::DropLocation_Center:
qCDebug(hovering) << "Tabbing" << droppedWindow << "into" << acceptingFrame;
if (!validateAffinity(droppedWindow))
return false;
acceptingFrame->addWidget(droppedWindow);
break;
@@ -241,10 +261,16 @@ bool DropArea::drop(QWidgetOrQuick *droppedWindow, KDDockWidgets::Location locat
qCDebug(docking) << "DropArea::addFrame";
if (auto dock = qobject_cast<DockWidgetBase *>(droppedWindow)) {
if (!validateAffinity(dock))
return false;
auto frame = Config::self().frameworkWidgetFactory()->createFrame();
frame->addWidget(dock);
m_layout->addWidget(frame, location, relativeTo);
} else if (auto floatingWindow = qobject_cast<FloatingWindow *>(droppedWindow)) {
if (!validateAffinity(floatingWindow))
return false;
m_layout->addMultiSplitter(floatingWindow->dropArea(), location, relativeTo);
floatingWindow->scheduleDeleteLater();
return true;
@@ -261,3 +287,16 @@ void DropArea::removeHover()
m_dropIndicatorOverlay->setWindowBeingDragged(nullptr);
m_dropIndicatorOverlay->setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
}
template<typename T>
bool DropArea::validateAffinity(T *window) const
{
if (window->affinityName() != affinityName()) {
// Commented the warning, so we don't warn when hovering over
//qWarning() << Q_FUNC_INFO << "Refusing to dock widget with incompatible affinity."
//<< window->affinityName() << affinityName();
return false;
}
return true;
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -68,13 +68,18 @@ public:
bool checkSanity(MultiSplitterLayout::AnchorSanityOption o = MultiSplitterLayout::AnchorSanity_All);
bool contains(DockWidgetBase *) const;
QString affinityName() const;
private:
Q_DISABLE_COPY(DropArea)
friend class Frame;
friend class TestDocks;
friend class DropIndicatorOverlayInterface;
friend class AnimatedIndicators;
template <typename T>
bool validateAffinity(T *) const;
bool m_inDestructor = false;
QString m_affinityName;
DropIndicatorOverlayInterface *m_dropIndicatorOverlay = nullptr;
};
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -87,7 +87,7 @@ public:
}
#endif
FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
FloatingWindow::FloatingWindow(MainWindowBase *parent)
: QWidgetAdapter(parent, KDDockWidgets::usesNativeDraggingAndResizing() ? Qt::Window : Qt::Tool)
, Draggable(this, KDDockWidgets::usesNativeDraggingAndResizing()) // FloatingWindow is only draggable when using a native title bar. Otherwise the KDDockWidgets::TitleBar is the draggable
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
@@ -105,6 +105,13 @@ FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
DockRegistry::self()->registerNestedWindow(this);
qCDebug(creation) << "FloatingWindow()" << this;
#ifdef Q_OS_WIN
# if QT_VERSION < 0x051000
// On Windows with Qt 5.9 (and maybe later but we don't care), the WM_NCALCSIZE isn't being processed unless we explicitly create the window.
// So create it now, otherwise floating dock widgets will show a native title bar until resized.
create();
# endif
#endif
maybeCreateResizeHandler();
@@ -115,25 +122,45 @@ FloatingWindow::FloatingWindow(QWidgetOrQuick *parent)
m_layoutDestroyedConnection = connect(ms, &MultiSplitterLayout::destroyed, this, &FloatingWindow::scheduleDeleteLater);
}
static QWidgetOrQuick* hackFindParentHarder(QWidgetOrQuick *p)
static MainWindowBase* hackFindParentHarder(Frame *frame, MainWindowBase *candidateParent)
{
// TODO: Using a parent helps the floating windows stay in front of the main window always.
// We're not receiving the parent via ctor argument as the app can have multiple-main windows,
// so use a hack here.
// Not quite clear what to do if the app supports multiple main windows though.
if (p)
return p;
if (candidateParent)
return candidateParent;
#ifdef KDDOCKWIDGETS_QTWIDGETS
const MainWindowBase::List windows = DockRegistry::self()->mainwindows();
if (windows.isEmpty())
return nullptr;
if (windows.size() == 1)
if (windows.size() == 1) {
return windows.first();
else {
qWarning() << Q_FUNC_INFO << "There's multiple MainWindows, not sure what to do about parenting";
} else {
const QString affinityName = frame ? frame->affinityName() : QString();
if (affinityName.isEmpty()) {
for (MainWindowBase *window : windows) {
if (window->affinityName().isEmpty())
return window;
}
qWarning() << Q_FUNC_INFO << "No window with empty affinity found";
} else {
for (MainWindowBase *window : windows) {
if (window->affinityName() == affinityName)
return window;
}
qWarning() << Q_FUNC_INFO << "No window with affinity" << affinityName << "found";
}
return windows.first();
}
#else
@@ -142,8 +169,8 @@ static QWidgetOrQuick* hackFindParentHarder(QWidgetOrQuick *p)
#endif
}
FloatingWindow::FloatingWindow(Frame *frame, QWidgetOrQuick *parent)
: FloatingWindow(hackFindParentHarder(parent))
FloatingWindow::FloatingWindow(Frame *frame, MainWindowBase *parent)
: FloatingWindow(hackFindParentHarder(frame, parent))
{
m_disableSetVisible = true;
// Adding a widget will trigger onFrameCountChanged, which triggers a setVisible(true).
@@ -233,6 +260,15 @@ bool FloatingWindow::anyNonClosable() const
return false;
}
bool FloatingWindow::anyNonDockable() const
{
for (Frame *frame : frames()) {
if (frame->anyNonDockable())
return true;
}
return false;
}
bool FloatingWindow::hasSingleFrame() const
{
return frames().size() == 1;
@@ -303,6 +339,12 @@ void FloatingWindow::updateTitleBarVisibility()
m_titleBar->setVisible(visible);
}
QString FloatingWindow::affinityName() const
{
auto frames = this->frames();
return frames.isEmpty() ? QString() : frames.constFirst()->affinityName();
}
void FloatingWindow::updateTitleAndIcon()
{
QString title;
@@ -326,6 +368,13 @@ void FloatingWindow::updateTitleAndIcon()
void FloatingWindow::onCloseEvent(QCloseEvent *e)
{
qCDebug(closing) << "Frame::closeEvent";
if (e->spontaneous() && anyNonClosable()) {
// Event from the window system won't close us
e->ignore();
return;
}
e->accept(); // Accepted by default (will close unless ignored)
Frame::List frames = this->frames();
@@ -353,6 +402,9 @@ LayoutSaver::FloatingWindow FloatingWindow::serialize() const
fw.geometry = geometry();
fw.isVisible = isVisible();
fw.multiSplitterLayout = dropArea()->multiSplitterLayout()->serialize();
fw.screenIndex = screenNumberForWidget(this);
fw.screenSize = screenSizeForWidget(this);
fw.affinityName = affinityName();
auto mainWindow = qobject_cast<MainWindowBase*>(parentWidget());
fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -27,10 +27,14 @@
#include "QWidgetAdapter.h"
#include "LayoutSaver_p.h"
QT_BEGIN_NAMESPACE
class QAbstractNativeEventFilter;
class QWindowStateChangeEvent;
QT_END_NAMESPACE
namespace KDDockWidgets {
class MainWindowBase;
class DropArea;
class Frame;
class MultiSplitterLayout;
@@ -40,8 +44,8 @@ class DOCKS_EXPORT FloatingWindow : public QWidgetAdapter
{
Q_OBJECT
public:
explicit FloatingWindow(QWidgetOrQuick *parent = nullptr);
explicit FloatingWindow(Frame *frame, QWidgetOrQuick *parent = nullptr);
explicit FloatingWindow(MainWindowBase *parent = nullptr);
explicit FloatingWindow(Frame *frame, MainWindowBase *parent = nullptr);
~FloatingWindow() override;
bool deserialize(const LayoutSaver::FloatingWindow &);
@@ -60,6 +64,7 @@ public:
TitleBar *titleBar() const { return m_titleBar; }
bool anyNonClosable() const;
bool anyNonDockable() const;
/**
* @brief checks if this FloatingWindow only has one frame.
@@ -106,8 +111,11 @@ public:
void updateTitleAndIcon();
void updateTitleBarVisibility();
QString affinityName() const;
Q_SIGNALS:
void numFramesChanged();
void windowStateChanged(QWindowStateChangeEvent *);
protected:
#ifdef Q_OS_WIN
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -39,6 +39,7 @@
#include <QTabBar>
#include <QCloseEvent>
#include <QTimer>
#define MARGIN_THRESHOLD 100
@@ -96,18 +97,19 @@ void Frame::updateTitleAndIcon()
}
}
setObjectName(dw->uniqueName());
} else if (currentTabIndex() != -1) {
qWarning() << Q_FUNC_INFO << "Invalid dock widget for frame." << currentTabIndex();
}
}
void Frame::addWidget(DockWidgetBase *dockWidget)
void Frame::addWidget(DockWidgetBase *dockWidget, AddingOption addingOption)
{
insertWidget(dockWidget, m_tabWidget->numDockWidgets()); // append
insertWidget(dockWidget, m_tabWidget->numDockWidgets(), addingOption); // append
}
void Frame::addWidget(Frame *frame)
void Frame::addWidget(Frame *frame, AddingOption addingOption)
{
if (frame->isEmpty()) {
qWarning() << "Frame::addWidget: frame is empty." << frame;
@@ -116,34 +118,46 @@ void Frame::addWidget(Frame *frame)
const auto &docks = frame->dockWidgets();
for (DockWidgetBase *dockWidget : docks)
addWidget(dockWidget);
addWidget(dockWidget, addingOption);
}
void Frame::addWidget(FloatingWindow *floatingWindow)
void Frame::addWidget(FloatingWindow *floatingWindow, AddingOption addingOption)
{
Q_ASSERT(floatingWindow);
for (Frame *f : floatingWindow->frames())
addWidget(f);
addWidget(f, addingOption);
}
void Frame::insertWidget(DockWidgetBase *dockWidget, int index)
void Frame::insertWidget(DockWidgetBase *dockWidget, int index, AddingOption addingOption)
{
qCDebug(addwidget()) << Q_FUNC_INFO << ((void*)this) << "; dockWidget=" << dockWidget << "; oldFrame=" << dockWidget->frame();
qCDebug(addwidget()) << Q_FUNC_INFO << ((void*)this) << "; dockWidget="
<< dockWidget << "; oldFrame=" << dockWidget->frame()
<< "; addingOption=" << addingOption;
Q_ASSERT(dockWidget);
if (contains(dockWidget)) {
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
return;
}
if (m_layoutItem)
dockWidget->addPlaceholderItem(m_layoutItem);
m_tabWidget->insertDockWidget(dockWidget, index);
if (hasSingleDockWidget()) {
Q_EMIT currentDockWidgetChanged(dockWidget);
setObjectName(dockWidget->uniqueName());
if (addingOption == AddingOption_StartHidden) {
dockWidget->close(); // Ensure closed
} else {
if (hasSingleDockWidget()) {
Q_EMIT currentDockWidgetChanged(dockWidget);
setObjectName(dockWidget->uniqueName());
if (!m_layoutItem) {
// When adding the 1st dock widget of a fresh frame, let's give the frame the size
// of the dock widget, so that when adding it to the main window, the main window can
// use that size as the initial suggested size.
resize(dockWidget->size());
}
}
}
connect(dockWidget, &DockWidgetBase::titleChanged, this, &Frame::updateTitleAndIcon);
@@ -312,7 +326,17 @@ DockWidgetBase *Frame::currentDockWidget() const
bool Frame::anyNonClosable() const
{
for (auto dw : dockWidgets()) {
if (dw->options() & DockWidgetBase::Option_NotClosable)
if ((dw->options() & DockWidgetBase::Option_NotClosable) && !DockRegistry::self()->isProcessingAppQuitEvent())
return true;
}
return false;
}
bool Frame::anyNonDockable() const
{
for (auto dw : dockWidgets()) {
if (dw->options() & DockWidgetBase::Option_NotDockable)
return true;
}
@@ -397,6 +421,15 @@ bool Frame::hasTabsVisible() const
return alwaysShowsTabs() || dockWidgetCount() > 1;
}
QString Frame::affinityName() const
{
if (isEmpty()) {
return {};
} else {
return dockWidgetAt(0)->affinityName();
}
}
DockWidgetBase *Frame::dockWidgetAt(int index) const
{
return qobject_cast<DockWidgetBase *>(m_tabWidget->dockwidgetAt(index));
@@ -406,16 +439,19 @@ void Frame::setDropArea(DropArea *dt)
{
if (dt != m_dropArea) {
qCDebug(docking) << "Frame::setDropArea dt=" << dt;
const bool wasInMainWindow = dt && isInMainWindow();
if (m_dropArea)
disconnect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
this, &Frame::updateTitleBarVisibility);
disconnect(m_visibleWidgetCountChangedConnection);
m_dropArea = dt;
if (m_dropArea) {
connect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
this, &Frame::updateTitleBarVisibility);
// We keep the connect result so we don't dereference m_dropArea at shutdown
m_visibleWidgetCountChangedConnection = connect(m_dropArea->multiSplitterLayout(), &MultiSplitterLayout::visibleWidgetCountChanged,
this, &Frame::updateTitleBarVisibility);
updateTitleBarVisibility();
if (wasInMainWindow != isInMainWindow())
Q_EMIT isInMainWindowChanged();
}
}
}
@@ -504,6 +540,9 @@ void Frame::scheduleDeleteLater()
{
qCDebug(creation) << Q_FUNC_INFO << this;
m_beingDeleted = true;
deleteLater();
QTimer::singleShot(0, this, [this] {
// Can't use deleteLater() here due to QTBUG-83030 (deleteLater() never delivered if triggered by a sendEvent() before event loop starts)
delete this;
});
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -71,14 +71,14 @@ public:
LayoutSaver::Frame serialize() const;
///@brief Adds a widget into the Frame's TabWidget
void addWidget(DockWidgetBase *);
void addWidget(DockWidgetBase *, AddingOption = AddingOption_None);
///@overload
void addWidget(Frame *);
void addWidget(Frame *, AddingOption = AddingOption_None);
///@overload
void addWidget(FloatingWindow *floatingWindow);
void addWidget(FloatingWindow *floatingWindow, AddingOption addingOption = AddingOption_None);
///@brief Inserts a widget into the Frame's TabWidget at @p index
void insertWidget(DockWidgetBase *, int index);
void insertWidget(DockWidgetBase *, int index, AddingOption addingOption = AddingOption_None);
///@brief removes a dockwidget from the frame
void removeWidget(DockWidgetBase *);
@@ -164,7 +164,7 @@ public:
FrameOptions options() const { return m_options; }
bool anyNonClosable() const;
bool anyNonDockable() const;
///@brief returns whether there's 0 dock widgets. If not persistent then the Frame will delete itself.
bool isEmpty() const { return dockWidgetCount() == 0; }
@@ -202,11 +202,14 @@ public:
**/
bool hasTabsVisible() const;
QString affinityName() const;
Q_SIGNALS:
void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *);
void numDockWidgetsChanged();
void hasTabsVisibleChanged();
void layoutInvalidated();
void isInMainWindowChanged();
private:
Q_DISABLE_COPY(Frame)
@@ -222,6 +225,7 @@ private:
const FrameOptions m_options;
QPointer<Item> m_layoutItem;
bool m_beingDeleted = false;
QMetaObject::Connection m_visibleWidgetCountChangedConnection;
};
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -35,7 +35,9 @@
#include <QObject>
#include <QMenu>
QT_BEGIN_NAMESPACE
class QStandardItem;
QT_END_NAMESPACE
namespace KDDockWidgets {
namespace Debug {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -51,6 +51,8 @@ TitleBar::TitleBar(FloatingWindow *parent)
{
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateCloseButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateFloatButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
init();
}
@@ -66,7 +68,11 @@ TitleBar::~TitleBar()
bool TitleBar::onDoubleClicked()
{
if (supportsFloatingButton()) {
if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes) && m_floatingWindow) {
// Not using isFloating(), as that can be a dock widget nested in a floating window. By convention it's floating, but it's not the title bar of the top-level window.
toggleMaximized();
return true;
} else if (supportsFloatingButton()) {
onFloatClicked();
return true;
}
@@ -74,6 +80,17 @@ bool TitleBar::onDoubleClicked()
return false;
}
void TitleBar::toggleMaximized()
{
if (!m_floatingWindow)
return;
if (m_floatingWindow->isMaximized())
m_floatingWindow->showNormal();
else
m_floatingWindow->showMaximized();
}
void TitleBar::setTitle(const QString &title)
{
if (title != m_title) {
@@ -138,11 +155,26 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
bool TitleBar::supportsFloatingButton() const
{
if (Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton) {
// Apps having a maximize/restore button traditionally don't have a floating one,
// QDockWidget style only has floating and no maximize/restore.
// We can add an option later if we need them to co-exist
return false;
}
// If we have a floating window with nested dock widgets we can't re-attach, because we don't
// know where to
return !m_floatingWindow || m_floatingWindow->hasSingleFrame();
}
bool TitleBar::supportsMaximizeButton() const
{
if (!(Config::self().flags() & Config::Flag_TitleBarHasMaximizeButton))
return false;
return m_floatingWindow != nullptr;
}
bool TitleBar::hasIcon() const
{
return !m_icon.isNull();
@@ -222,3 +254,8 @@ void TitleBar::onFloatClicked()
makeWindow();
}
}
void TitleBar::onMaximizeClicked()
{
toggleMaximized();
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -31,8 +31,10 @@
#include <QVector>
#include <QIcon>
QT_BEGIN_NAMESPACE
class QHBoxLayout;
class QLabel;
QT_END_NAMESPACE
namespace KDDockWidgets {
@@ -65,9 +67,12 @@ public:
/// There should always be at least 1. If more than 1 then they are tabbed.
DockWidgetBase::List dockWidgets() const;
///@brief returns whether this title bar supports a floating/unfloating button
///@brief returns whether this title bar supports a floating/docking button
bool supportsFloatingButton() const;
///@brief returns whether this title bar supports a maximize/restore button
bool supportsMaximizeButton() const;
///@brief returns whether this title bar has an icon
bool hasIcon() const;
@@ -83,6 +88,8 @@ public:
///@brief getter for m_floatingWindow
const FloatingWindow *floatingWindow() const { return m_floatingWindow; }
virtual void updateCloseButton() {}
Q_SIGNALS:
void titleChanged();
void iconChanged();
@@ -90,8 +97,11 @@ Q_SIGNALS:
protected:
void onCloseClicked();
void onFloatClicked();
void onMaximizeClicked();
void toggleMaximized();
virtual void updateFloatButton() {}
virtual void updateCloseButton() {}
virtual void updateMaximizeButton() {}
// The following are needed for the unit-tests
virtual bool isCloseButtonVisible() const { return true; }

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,9 @@
#include "Config.h"
#include <QApplication>
#include <QScreen>
#include <QWidget>
#include <QWindow>
#ifdef QT_X11EXTRAS_LIB
# include <QtX11Extras/QX11Info>
@@ -65,6 +68,30 @@ inline bool windowManagerHasTranslucency()
return true;
}
inline QSize screenSizeForWidget(const QWidget *w)
{
QWidget *topLevel = w->window();
if (QWindow *window = topLevel->windowHandle()) {
if (QScreen *screen = window->screen()) {
return screen->size();
}
}
return {};
}
inline int screenNumberForWidget(const QWidget *w)
{
QWidget *topLevel = w->window();
if (QWindow *window = topLevel->windowHandle()) {
if (QScreen *screen = window->screen()) {
return qApp->screens().indexOf(screen);
}
}
return -1;
}
};
#endif

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -22,6 +22,7 @@
#include "FloatingWindow_p.h"
#include "TitleBar_p.h"
#include "DragController_p.h"
#include "Config.h"
#include <QEvent>
#include <QMouseEvent>
@@ -266,15 +267,23 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
return *result != 0;
} else if (msg->message == WM_NCLBUTTONDBLCLK) {
// We don't want double click to maximize the window
if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes)) {
// By returning false we accept Windows native action, a maximize.
// We could also call titleBar->onDoubleClicked(); here which will maximize if Flag_DoubleClickMaximizes is set,
// but there's a bug in QWidget::showMaximized() on Windows when we're covering the native title bar, the window is maximized with an offset.
// So instead, use a native maximize which works well
return false;
} else {
// Let the title bar handle it. It will re-dock the window.
if (TitleBar *titleBar = w->titleBar()) {
if (titleBar->isVisible()) { // can't be invisible afaik
titleBar->onDoubleClicked();
if (TitleBar *titleBar = w->titleBar()) {
if (titleBar->isVisible()) { // can't be invisible afaik
titleBar->onDoubleClicked();
}
}
}
return true;
return true;
}
} else if (msg->message == WM_GETMINMAXINFO) {
// Qt doesn't work well with windows that don't have title bar but have native frames.
// When maximized they go out of bounds and the title bar is clipped, so catch WM_GETMINMAXINFO
@@ -282,8 +291,9 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
// According to microsoft docs it only works for the primary screen, but extrapolates for the others
QScreen *screen = QApplication::primaryScreen();
if (w->isMaximized() || !screen || w->windowHandle()->screen() != screen)
if (!screen || w->windowHandle()->screen() != screen) {
return false;
}
DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,9 @@
#include <QPoint>
#include <QDebug>
QT_BEGIN_NAMESPACE
class QMouseEvent;
QT_END_NAMESPACE
namespace KDDockWidgets {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -22,6 +22,7 @@
#include "DropArea_p.h"
#include <QPainter>
#include <QPainterPath>
#include <QState>
#include <QStateMachine>

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -23,7 +23,9 @@
#include "DropIndicatorOverlayInterface_p.h"
QT_BEGIN_NAMESPACE
class QRubberBand;
QT_END_NAMESPACE
namespace KDDockWidgets {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
#include "Separator_p.h"
#include "FrameworkWidgetFactory.h"
#include <QRubberBand>
#include <QApplication>
#include <QDebug>
@@ -45,6 +46,8 @@ Anchor::Anchor(Qt::Orientation orientation, MultiSplitterLayout *multiSplitter,
, m_type(type)
, m_layout(multiSplitter)
, m_separatorWidget(Config::self().frameworkWidgetFactory()->createSeparator(this, multiSplitter->multiSplitter()))
, m_lazyResize(Config::self().flags() & Config::Flag_LazyResize)
, m_lazyResizeRubberBand(m_lazyResize ? new QRubberBand(QRubberBand::Line, multiSplitter->multiSplitter()) : nullptr)
{
multiSplitter->insertAnchor(this);
connect(this, &QObject::objectNameChanged, m_separatorWidget, &QObject::setObjectName);
@@ -252,7 +255,15 @@ void Anchor::setPosition(int p, SetPositionOptions options)
void Anchor::updatePositionPercentage()
{
m_positionPercentage = (position() * 1.0) / m_layout->width();
const int layoutLength = m_layout->length(m_orientation);
m_positionPercentage = (position() * 1.0) / layoutLength;
if (position() > layoutLength) {
// This warning makes the unit-tests fail if some invalid m_positionPercentage ever appears.
// Bug fixed now though.
qWarning() << Q_FUNC_INFO << "Weird position percentage" << m_positionPercentage
<< position() << layoutLength;
}
}
int Anchor::position() const
@@ -500,21 +511,45 @@ int Anchor::cumulativeMinLength(Anchor::Side side) const
(side == Side1 && (m_type & (Type_RightStatic | Type_BottomStatic))))
return 2 * staticAnchorThickness;
}
const CumulativeMin result = cumulativeMinLength_recursive(side);
const int numNonStaticAnchors = result.numItems >= 2 ? result.numItems - 1
: 0;
int r = Anchor::thickness(isStatic()) + Anchor::thickness(true)
+ numNonStaticAnchors*Anchor::thickness(false)
+ result.minLength;
return r;
}
Anchor::CumulativeMin Anchor::cumulativeMinLength_recursive(Anchor::Side side) const
{
const auto items = this->items(side);
int minLength = 0;
CumulativeMin result = { 0, 0 };
for (auto item : items) {
const int itemMin = item->cumulativeMinLength(side, orientation());
minLength = qMax(itemMin, minLength);
Anchor *oppositeAnchor = item->anchorAtSide(side, orientation());
if (!oppositeAnchor) {
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
return {0, 0};
}
CumulativeMin candidateMin = { 0, 0 };
if (!item->isPlaceholder()) {
candidateMin.numItems++;
candidateMin.minLength = item->minLength(orientation());
}
candidateMin += oppositeAnchor->cumulativeMinLength_recursive(side);
if (candidateMin.minLength >= result.minLength) {
result = candidateMin;
}
}
auto map = m_layout->anchorsShouldFollow();
// Dont' use isFollowing() here, because when restoring a placeholder we clear the followers first
const bool willFollow = map.contains(const_cast<Anchor*>(this));
const int thickness = willFollow ? 0 : Anchor::thickness(isStatic());
return thickness + minLength;
return result;
}
void Anchor::setFollowee(Anchor *followee)
@@ -668,6 +703,22 @@ void Anchor::setThickness()
}
}
void Anchor::setLazyPosition(int pos)
{
if (m_lazyPosition != pos) {
m_lazyPosition = pos;
QRect geo = m_separatorWidget->geometry();
if (isVertical()) {
geo.moveLeft(pos);
} else {
geo.moveTop(pos);
}
m_lazyResizeRubberBand->setGeometry(geo);
}
}
int Anchor::position(QPoint p) const
{
return isVertical() ? p.x() : p.y();
@@ -729,10 +780,20 @@ void Anchor::onMousePress()
s_isResizing = true;
m_layout->setAnchorBeingDragged(this);
qCDebug(anchors) << "Drag started";
if (m_lazyResize) {
setLazyPosition(position());
m_lazyResizeRubberBand->show();
}
}
void Anchor::onMouseReleased()
{
if (m_lazyResize) {
m_lazyResizeRubberBand->hide();
setPosition(m_lazyPosition);
}
s_isResizing = false;
m_layout->setAnchorBeingDragged(nullptr);
}
@@ -769,7 +830,11 @@ void Anchor::onMouseMoved(QPoint pt)
m_lastMoveDirection = positionToGoTo < position() ? Side1
: (positionToGoTo > position() ? Side2
: Side_None); // Side_None shouldn't happen though.
setPosition(positionToGoTo);
if (m_lazyResize)
setLazyPosition(positionToGoTo);
else
setPosition(positionToGoTo);
}
void Anchor::onWidgetMoved(int p)
@@ -792,7 +857,8 @@ Anchor *Anchor::deserialize(const LayoutSaver::Anchor &a, MultiSplitterLayout *l
auto anchor = new Anchor(Qt::Orientation(a.orientation), layout, Anchor::Type(a.type));
anchor->setObjectName(a.objectName);
anchor->setGeometry(a.geometry);
anchor->updatePositionPercentage();
anchor->m_positionPercentage = a.positionPercentage;
anchor->setProperty("indexFrom", a.indexOfFrom);
anchor->setProperty("indexTo", a.indexOfTo);
anchor->setProperty("indexFolowee", a.indexOfFollowee);
@@ -829,6 +895,7 @@ LayoutSaver::Anchor Anchor::serialize() const
a.indexOfFrom = allAnchors.indexOf(from());
a.indexOfTo = allAnchors.indexOf(to());
a.indexOfFollowee = followee() ? allAnchors.indexOf(followee()) : -1;
a.positionPercentage = m_positionPercentage;
a.side1Items.clear();
a.side1Items.reserve(this->side1Items().size());

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,10 @@
#include <QRect>
#include <QVector>
QT_BEGIN_NAMESPACE
class QRubberBand;
QT_END_NAMESPACE
namespace KDDockWidgets {
class Item;
@@ -281,7 +285,19 @@ public:
static bool isResizing();
private:
struct CumulativeMin {
int minLength;
int numItems;
CumulativeMin& operator+=(CumulativeMin other) {
minLength += other.minLength;
numItems += other.numItems;
return *this;
}
};
CumulativeMin cumulativeMinLength_recursive(Anchor::Side side) const;
void setThickness();
void setLazyPosition(int);
Q_SIGNALS:
void positionChanged(int pos);
@@ -308,7 +324,7 @@ public:
QPointer<Anchor> m_from;// QPointer just so we can assert. They should never be null.
QPointer<Anchor> m_to;
const Type m_type;
qreal m_positionPercentage = 0.0;
qreal m_positionPercentage = 0.0; // Should be between 0 and 1
// Only set when anchor is moved through mouse. Side1 if going towards left or top, Side2 otherwise.
Side m_lastMoveDirection = Side_None;
@@ -329,6 +345,9 @@ public:
QRect m_geometry;
Anchor *m_followee = nullptr;
QMetaObject::Connection m_followeeDestroyedConnection;
const bool m_lazyResize;
int m_lazyPosition = 0;
QRubberBand *const m_lazyResizeRubberBand;
};
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -233,6 +233,9 @@ void Item::ensureMinSize(Qt::Orientation orientation)
Anchor *anchor1 = anchorGroup().anchorAtSide(Anchor::Side1, orientation);
Anchor *anchor2 = anchorGroup().anchorAtSide(Anchor::Side2, orientation);
anchor1 = anchor1->isFollowing() ? anchor1->endFollowee() : anchor1;
anchor2 = anchor2->isFollowing() ? anchor2->endFollowee() : anchor2;
const int bound1 = d->m_layout->boundPositionForAnchor(anchor1, Anchor::Side1);
const int bound2 = d->m_layout->boundPositionForAnchor(anchor2, Anchor::Side2);
@@ -241,7 +244,7 @@ void Item::ensureMinSize(Qt::Orientation orientation)
const int suggestedDelta1 = qMin(delta, qCeil(delta / 2) + anchor1->thickness() + 1);
const int maxPos1 = bound2 - newLength - anchor1->thickness();
const int newPosition1 = qMax(qMin(maxPos1, anchor1->position() - suggestedDelta1), bound1); // Honour the bound
const int newPosition1 = qMin(anchor1->position(), qMax(qMin(maxPos1, anchor1->position() - suggestedDelta1), bound1)); // Honour the bound
const int newPosition2 = newPosition1 + anchor1->thickness() + newLength; // No need to check bound2, we have enough space afterall
if (!anchor1->isStatic())
@@ -365,17 +368,6 @@ const AnchorGroup &Item::anchorGroup() const
return d->m_anchorGroup;
}
int Item::cumulativeMinLength(Anchor::Side side, Qt::Orientation orientation) const
{
Anchor *oppositeAnchor = anchorAtSide(side, orientation);
if (!oppositeAnchor) {
// Shouldn't happen. But don't assert as this might be being called from a dumpDebug()
qWarning() << Q_FUNC_INFO << "Null opposite anchor";
return 0;
}
return minLength(orientation) + oppositeAnchor->cumulativeMinLength(side);
}
QSize Item::minimumSize() const
{
return isPlaceholder() ? QSize(0, 0)
@@ -448,6 +440,9 @@ void Item::onLayoutRequest() const
if (!d->m_frame || d->m_isPlaceholder)
return; // It's a placeholder, nothing to do.
if (LayoutSaver::restoreInProgress())
return; // we don't even have the anchors yet, nothing to do
if (d->m_frame->geometry() != geometry()) {
// The frame is controlled by the layout, it can't change its geometry on its own.
// Put it back.
@@ -595,14 +590,15 @@ void Item::Private::turnIntoPlaceholder()
m_layout->clearAnchorsFollowing();
AnchorGroup anchorGroup = q->anchorGroup();
auto layout = m_layout; // copy it, since we're deleting 'q', which deletes 'this'
if (anchorGroup.isValid()) {
m_layout->emitVisibleWidgetCountChanged();
layout->emitVisibleWidgetCountChanged();
} else {
// Auto-destruction, which removes it from the layout
delete q;
}
m_layout->updateAnchorFollowing(anchorGroup);
layout->updateAnchorFollowing(anchorGroup);
}
void Item::Private::setIsPlaceholder(bool is)

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -150,8 +150,6 @@ public:
AnchorGroup& anchorGroup();
const AnchorGroup& anchorGroup() const;
int cumulativeMinLength(Anchor::Side, Qt::Orientation orientation) const;
QSize minimumSize() const;
bool isPlaceholder() const;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -171,6 +171,57 @@ bool MultiSplitterLayout::validateInputs(QWidgetOrQuick *widget,
return true;
}
std::pair<int,int> MultiSplitterLayout::boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const
{
const int bound1 = boundPositionForAnchor(anchor1, Anchor::Side1);
const int bound2 = boundPositionForAnchor(anchor2, Anchor::Side2);
if (newPos1 >= bound1 && newPos2 <= bound2) {
// Simplest case, it's bounded.
return { newPos1, newPos2 };
}
if (newPos1 < bound1) {
// the anchor1 is out of bounds
const int bythismuch = bound1 - newPos1;
newPos1 = bound1;
newPos2 = newPos2 + bythismuch;
if (newPos2 > bound2) {
qWarning() << "Adjusted interval still out of bounds. Not enough space. #1"
<< "; newPos1=" << newPos1
<< "; newPos2=" << newPos2
<< "; bounds=" << bound1 << bound2
<< "; anchor1=" << anchor1
<< "; anchor2=" << anchor2
<< "; size=" << size();
}
return { newPos1, newPos2 };
} else if (newPos2 > bound2) {
// the anchor2 is out of bounds
const int bythismuch = newPos2 - bound2;
newPos2 = bound2;
newPos1 = newPos1 - bythismuch;
if (newPos1 < bound1) {
qWarning() << "Adjusted interval still out of bounds. Not enough space. #2"
<< "; newPos1=" << newPos1
<< "; newPos2=" << newPos2
<< "; bounds=" << bound1 << bound2
<< "; anchor1=" << anchor1
<< "; anchor2=" << anchor2
<< "; size=" << size();
}
return { newPos1, newPos2 };
}
return { newPos1, newPos2 };
}
void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame *relativeToWidget, AddingOption option)
{
auto frame = qobject_cast<Frame*>(w);
@@ -267,6 +318,7 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
case Location_OnTop:
direction1Anchor = existingAnchor;
direction2Anchor = newAnchor;
std::tie(posForExistingAnchor, posForNewAnchor) = boundInterval(posForExistingAnchor, existingAnchor, posForNewAnchor, newAnchor);
delta1 = originalExistingAnchorPos - posForExistingAnchor;
delta2 = posForNewAnchor - posForExistingAnchor;
break;
@@ -274,6 +326,7 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
case Location_OnBottom:
direction1Anchor = newAnchor;
direction2Anchor = existingAnchor;
std::tie(posForNewAnchor, posForExistingAnchor) = boundInterval(posForNewAnchor, newAnchor, posForExistingAnchor, existingAnchor);
delta1 = posForExistingAnchor - posForNewAnchor;
delta2 = posForExistingAnchor - originalExistingAnchorPos;
break;
@@ -297,16 +350,13 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
existingAnchor->setPosition(posForExistingAnchor);
}
// If you drop a 100px in the middle of a layout, it will steal some space from the left widgets
// and still some space from the right ones. delta1 is the space stolen at the left
// delta2 is the space stolen at the right. The sum of delta1+delta2 is the size of the widget
// (plus the splitter). Then we propagate the resize, so that all widgets chip in and get smaller
// to make room for ours.
propagateResize(delta1, direction1Anchor, /*direction*/ Anchor::Side1);
propagateResize(delta2, direction2Anchor, /*direction*/ Anchor::Side2);
// Make sure not just the side1/side2 adjacent widgets are contributing space for our new widget
// the ones adjacents to the adjacents (recursive) must also give.
// The code would work fine without this, it's just that it wouldn't look fair.
propagateResize(delta1, direction1Anchor, Anchor::Side1);
propagateResize(delta2, direction2Anchor, Anchor::Side2);
}
if (newAnchor) {
// Also ensure the widget has a minimum size in the other direction. So, when adding to
// left/right, it will still have its minimum height honoured, and vice-versa.
@@ -343,8 +393,9 @@ void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame
addItems_internal(ItemList{ item });
}
m_addingItem = false;
updateAnchorFollowing();
m_addingItem = false;
maybeCheckSanity();
}
@@ -451,8 +502,10 @@ static Anchor::List removeSmallestPath(QVector<Anchor::List> &paths)
void MultiSplitterLayout::propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction)
{
Q_ASSERT(delta >= 0);
if (delta == 0 || fromAnchor->isStatic())
if (delta < 0)
qWarning() << Q_FUNC_INFO << "Invalid delta" << delta << fromAnchor << direction;
if (delta <= 0 || fromAnchor->isStatic())
return;
QVector<Anchor::List> paths;
@@ -551,6 +604,19 @@ void MultiSplitterLayout::ensureItemsMinSize()
}
}
QString MultiSplitterLayout::affinityName() const
{
if (auto ms = multiSplitter()) {
if (auto mainWindow = ms->mainWindow()) {
return mainWindow->affinityName();
} else if (auto fw = ms->floatingWindow()) {
return fw->affinityName();
}
}
return QString();
}
void MultiSplitterLayout::addMultiSplitter(MultiSplitter *sourceMultiSplitter,
Location location,
Frame *relativeTo)
@@ -668,6 +734,9 @@ QPair<int, int> MultiSplitterLayout::boundPositionsForAnchor(Anchor *anchor) con
}
}
if (anchor->isFollowing())
anchor = anchor->endFollowee();
const int minSide1Length = anchor->cumulativeMinLength(Anchor::Side1);
const int minSide2Length = anchor->cumulativeMinLength(Anchor::Side2);
const int length = anchor->isVertical() ? width() : height();
@@ -745,6 +814,8 @@ MultiSplitterLayout::Length MultiSplitterLayout::availableLengthForDrop(Location
break;
}
anchor = anchor->isFollowing() ? anchor->endFollowee() : anchor;
const int minForAlreadyOccupied1 = anchor->cumulativeMinLength(Anchor::Side1) - anchor->thickness(); // TODO: Check if this is correct, we're discounting the anchor twice
const int minForAlreadyOccupied2 = anchor->cumulativeMinLength(Anchor::Side2) - anchor->thickness();
@@ -966,7 +1037,10 @@ void MultiSplitterLayout::dumpDebug() const
{
Q_EMIT aboutToDumpDebug();
qDebug() << Q_FUNC_INFO << "m_size=" << m_size
<< "; minimumSize=" << minimumSize();
<< "; minimumSize=" << minimumSize()
<< "; parentWidget.size=" << multiSplitter()->size()
<< "; window=" << multiSplitter()->window()
<< "; window.size=" << multiSplitter()->window()->size();
qDebug() << "Items:";
for (auto item : items()) {
@@ -1000,7 +1074,8 @@ void MultiSplitterLayout::dumpDebug() const
<< "; isFollowing=" << anchor->isFollowing()
<< "; followee=" << anchor->followee()
<< "; from=" << ((void*)anchor->from())
<< "; to=" << ((void*)anchor->to());
<< "; to=" << ((void*)anchor->to())
<< "; positionPercentage=" << anchor->positionPercentage();
}
qDebug() << "Num Frame:" << Frame::dbg_numFrames();
@@ -1016,6 +1091,13 @@ void MultiSplitterLayout::positionStaticAnchors()
m_rightAnchor->setPosition(width() - m_rightAnchor->thickness());
}
void MultiSplitterLayout::redistributeSpace()
{
positionStaticAnchors();
redistributeSpace_recursive(m_leftAnchor, 0);
redistributeSpace_recursive(m_topAnchor, 0);
}
void MultiSplitterLayout::redistributeSpace(QSize oldSize, QSize newSize)
{
positionStaticAnchors();
@@ -1283,7 +1365,8 @@ bool MultiSplitterLayout::checkSanity(AnchorSanityOption options) const
if ((options & AnchorSanity_WidgetInvalidSizes) && !item->isPlaceholder()) {
if (item->width() <= 0 || item->height() <= 0) {
dumpDebug();
qWarning() << "Invalid size for widget" << item << item->size() << "; isPlaceholder=" << item->isPlaceholder();
qWarning() << "Invalid size for widget" << item << item->size() << "; isPlaceholder=" << item->isPlaceholder()
<< "; minSize=" << item->minimumSize();
return false;
}
}
@@ -1512,6 +1595,7 @@ void MultiSplitterLayout::restorePlaceholder(Item *item)
Q_ASSERT(anchorGroup.isStaticOrFollowsStatic());
anchorGroup.updateItemSizes();
maybeCheckSanity();
item->endBlockPropagateGeo();
return;
}
@@ -1553,27 +1637,6 @@ void MultiSplitterLayout::restorePlaceholder(Item *item)
const int boundPosition2 = side2Anchor->isStatic() ? side2Anchor->position()
: boundPositionForAnchor(side2Anchor, Anchor::Side2);
// Double check the available space again, for sanity
if (!anchorGroup.hasAvailableSizeFor(newSize, orientation)) {
qWarning() << "There's not enough space: bound2=" << boundPosition2
<< "; bound1=" << boundPosition1
<< "; newSize=" << newSize
<< "; anchorGroup.available" << anchorGroup.availableSize()
<< "; widgetMinSize=" << widgetMinSize
<< "; newspace=" << boundPosition2 - boundPosition1 - side1Anchor->thickness()
<< "; available_old=" << availableSize
<< "; available_new=" << this->availableSize()
<< "; anchors=" << side1Anchor << side2Anchor
<< "; oldPos1=" << oldPosition1
<< "; oldPos2=" << oldPosition2
<< "; thickness=" << side1Anchor->thickness() << side2Anchor->thickness()
<< "; isFollowing=" << side1Anchor->isFollowing() << side2Anchor->isFollowing()
<< "; static=" << side1Anchor->isStatic() << side2Anchor->isStatic()
<< "; size=" << m_size
<< "; m_minSize=" << m_minSize;
return;
}
const int newLength = anchorFollowingInwards->isVertical() ? newSize.width() : newSize.height();
// Let's try that each anchor contributes 50%, so that the widget appears centered
const int suggestedLength1 = qMin(newLength, qCeil(newLength / 2) + side1Anchor->thickness() + 1);
@@ -1791,7 +1854,7 @@ void MultiSplitterLayout::updateAnchorFollowing(const AnchorGroup &groupBeingRem
}
}
if (doShift)
if (doShift && !anchorToShift->isFollowing())
anchorToShift->setPosition(newPosition);
}

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -442,12 +442,14 @@ private:
friend class Anchor;
friend class TestDocks;
friend class KDDockWidgets::Debug::DebugWindow;
friend class LayoutSaver;
struct AnchorBounds {
Anchor *side1;
Anchor *side2;
};
std::pair<int,int> boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const;
void blockItemPropagateGeo(bool block);
/**
@@ -549,6 +551,7 @@ private:
* When this MultiSplitter is resized, it gives or steals the less/extra space evenly through
* all widgets.
**/
void redistributeSpace();
void redistributeSpace(QSize oldSize, QSize newSize);
void redistributeSpace_recursive(Anchor *fromAnchor, int minAnchorPos);
@@ -561,11 +564,13 @@ private:
/**
* Called by addWidget().
* If you drop a 100px in the middle of a layout, it will steal some space from the left widgets
* and still some space from the right ones. delta1 is the space stolen at the left
* delta2 is the space stolen at the right. The sum of delta1+delta2 is the size of the widget
* (plus the splitter). Then we propagate the resize, so that all widgets chip in and get smaller
* to make room for ours.
*
* When adding a widget to a layout, it will steal space from the widgets on the left (or top) (@p direction being Anchor::Side1),
* and from the widgets on the right (or bottom) (@p direction being Anchor::Side2).
*
* @param delta the amount of space we're stealing in the specified side
* @param fromAnchor The anchor we're starting from
* @param direction if we're going left/top (Side1) or right/bottom (Side2)
*/
void propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction);
@@ -583,6 +588,8 @@ private:
bool isRestoringPlaceholder() const { return m_restoringPlaceholder; }
bool isAddingItem() const { return m_addingItem; }
QString affinityName() const;
MultiSplitter *const m_multiSplitter;
Anchor::List m_anchors;

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,9 @@
#include "DockWidgetBase.h"
QT_BEGIN_NAMESPACE
class QCloseEvent;
QT_END_NAMESPACE
namespace KDDockWidgets {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2018-2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2018-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,6 +1,6 @@
/* This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify
@@ -36,7 +36,9 @@
#include <QObject>
#include <QCloseEvent>
QT_BEGIN_NAMESPACE
class QWindow;
QT_END_NAMESPACE
namespace KDDockWidgets {

View File

@@ -1,7 +1,7 @@
/*
This file is part of KDDockWidgets.
Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Copyright (C) 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
Author: Sérgio Martins <sergio.martins@kdab.com>
This program is free software; you can redistribute it and/or modify

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