Compare commits

...

401 Commits

Author SHA1 Message Date
Giuseppe D'Angelo
7a2ffa030b Frame: stop using "virtual" signals
They don't work as expected across DLL boundaries on MSVC: connecting
to Frame::isFocusedChanged using the PMF syntax fails.

The reason has to do with how MSVC implements pointers to virtual
function members: they are "regular" function pointers to a DLL-local
stub that implements the virtual call. For that reason, a connect()
happening in client code would generate a pointer for the signal that
doesn't compare equal to the same pointer generated from inside KDDW;
and moc-generated code relies on this comparison to find the index of
the signal.

Just avoid the whole thing: rename the non-signal virtual call into a
"callback", and reimplement it in the first QObject subclass to turn it
into a proper signal.
2020-12-22 11:03:20 +01:00
Sergio Martins
dd8f46880f qtquick: Implement TabBarQuick::tabAt()
Also redirect the mouse press event to our C++ counterpart.
2020-12-21 17:09:55 +00:00
Sergio Martins
e9159a08bd Fix build when usign installed TabWidget_p.h 2020-12-21 13:31:10 +00:00
Sergio Martins
cf89891e4f Merge branch '1.2' 2020-12-21 12:43:31 +00:00
Sergio Martins
1cc8824f84 Workaround shiboken crash, found by Milian 2020-12-21 12:41:09 +00:00
Sergio Martins
b73c526c57 qtquick: Move the event redirector to the base class
Instead of having it in TitleBarQuick.
It will be needed by the TabWidget too
2020-12-20 17:12:44 +00:00
Sergio Martins
add577c46f qtquick: Fix title not changing when we changed current tab
added unit-test too
2020-12-20 16:30:25 +00:00
Sergio Martins
9ddd65ea71 qtquick: tidy some connects 2020-12-20 15:37:17 +00:00
Sergio Martins
b15afa9231 Move TabWidget instantiation to base class Frame
Instead of being instantiated by both FrameQuick and FrameWidget
2020-12-20 14:21:39 +00:00
Allen Winter
f8dfde2784 README.md - add a "Using" section 2020-12-20 08:27:00 -05:00
Sergio Martins
6e0898a31e Remove some methods from TabWidgetQuick
Reuse more from base class.
2020-12-20 13:19:07 +00:00
Sergio Martins
8b98dafba2 TabBar::numDockWidgets() doesn't need to be virtual 2020-12-20 12:32:50 +00:00
Sergio Martins
a123437d03 qtquick: Implement the TabWidget backend
Not yet connected to the GUI.
2020-12-20 12:06:29 +00:00
Sergio Martins
616c871da7 qtquick: initialize the tab widget via the factory 2020-12-20 11:25:50 +00:00
Sergio Martins
925c9725b3 qtquick: Build TabBarQuick too 2020-12-20 01:09:02 +00:00
Sergio Martins
1a173a7c72 Add a TabWidgetQuick.cpp
Doesn't do much yet
2020-12-20 01:02:55 +00:00
Sergio Martins
a959da46db qtquick: Build TabWidget.cpp too
Not being used yet though.
2020-12-20 01:01:20 +00:00
Sergio Martins
a9c70d086c qtquick: Move TabWidget_p.h out of widgets/
To be shared with qtquick
2020-12-20 00:07:07 +00:00
Sergio Martins
48bacb2811 build-all: Add option to run the tests 2020-12-19 23:30:09 +00:00
Sergio Martins
6b846fcdff qt6: Use more 'auto' to fix some Wconversion errors 2020-12-19 22:54:06 +00:00
Sergio Martins
fdc97fecaf build-all: Don't test the unity variations by default 2020-12-19 22:16:29 +00:00
Sergio Martins
86983d58f5 Merge branch '1.2' 2020-12-19 20:05:19 +00:00
Sergio Martins
58f7edb0bc build-all: Build with unity turned off too
As it often finds builds failures to do missing includes
2020-12-19 20:00:05 +00:00
Sergio Martins
302bbeceb0 Merge branch '1.2' 2020-12-19 19:02:46 +00:00
Sergio Martins
cd9e16398c qtquick: Fix the build for Qt6
And added cmake presets for Qt6
2020-12-19 18:59:33 +00:00
Sergio Martins
1e1ea8db34 qtquick: fix build 2020-12-19 18:04:54 +00:00
Sergio Martins
7ac3ea1fcf Merge branch '1.2' 2020-12-19 17:52:52 +00:00
Sergio Martins
3f661d0322 Add dev-scripts/build-all.dart
This script builds all our supported configurations.
Useful if you don't have access to KDAB CI, or if you don't want
to wait for the results.
2020-12-19 17:51:29 +00:00
Sergio Martins
8dad079df5 Add a CMakePresets.json
Contains the configrations we support:
    QtWidgets, QtQuick, static, Python and developer-build
2020-12-19 16:07:49 +00:00
Sergio Martins
1727e50489 Merge branch '1.2' 2020-12-19 15:44:59 +00:00
Sergio Martins
1ab9688f58 Fix Python build 2020-12-19 15:44:44 +00:00
Sergio Martins
613b1b8524 Allow the user to change the absolute min and max widget sizes.
It's not hardcoded anymore
2020-12-19 15:34:37 +00:00
Sergio Martins
ae5edc9ebf Added README-troubleshooting
Fixes issue #114
2020-12-19 14:59:40 +00:00
Sergio Martins
d0c15b5da2 Add missing include 2020-12-19 14:59:04 +00:00
Sergio Martins
ccbd15c922 Merge branch '1.2' 2020-12-19 14:12:10 +00:00
Sergio Martins
7b9673e4e2 Shrink the overlay popup when we shrink the main window
Should be enough for issue #118
2020-12-19 14:10:01 +00:00
Sergio Martins
7fbcbbacdf Preserve overlay size when resizing main window
For issue #118
2020-12-19 14:09:27 +00:00
Sergio Martins
d1f48b0685 Save the last overlayed size
We might want to restore it
2020-12-19 14:09:27 +00:00
Allen Winter
9ee6b91f61 Merge branch '1.2' 2020-12-18 13:04:02 -05:00
Allen Winter
e6c89c0564 setup for 1.2.1 2020-12-18 13:02:49 -05:00
Sergio Martins
8b97088c70 Fix QtQuick build 2020-12-18 17:39:24 +00:00
Sergio Martins
e5809c06c7 Enable resizing the overlayed dock widgets
Fixed WidgetResizeHandler to not allow it to resize the overlay
bigger than its parent

For issue #116

The size isn't remembered though. Next time you open the overlay
it will have the previous size.
2020-12-18 17:15:54 +00:00
Sergio Martins
23b69d4e9e Fix overlay disappearing when we were resizing
We allow the user to click 4px outside of the overlay for better
resizing. But we also had code that would hide the overlay if we
clicked elsewhere. So these two requirements were in conflict.

So, before hidding, check if the frame accepted the press.

Also for issue #118
2020-12-18 16:02:35 +00:00
Sergio Martins
0f468033fb Commit the code needed for resizing overlays/popups
Still not happy with it, so the actual part that enables it
is commented out

Also for issue #118
2020-12-18 15:35:43 +00:00
Sergio Martins
468074cda8 Fix min/max not being respected when resizing overlay popup
While the min size wasn't set the min size hint was.
Use our wrapper which honours the size hint.

Also for isssue #118
2020-12-18 15:32:43 +00:00
Sergio Martins
7acbe0c62e Make the WidgetResizeHandler margin public 2020-12-18 15:23:52 +00:00
Sergio Martins
2c1a70280a WidgetResizeHandler: Fix the geometry calculation for non-toplevels 2020-12-18 15:20:58 +00:00
Sergio Martins
d2fa5efafe Add Utils::globalGeometry() 2020-12-18 14:29:44 +00:00
Sergio Martins
a437bc5d07 Minor WidgetResizeHandler cleanups 2020-12-18 12:11:28 +00:00
Sergio Martins
2447bb12c8 WidgetResizeHandler: Dont' accept the release if we're not resizing 2020-12-18 11:58:08 +00:00
Sergio Martins
1082d5e8e1 Added WidgetResizeHandler::setAllowedResizeSides() 2020-12-18 10:43:36 +00:00
Sergio Martins
3b1ac9a2d0 Merge branch '1.2' 2020-12-17 20:38:49 +00:00
Sergio Martins
4635405fb5 Allow WidgetResizeHandler to act on global event filter
Needed for widgets which are not top-levels
2020-12-17 19:47:19 +00:00
Sergio Martins
c481875e55 Fix WidgetResizeHandler::cursorPosition() for negative positions
Usually we don't get negative relative positions, since we're
tracking top-level widgets which wouldn't receive exterior mouse
events to begin with.
But we want the WidgetResizeHandler to work for embedded widgets
too, for example for the sidebar overlays.
2020-12-17 19:47:19 +00:00
Sergio Martins
62f50f9458 Minor coding style 2020-12-17 19:47:19 +00:00
Sergio Martins
c86252665c Make WidgetResizeHandler::mouseMoveEvent() return bool
Returns true if it consumed the event
2020-12-17 19:47:19 +00:00
Sergio Martins
d141863ffb Make a method private 2020-12-17 19:47:19 +00:00
Sergio Martins
e99b9678e4 Add WidgetResizeHandler::restoreMouseCursor() 2020-12-17 19:47:19 +00:00
Sergio Martins
e0a034748d Factor out setting the mouse cursor
We'll soon also need to call qApp's set cursor
2020-12-17 19:47:19 +00:00
Sergio Martins
c3377431cc docs++ 2020-12-17 19:47:19 +00:00
Allen Winter
2fb70009cc OBS - updates 2020-12-17 14:40:16 -05:00
Allen Winter
289ddffbe2 prep for 1.2.0 release 2020-12-17 14:40:16 -05:00
Sergio Martins
54738bbea2 Simplify WidgetResizeHandler::cursorPosition() impl 2020-12-17 18:51:40 +00:00
Sergio Martins
5c6eb352d6 Use the CursorPosition enum as flags 2020-12-17 18:46:31 +00:00
Sergio Martins
f657399ef8 Make enum public so we can use Q_ENUM 2020-12-17 18:44:48 +00:00
Sergio Martins
be0f72ca25 Remove unneeded method 2020-12-17 17:05:55 +00:00
Sergio Martins
31598d8334 Make a variable private 2020-12-17 16:59:21 +00:00
Sergio Martins
5e64463a8c Remove some Item_p.h/multisplitter includes
that's private API for the layouts, don't want them exposed higher
in the stack
2020-12-17 13:48:50 +00:00
Sergio Martins
27ffb64eb4 Merge branch '1.2' 2020-12-17 12:45:33 +00:00
Sergio Martins
c4ffe10e12 Fix non unity build 2020-12-17 12:24:12 +00:00
Sergio Martins
efcad6d2be Update ChangeLog 2020-12-17 11:53:12 +00:00
Sergio Martins
c7dc7bdb3d Bump so version on master 2020-12-17 11:15:42 +00:00
Sergio Martins
b6925c928a Clear sidebars before restoring them
If restoring a state without a sidebar, the existing sidebars
should hide

Fixes issue #116
2020-12-17 10:51:16 +00:00
Sergio Martins
75368d334a Don't restore the popup/overlay state
popups are perishable
2020-12-17 10:15:39 +00:00
Allen Winter
d39ce8f66e tests/Testing.cpp - include <QTest>, not <QtTest> 2020-12-16 17:10:07 -05:00
Allen Winter
805ea41d8a Rubberband_quick.h, RubberBandQuick.h - adjust include guard 2020-12-16 17:04:06 -05:00
Allen Winter
d7ed2494be CMakeLists.txt - add documentation for the new options 2020-12-16 17:01:47 -05:00
Allen Winter
0990337cdc examples/qtquick/CMakeLists.txt - add license+copyright header 2020-12-16 16:51:03 -05:00
Allen Winter
e41cdb6f78 various - fix misspellings 2020-12-16 16:49:22 -05:00
Sergio Martins
666673a251 Added a comment 2020-12-16 18:46:56 +00:00
Sergio Martins
34cb2ae5f0 Restore the SideBars when restoring a layout
For issue #116
2020-12-16 18:27:29 +00:00
Sergio Martins
51d8c2ff3e tests: Group the "Save/restore" tests together 2020-12-16 18:15:06 +00:00
Sergio Martins
aa9f50d0f4 Serialize the SideBar to JSON too
Part 1 of 2 for issue #116
2020-12-16 17:58:05 +00:00
Sergio Martins
b7c36a2ec9 Stabilize a test with 5.9
Minor differences in the frame's min-sizes, no point in spending
more time on such old Qt version just so tests pass.
2020-12-16 17:24:33 +00:00
Sergio Martins
e18fb7ffd8 Remove unneeded private moc include
Doesn't play nice with unity builds
2020-12-16 15:48:32 +00:00
Sergio Martins
96439339c7 cmake: Add a missing QtQuick header to the source list 2020-12-16 15:46:31 +00:00
Sergio Martins
04d418b2e9 same for QtQuick 2020-12-16 15:42:48 +00:00
Sergio Martins
c04d856322 cmake: Add the header files to the source list too
So they are automoced with unity build
2020-12-16 15:40:25 +00:00
Sergio Martins
1c41c33f33 Merge the two qrc files that are only for QtQuick 2020-12-16 11:53:36 +00:00
Sergio Martins
1dae128511 Fix the static build
The other library qrc is only needed for QtQuick
2020-12-16 11:41:15 +00:00
Sergio Martins
eab80c0545 Fix a -Wmismatched-tag warning 2020-12-16 10:54:47 +00:00
Sergio Martins
288c3df30b Add missing include 2020-12-15 23:11:52 +00:00
Sergio Martins
e8728e8a35 qtquick: Fix the qrc not being included 2020-12-15 23:07:11 +00:00
Sergio Martins
94bfc4f2d1 qtquick: Fix build 2020-12-15 23:02:13 +00:00
Sergio Martins
d2da07508a Replaced an include with a fwd decl 2020-12-15 22:58:10 +00:00
Sergio Martins
7082e40cd5 Fix non-developer build
These exports are needed
2020-12-15 21:56:22 +00:00
Sergio Martins
2c8bbd10f7 Remove the export macros from the old library
We now use only 1 library, so use the same export macros
2020-12-15 21:41:10 +00:00
Sergio Martins
ed8a401fe8 Meld kddockwidgets__multisplitter into kddockwidgets lib
It's overkill to have it in a separate library.
Simplifies deployment.
Speeds up build, as it was breaking paralellism.
2020-12-15 21:00:41 +00:00
Sergio Martins
72428075f0 Fix -Wshadow warnings 2020-12-15 20:38:20 +00:00
Sergio Martins
562db7c812 qtquick: Fix build
Missing include guards
2020-12-15 13:38:35 +00:00
Sergio Martins
3ee15d1307 Give python a bit of help building KDDW 2020-12-15 12:15:23 +00:00
Sergio Martins
cf232dda34 Enable Unity Builds unconditionally
Projects using KDDW won't need to modify KDDW, so there's no impact
on incremental builds
2020-12-15 11:13:41 +00:00
Sergio Martins
bfeac02a45 cmake: Make KDDW build faster with a unity build 2020-12-12 15:18:39 +00:00
Sergio Martins
22f4c36eb0 cmake: remove duplicate set of qtquick/qtwidget cmake options
Let's simply things and only use one set. If the top-level requires
QtQuick then so will multisplitter. It's not mix-and-match.
2020-12-12 14:11:26 +00:00
Allen Winter
3dc56ea4ef Merge branch '1.1' 2020-12-12 08:20:10 -05:00
Allen Winter
cbedc06e97 various - prep for 1.1.1 release 2020-12-11 16:56:04 -05:00
Sergio Martins
8f85f28ed4 Merge branch '1.1' 2020-12-11 16:44:07 +00:00
Sergio Martins
b5c2757874 Updated Changelog with v1.1.1 2020-12-11 16:36:37 +00:00
Sergio Martins
46f57ffbbc Merge branch '1.1' 2020-12-10 21:39:54 +00:00
Sergio Martins
fb6e2afd7b Windows: Don't start a drag when it's a resize
There were a few pixels where you could click where, very near the
border where it would start a drag while it shouldn't.

The drag state machine then was in an invalid state which could lead to
a crash.

Fixes issue #110
2020-12-10 21:16:42 +00:00
Sergio Martins
f74eb73326 qt6: Use only one CMake option for Qt6
We still had OPTION_QT6 leftovers
2020-12-09 14:09:20 +00:00
Sergio Martins
ceb796d807 qt6: Don't set the HDPI attributes
They don't do anything on Qt6, but warn.
2020-12-09 12:23:17 +00:00
Sergio Martins
9a9488117c Add a State dtor, fixes a -Wweak-vtables warning 2020-12-09 11:13:55 +00:00
Sergio Martins
e55d75ba98 qt6: Fix build on Windows 2020-12-08 23:21:51 +00:00
Sergio Martins
742763e6e5 qt6: Remove unneeded Qt5Test find
It's already found in the root CMakeLists.txt
2020-12-08 22:56:59 +00:00
Sergio Martins
d7242f17b7 examples: Add AUTORCC
Fixes standalone examples not showing images
2020-12-08 22:36:23 +00:00
Sergio Martins
eba395dc4c Fix build 2020-12-08 22:31:21 +00:00
Sergio Martins
89635d8b98 qt6: build is fixed now 2020-12-08 21:47:35 +00:00
Sergio Martins
f475312f11 qt6: Port away from QStateMachine
QStateMachine was moved to qtsxml module, which is not ported to
Qt6 yet.

We use QStateMachine for the semantics it gives us, not because of
its implementation. The implementation is trivial, so do it outselves.
We used very little from QStateMachine.
2020-12-08 19:55:52 +00:00
Sergio Martins
9f604829dd qt6: Use AUTORCC instead of qt5_add_resources
As this works with both Qt5 and Qt6
2020-12-08 18:11:34 +00:00
Sergio Martins
85f829f97a qt6: Remove an hardcoded Qt5 lib 2020-12-08 16:38:08 +00:00
Sergio Martins
e3bb0b3d48 qt6: Link to StateMachine 2020-12-08 16:36:00 +00:00
Sergio Martins
22c0b6f9b6 qt6: Fix build of FloatingWindow.cpp
QWidget::nativeEvent() changed signature
2020-12-08 16:33:35 +00:00
Sergio Martins
200cb7aded qtquick: Fix build with gcc 2020-12-08 15:06:21 +00:00
Sergio Martins
4999fdd819 qtquick: Remove the native child widget case
This can only happen for QtWidgets. With QtQuick all items are
alien, never native
2020-12-06 15:48:31 +00:00
Sergio Martins
76aca4139a Minor cleanup 2020-12-05 23:37:56 +00:00
Sergio Martins
64276cb872 qtquick: Fix clicking on close button of floating window
Since we're using native events on windows we were using the whole
title bar for dragging, but obviously the buttons don't serve for
window dragging
2020-12-05 23:28:04 +00:00
Sergio Martins
aad63d117b qtquick: Enable aero-snap on Windows
The events simply need to be forward from QWindow to the FloatingWindow.
That happens automatically for QWindow/QWidget, but not with
QWindow/QQuickItem, so add this behaviour to QtQuick too, so it behaves
the same as QtWidgets and we have a single code path for aero snap
2020-12-05 15:20:26 +00:00
Sergio Martins
bd66af0a9f Remove unused logging categories 2020-12-05 14:29:13 +00:00
Sergio Martins
a21d80279e qtquick: Fix background of indicator being black on macOS 2020-12-03 19:15:34 +00:00
Sergio Martins
4347aefc8f Fix a -Wweak-vtable warning on macOS 2020-12-03 18:45:08 +00:00
Sergio Martins
b6341154fb qtquick: Fix a test
The tests are not prepared for the min-size of the floating window
to change (QLayout helped with that). We need to write some kind
of constraint propagation first, until then we use a static
size for the non-contents
2020-12-03 18:24:26 +00:00
Sergio Martins
16f6692b79 qtquick: Add an example of how to instantiate a dock widget from QML
Instead of using C++
2020-12-03 14:58:59 +00:00
Sergio Martins
6c1026956a qtquick: titlebar's height is reduced to 0 when invisible
otherwise we see an empty space when it's not visible
2020-12-02 23:33:04 +00:00
Sergio Martins
97679abb02 qtquick: silence warning
not worth it
2020-12-02 23:29:15 +00:00
Sergio Martins
e173a4cfff qtquick: Also allow for custom titlebar for the Frame 2020-12-02 23:24:28 +00:00
Sergio Martins
26971ef1e2 qtquick: Add an example of a custom title bar 2020-12-02 23:20:44 +00:00
Sergio Martins
d7bde47105 qtquick: Don't hardcode the title bar height in C++
It's now in QML and the user is allowed to change it
2020-12-02 22:58:11 +00:00
Sergio Martins
143c82291d qtquick: The user can now choose another titlebar file 2020-12-02 22:43:01 +00:00
Sergio Martins
979b2fdd40 qtquick: Set the frameworkwidget factory as a context prop 2020-12-02 22:38:17 +00:00
Sergio Martins
46337d900b qtquick: Load the titlebars with a loader
in followup commit the source will be user configurable
2020-12-02 21:43:57 +00:00
Sergio Martins
ec1f1bea9a qtquick: Move some style code to TitleBar.qml
Doesn't belong in the base component
2020-12-02 21:30:55 +00:00
Sergio Martins
d207028d60 qtquick: Document and add proper interface to TitleBarBase.qml 2020-12-02 16:17:46 +00:00
Sergio Martins
a237eae508 qtuick: Add a customtitlebar example stub
Not custom yet.
2020-12-02 15:48:51 +00:00
Sergio Martins
243396f22b qtquick: Move the example into a sub-directory
We'll have other qtquick examples
2020-12-02 14:52:10 +00:00
Sergio Martins
f240bf5d40 Disable Aero-snap for QtQuick, for now 2020-12-01 20:33:52 +00:00
Sergio Martins
785db1b50c qtquick: Fix detaching windows not working smoothly
Use the global event filter, as grabing isn't reliable in QtQuick.

Either not the same item is getting the mouse moves and release, or
it's because QtQuick internally is also setting/unsetting grabbers,
as seen in the source code
2020-12-01 20:14:46 +00:00
Sergio Martins
b6a7048a4f Fwd declare QWidget, no need to include in the header 2020-12-01 19:48:41 +00:00
Sergio Martins
52bff59024 Remove duplicated logic regarding mouse events 2020-11-30 23:14:00 +00:00
Sergio Martins
bba0195196 Move "mouseEvent()" function to Utils_p.h
So it can be reused
2020-11-30 23:00:30 +00:00
Sergio Martins
41fdb9c7df qtquick: Fix non-developer build 2020-11-30 18:37:39 +00:00
Sergio Martins
c1c1da25a8 Fix build with vs2013 2020-11-30 14:11:21 +00:00
Sergio Martins
b4e7b97646 examples: Fix non-developer build 2020-11-30 12:25:29 +00:00
Sergio Martins
2c687fe469 qtquick: Implement the rubber band
When hovering a drop indicator we now see the blue rubberband
2020-11-29 22:31:11 +00:00
Sergio Martins
10e0402afa Fix build 2020-11-29 19:51:37 +00:00
Sergio Martins
ebb1179167 fix namespace 2020-11-29 19:41:30 +00:00
Sergio Martins
15c196d865 qtquick: Move Rubberband to its own file 2020-11-29 19:23:10 +00:00
Sergio Martins
e897ce6e8e qtquick: Add a replacement for QAction
So we don't depend on QtWidgets lib
2020-11-29 18:44:00 +00:00
Sergio Martins
83d9db1b64 qtquick: multisplitter lib no longer depends on QtWidgets
Added a QSizePolicy replacement.
KDDW for QtQuick still needs to link to QtWidgets, but it's a
step forward
2020-11-29 18:08:49 +00:00
Sergio Martins
60331fc654 qtquick: Remove some unneeded QtWidget includes 2020-11-29 17:57:14 +00:00
Sergio Martins
e8f16dd172 qtquick: Don't build ObjectViewer 2020-11-29 17:50:51 +00:00
Sergio Martins
44bd451362 qtquick: stabilize tests with offscreen QPA 2020-11-29 15:17:29 +00:00
Sergio Martins
1d2a791dfc qtquick: Fix DND operation never ending
When double-clicking a title bar we're getting: Press, Release,
Press and never the Release. We get the MouseDblClickEvent instead,
so handle that
2020-11-29 14:40:03 +00:00
Sergio Martins
a4ba45b423 qtquick: Fix flickering the first time we drag over 2020-11-28 19:27:18 +00:00
Sergio Martins
12576970c6 qtquick: Fix dragged window being behind in Z order
For QtWidgets, when we raise something that's not a top-level
it won't raise. So do the same thing for QtQuick
2020-11-28 18:28:43 +00:00
Sergio Martins
e0ee7d14b0 qtquick: Remove warning about we not implementing keyboard ungrab
I don't see it in QQuickItem API, so we also don't grab it to
begin with
2020-11-28 17:53:39 +00:00
Sergio Martins
66f447ad3f qtquick: Fix crash when hovering over titlebar
It would think we were over a drag indicator, because during an instant
the overlay window has 0x0 size but the indicators are still visible.

Fix by only showing the overlay window (visible property) when it has
the correct size
2020-11-28 17:11:46 +00:00
Sergio Martins
75db599142 tests: Make DropArea::hover() return the chosen drop location
Just for tests.
2020-11-28 16:47:19 +00:00
Sergio Martins
a5c65ae039 Introduce NullIndicators and a Config option for it
Mostly for debugging purposes, but also useful if anyone doesn't
want to display indicators
2020-11-28 14:11:39 +00:00
Sergio Martins
5c055e0b8d examples: Add a flag to disable aero-snap
For development/debugging purposes only
2020-11-28 00:39:30 +00:00
Sergio Martins
090483f04e qtquick: Add some flags to the example
Mostly for debugging at this point
2020-11-28 00:18:41 +00:00
Sergio Martins
953219f6f3 qtquick: Minor refactoring
Removed some hardcoded QLineEdit casts, that code is now generic.
2020-11-27 18:47:03 +00:00
Sergio Martins
8356bb0c3c Introduce KDDockWidgets::widgetAt()
Only repro for QtWidgets for now, but already useful to remove some
QApplication includes
2020-11-27 18:29:46 +00:00
Sergio Martins
758548f7a2 qtquick: Only Windows needs Angle 2020-11-27 17:11:45 +00:00
Sergio Martins
2aa16eb6d9 examples: comment out the old multisplitter example
It's not for end users. Might even delete it
2020-11-27 17:07:44 +00:00
Sergio Martins
9d6cef24c1 qtquick: Use Angle on Windows
It's aero-snap friendly. Otherwise shows black artifacts.
We can however use normal GL and use FramelessWindowhint instead
2020-11-27 16:46:21 +00:00
Sergio Martins
52999d4cc9 qtquick: Don't process native events when being deleted 2020-11-27 15:03:55 +00:00
Sergio Martins
bc2fff2c04 qtquick: Window resizing works on Linux/mac now 2020-11-27 13:59:27 +00:00
Sergio Martins
40fa4e98f8 qtquick: Fix tests
Mouse tracking isn't implemented, so warns
2020-11-27 11:06:47 +00:00
Sergio Martins
a07f63a2c7 qtquick: Fix build on Windows 2020-11-26 23:30:55 +00:00
Sergio Martins
39c9a4da9b qtquick: Enable native event handling for the floating windows
Same as done with QtWidgets, now hopefully we have native window
resize
2020-11-26 23:26:15 +00:00
Sergio Martins
e0aa547393 qtquick: Start using the WidgetResizeHandler
It's no longer commented out in FloatingWindow.
Doesn't do much for QtQuick, but at least we don't have different
code paths

Making it work is next.
2020-11-26 23:13:46 +00:00
Sergio Martins
08eab8882f qtquick: Call FloatingWindow::setupWindow() at the correct time
We need a QWindow. While that's done very early, and automatically
for QtWidgets, for QtQuick it's done later, by us, manually
2020-11-26 22:52:23 +00:00
Sergio Martins
04723ac3a9 Minor refactoring, moved code into a function 2020-11-26 22:46:14 +00:00
Sergio Martins
c25043fa00 qtquick: Enable more QtWidget paths that compile fine
Just needed a dummy create() function
2020-11-26 22:28:57 +00:00
Sergio Martins
5372920cd0 qtquick: Port NCHITTESTEventFilter 2020-11-26 21:53:48 +00:00
Sergio Martins
28ae4c3b8c Add helper to get FloatingWindow by its WId 2020-11-26 21:42:45 +00:00
Sergio Martins
6158ebd520 qtquick: Fix our QtQuick QLineEdit clashing with the QWidgets one
Some indirect include is making it clash. Cleaner to use a different
name anyway
2020-11-26 21:28:01 +00:00
Sergio Martins
b77430e574 Remove two more unneeded QApplication includes 2020-11-26 19:35:46 +00:00
Sergio Martins
fad81d595a qtquick: Remove some dependencies to QApplication 2020-11-26 19:31:32 +00:00
Sergio Martins
0980a7f601 qtquick: Port qtTopLevelForHWND() to Windows
Deals in QWindow instead of QWidget
2020-11-26 18:54:58 +00:00
Sergio Martins
46830713e0 stabilize test on Windows 2020-11-26 18:27:17 +00:00
Sergio Martins
cd796db0ff Add helper topLevelForHandle() 2020-11-26 17:53:28 +00:00
Sergio Martins
29da5f4a8a qtquick: Fix another include
Fixes the multisplitter build on Windows, which has a more picky
linker
2020-11-26 17:18:55 +00:00
Sergio Martins
315bdc71e5 qtquick: Include the correct include
Not the widget one
2020-11-26 17:17:18 +00:00
Sergio Martins
a8965bea91 Fix a unit-test
Broke it by mistake
2020-11-26 17:00:10 +00:00
Sergio Martins
bf259c7ffc qtquick: Fix build on mac
Mostly due to -Werror and clang having more warnings
2020-11-26 16:45:12 +00:00
Sergio Martins
8a990451d7 Fix casting -1 to WId on mac 2020-11-26 15:59:32 +00:00
Sergio Martins
974bdbd637 Fix a -Wweak-vtable warning 2020-11-26 15:55:52 +00:00
Sergio Martins
cb9d77cd18 Fix more -Woverloaded-virtual 2020-11-26 15:17:33 +00:00
Sergio Martins
af10efa587 Fix more -Woverloaded-virtual 2020-11-26 15:15:37 +00:00
Sergio Martins
a0fb47af58 Fix -Woverloaded-virtual 2020-11-26 14:52:04 +00:00
Sergio Martins
598fbdc51b qtquick: Fix build on mac
RubberBand was being compiled twice. We shouldn't compile the
_widget* multisplitter files when using QtQuick.

Also implies tst_multisplitter shouldn't be run when compiling
for QtQuick.
2020-11-26 14:29:17 +00:00
Sergio Martins
81c3f3f0c6 Fix a -Wweak-vtables warning 2020-11-26 13:28:15 +00:00
Sergio Martins
b9962b3df0 qtquick: Make the QWinWidget case specific to QtWidgets
With QtQuick we don't have QWinWidget
2020-11-26 13:23:06 +00:00
Sergio Martins
a71386e62a Fix using qAbs(bool)
detected by msvc
2020-11-26 12:59:51 +00:00
Sergio Martins
812ce1c08b qtquick: Fix floating dock #3 in the example
It would go to its previous place (0,0), so don't show it at
start.

Added a unit-test as thought it was a bug, but code is correct.
2020-11-26 12:31:50 +00:00
Sergio Martins
189e82450b qtquick: Add 2px margin for the the tab widget
Mimics QtWidgets
2020-11-26 12:06:52 +00:00
Sergio Martins
a3dbc3739c qtquick: Make title bar look nicer 2020-11-26 12:02:58 +00:00
Sergio Martins
c8d34375af qtquick: Add margin to Frame 2020-11-26 11:43:30 +00:00
Sergio Martins
6c3775ea45 qtquick: floating window now has a border and 4px margin
Just like the QtWidget one
2020-11-26 11:36:36 +00:00
Sergio Martins
5a72e1adab qtquick: Fix some colors 2020-11-25 23:52:56 +00:00
Sergio Martins
0f7f310734 qtquick|example: Use the KDAB logs, as we have for QtWidgets 2020-11-25 23:09:53 +00:00
Sergio Martins
e8bf2a5222 qtquick: Remove dummy background color 2020-11-25 22:59:30 +00:00
Sergio Martins
468f545c9a qtquick: Enable more tests
these pass now, too
2020-11-25 22:15:52 +00:00
Sergio Martins
a59ff6dfee qtquick: Build tests with ctest too 2020-11-25 21:41:46 +00:00
Sergio Martins
4fa415ba05 qtquick: Comment out a few tests
We don't support max-size yet
2020-11-25 21:01:37 +00:00
Sergio Martins
d851c60b2f qtquick: Don't allow the initial window size to be smaller than its min
Fixes a test
2020-11-25 20:52:29 +00:00
Sergio Martins
bf097098e5 qtquick: Fix pointer comparison in test 2020-11-25 20:10:32 +00:00
Sergio Martins
8c93b85a24 qtquick: Remove unneeded code 2020-11-25 20:10:13 +00:00
Sergio Martins
f87d67dd59 qtquick: Remove unneeded code 2020-11-25 20:07:08 +00:00
Sergio Martins
bc3278b218 qtquick: tst_positionWhenShown 2020-11-25 17:01:37 +00:00
Sergio Martins
d2c55a67df qtquick: Fix more tests 2020-11-25 16:28:54 +00:00
Sergio Martins
a3414fd92b qtquick: Fix tst_addToSmallMainWindow1 2020-11-25 16:10:04 +00:00
Sergio Martins
12f3ba9074 qtquick: Fix some tests 2020-11-25 16:02:42 +00:00
Sergio Martins
034d2c8aee README: Mention Qt6 2020-11-25 12:31:29 +00:00
Sergio Martins
ad3f4141c8 qtquick|tests: Fix main window QWindow not being resized 2020-11-24 18:40:22 +00:00
Sergio Martins
cde2e4e571 qtquick: Fix tst_constraintsAfterPlaceholder
The resize happens sync for QtQuick, so that wait isn't needed
2020-11-24 13:36:10 +00:00
Sergio Martins
e0775467b2 qtquick|tests: Fix verifying frame min size 2020-11-24 13:12:53 +00:00
Sergio Martins
b20ffcde61 tst_negativeAnchorPosition: Fix typo
we need to subtract from height(), not width()
2020-11-24 12:44:00 +00:00
Sergio Martins
43bdb6a4c2 qtquick: Bunch of fixes for min-size support 2020-11-24 11:38:13 +00:00
Sergio Martins
929aabdc0d example: Add --native-title-bar
Behind developer mode for now
2020-11-23 21:37:59 +00:00
Sergio Martins
6a90a5125f TitleBar: these 4 getters are only for development mode 2020-11-23 20:22:45 +00:00
David Faure
77016a619f Fixes for being used as a subproject
* Don't use CMAKE_SOURCE_DIR, always use CMAKE_CURRENT_SOURCE_DIR
* -Werror=undef found that #if was used instead of #ifdef
2020-11-23 21:16:40 +01:00
Sergio Martins
dc328ff7c4 Fix developer mode build on Windows
developer mode will make some 'private' in headers be 'public', this
affects name mangling on MSVC.

kddockwidgets_linter didn't have the developer mode macro enabled,
so there was a mangling mismatch when linking
2020-11-23 17:40:06 +00:00
Sergio Martins
dc13665566 Fix forward declaration 2020-11-23 16:53:52 +00:00
Sergio Martins
d1279ab119 Merge branch '1.1' 2020-11-23 15:57:42 +00:00
Sergio Martins
80bf6032f3 Fix FloatingWindow's title when shown in the taskbar
The window title isn't only important when using native title bar,
it's also important for showing the correct title in the taskbar
or alt-tab.

For example, if using "minimize support", they'll show in the task bar

Fixes issue #101
2020-11-23 15:52:50 +00:00
Sergio Martins
22437e75c6 qtquick: Override min size for FrameQuick
Fixes constriants not propagating from dock widget to frame
2020-11-23 15:45:06 +00:00
Sergio Martins
801e49de9d Make DockWidgetQuick::minimumSize()/max virtual
While for QtWidgets we have layouts which propagate the constraints
up, for QtQuick we don't, so we need to override minimumSize instead
2020-11-23 15:45:06 +00:00
Sergio Martins
a847a574ed qtquick: Delete the frame visual item delayed
If we're in a QML event handler we can't delete the QML item,
it's a QML limitation. We still want to delete the dockwidgets now though
since all tests depend on that.
2020-11-23 15:45:06 +00:00
Sergio Martins
ef8807bc09 Minor style 2020-11-23 15:45:06 +00:00
Allen Winter
d441477fa9 Merge branch '1.1' 2020-11-22 12:25:28 -05:00
Allen Winter
ece7b22bec apidox - fix doc for enum KDDockWidgets::Config::Flag
doxygen likes "///<"  (not "///>")

Issue#106
2020-11-22 12:17:38 -05:00
Sergio Martins
97d8e4f18b qtquick|tests: Be able to deliver a double-click event to a titlebar
Needed for tests
2020-11-20 21:44:06 +00:00
Sergio Martins
9937f5d789 qtquick: Fix floating windows having a parentItem
they are top-level windows, so shouldn't have a parent item.
Only a QObject::parent()
2020-11-20 16:55:00 +00:00
Sergio Martins
675edfe6db qtquick: Move the floating button logic one level up, to the base class
So QtQuick can use the same logic.

Fixes tst_nonDockable too.
2020-11-20 15:35:46 +00:00
Sérgio Martins
c23d179385 Add support for Qt6
Adds the -DKDDockWidgets_QT6=ON option
Also you'll need to port any required Qt6 module first (For example QStateMachine).
2020-11-20 12:36:11 +00:00
Sergio Martins
36af020505 Fix two scoped value rollback being temporaries 2020-11-20 09:55:31 +00:00
Sergio Martins
7b2d195139 qtquick: Put the failing QtQuick tests at the end
So they don't corrupt the passing ones.
Approx 40 need to pass now.
2020-11-19 22:59:00 +00:00
Sergio Martins
984ab3bb13 qtquick: Add support for disabling close button
For when dockwidget isnt' closable.
Fixes tst_nonClosable() too
2020-11-19 22:12:49 +00:00
Sergio Martins
0331f90791 Move title bar logic regarding close button to base class
So QtQuick can use it too
2020-11-19 22:09:40 +00:00
Sergio Martins
b19c53d650 qtquick: Implement some title bar helpers for tests
Mostly getters that return the title button state
2020-11-19 21:45:29 +00:00
Sergio Martins
3670cf2377 qtquick: Comment the embedded main window test
We don't support that yet
2020-11-19 17:49:27 +00:00
Sergio Martins
864670b0a1 qtquick: Fix some cases where floating windows didn't have parent
Happened because floatingWindow->parentWidget() was null. On QtWidgets
this works because QWidget::parentWidget() will return the transient parent
without a problem.
2020-11-19 17:47:02 +00:00
Sergio Martins
0cf148b389 tests: Check for the existence of QWindow at exit, not QWidget
So we're clean for QtQuick too
2020-11-19 16:34:30 +00:00
Sergio Martins
921d0892a0 qtquick: Fix tst_setVisibleFalseWhenSideBySide
Was also broken (but passed) for QtWidgets.
We don't support hidding widgets with setVisible(false).
2020-11-19 11:59:09 +00:00
Sergio Martins
beb2d9e659 qtquick: Add support for non-closable windows
fixes tst_preventClose
2020-11-18 18:13:35 +00:00
Sergio Martins
59168da231 qtquick: Fix crash at exit
Don't delete our QWindow if the QWindow is currently deleting us
2020-11-18 18:10:32 +00:00
Sergio Martins
3dbf699c49 qtquick: Fix QtQuick parenting
Mimic QtWidgets so we have similar behaviour:

- When a MainWindow is deleted, its associated QWindow is deleted
too.
- When a MainWindow is deleted, it deletes any FloatingWindows
which have it as a transient parent
2020-11-18 16:18:42 +00:00
Sergio Martins
baa7561b13 qtquick: Fix tst_addAndReadd
Wasn't even correct for QtWidgets and just passing because the
widget wasn't getting dropped QtWidgets, since the indicator was hidden.

For QtQuick the indicator is also hidden but it's accepting it. To fix.
2020-11-18 14:42:34 +00:00
Sergio Martins
ac5f4b871c qtquick: Fix tst_honourGeometryOfHiddenWindow
We want the geometry of the QWindow
2020-11-18 14:30:08 +00:00
Sergio Martins
52bf449366 qtquick: Fixes for focusing
Fixes tst_focus too, and  maybe a few other tests
2020-11-18 14:01:55 +00:00
Sergio Martins
08a1c4e6df qtquick: Implement focus policy
fixes tst_dockWidgetGetsFocusWhenDocked
2020-11-18 12:35:46 +00:00
Sergio Martins
156dad6e03 qtquick: Fix IndicatorWindow::posForIndicator()
Wasn't even implemented. Should make some tests pass.
2020-11-18 12:06:20 +00:00
Sergio Martins
b642d2df2f qtquick: Fix dropped widget not being the current tab 2020-11-18 11:44:55 +00:00
Sergio Martins
5cedfab82c Make DropIndicatorOverlay::posForIndicator() pure virtual 2020-11-18 11:44:02 +00:00
Sergio Martins
af9d62a58c qtquick: Fix FloatingWindows not being destroyed when empty
Also fixes tst_createFloatingWindow
2020-11-17 23:15:33 +00:00
Sergio Martins
0758496b0d Fix QtQuick build without developer mode 2020-11-17 21:15:07 +00:00
Sergio Martins
a452723919 Fix tests running with a nullptr state machine 2020-11-17 21:08:05 +00:00
Sergio Martins
774e66ba6f Minor: add an assert
Provides a nicer backtrace when it happens.
Currently repro on QtQuick only
2020-11-17 21:04:32 +00:00
Sergio Martins
f13de89db5 tests. Fix utils.h draggableFor() for QtQuick 2020-11-17 20:44:04 +00:00
Sergio Martins
1393ae224f Fix DockWidget::windowActiveAboutToChange() being emitted too much
Was being emitted even when it didn't affect our window.
This caused an explosion of signals when we had many dock widgets.
2020-11-17 18:52:54 +00:00
Sergio Martins
d92d7c258c tests: Remove stray qDebug 2020-11-17 18:15:38 +00:00
Sergio Martins
619ca74051 Use QFAIL instead of qFatal
more elegant, and test can continue, so we see the whole result
2020-11-17 18:15:03 +00:00
Sergio Martins
aa39a71ae5 Rename tst_common to tst_docks
Now that tst_docks was all ported let's have the old name again.
Also cmake -jN now works, as tst_docks is called by the tests_launcher
while tst_common was not
2020-11-17 15:44:49 +00:00
Sergio Martins
f4e33c1409 tests: Port the remaining tests to QtQuick
Tests now build with QtQuick too.
2020-11-17 15:26:14 +00:00
Sergio Martins
ad96336a36 Also init resources if QT_STATIC is defined
Should help with WASM, so you don't need to manually call
Q_INIT_RESOURCE
2020-11-17 11:35:37 +00:00
Sergio Martins
50d1e75709 Fully scope the qrc resource files
So they don't clash when KDDW is a static library
2020-11-17 11:33:22 +00:00
Sergio Martins
793c4e509b Fix crash regarding edge case of dragging window to itself
If we're not showing a title bar, then the draggable is the tabbar,
not the floating window.
2020-11-16 22:55:45 +00:00
Sergio Martins
e4a33ad8b7 Added README for wasm
Fixes issue #100
2020-11-13 18:45:58 +00:00
Sergio Martins
52cf53ce5c example: init resources if static
Fixes title bar not having icons on WASM
For issue #100
2020-11-13 18:33:13 +00:00
Sergio Martins
8791870efb Blacklist classical indicators on WASM
Use segmented indicators.
Wasm doesn't support top-level windows with translucency, which
is required for the classical indicators

For issue #100
2020-11-13 18:18:25 +00:00
Sergio Martins
4f8c47bfa4 Don't require Qt5X11Extras for web assembly
For issue #100
2020-11-13 17:43:38 +00:00
Sergio Martins
ad141df6b1 Merge branch '1.1' 2020-11-13 16:00:49 +00:00
Sergio Martins
72605292b7 Don't show dock/undock icon when dockwidget is not dockable
Fixes issue #99
2020-11-13 15:51:26 +00:00
Sergio Martins
1443eff1d9 tests: Port some tests to QtQuick
15 to go
2020-11-10 18:00:19 +00:00
Sergio Martins
22d5db32ce FocusScope: Remove focus when nothing is focused 2020-11-09 15:25:32 +00:00
Sergio Martins
e9a321039f Renamed signal to windowActiveAboutToChange()
Added the 'aboutTo', as the event hasn't been caught by the top-level
window yet
2020-11-09 13:46:16 +00:00
Sergio Martins
b93f115b53 Added DockWidget::windowActiveChanged() signal
Tracking the top-level window's 'isActiveWindow' property is difficult
since the dock widget's top-level window is changing all the time
when docking and undocking. So added a convenience signal
2020-11-09 13:35:05 +00:00
Sergio Martins
7faaf2cc69 tests: Port more tests to QtQuick
18 to go before we start fixing them
2020-11-08 15:18:16 +00:00
Sergio Martins
978f9d3a5e tests: Port another test to QtQuick
29 to go
2020-11-06 23:52:48 +00:00
Sergio Martins
09793b3481 tests: Port more tests to QtQuick
31 to go
2020-11-06 23:33:02 +00:00
Sergio Martins
f4d9a96ddb Added Config::Flag_KeepAboveIfNotUtilityWindow
Which sets Qt::WindowStaysOnTopHint for floating windows.
Usually unneeded, as floating windows are Qt::Tool with a transient
parent, but in case you're using Flag_DontUseUtilityFloatingWindows
then you might want (or not) to keep them above.
2020-11-05 12:11:48 +00:00
Sergio Martins
4ac892038f Fix QtQuick build 2020-11-04 11:34:35 +00:00
Colin Ogilvie
387ba291be export FocusScope 2020-11-04 11:32:42 +00:00
Sergio Martins
5d3d189774 tests: Port 1 test to QtQuick 2020-11-03 17:22:50 +00:00
Sergio Martins
a6a1047f75 FocusScope: Also honour widgets embedded in the tab bar
A widget embedded in a tab bar (like a line edit) lives outside
the DockWidget but inside the Frame. It should still focus the
current dockwidget.
2020-11-03 17:12:21 +00:00
Sergio Martins
3e70b846e8 FocusScope: Modify the member variable before emitting signal 2020-11-03 16:19:47 +00:00
Sergio Martins
0a2aebd78d FocusScope: Also focus when the user clicks on tab widget background
Relevant when you don't have a title bar
2020-11-03 15:29:13 +00:00
Sergio Martins
ddad0feaf3 tests: Additional 5 tests build for QtQuick now
37 to go
2020-11-02 20:04:14 +00:00
Sergio Martins
aba1cce1f6 Merge branch '1.1' into master 2020-11-02 11:37:56 +00:00
Sergio Martins
7318675e84 Clarify comment about fractionaly scalling factors
The problem can happen on Windows too.

Fixes issue #94

Nothing more we can do here, it's fixed for Qt 5.15.2.
2020-11-02 11:36:24 +00:00
Sergio Martins
cf700f8eb6 tests: port more tests to QtQuick
43 to go
2020-10-30 18:29:59 +00:00
Sergio Martins
2c1d099c83 Fix include here too 2020-10-30 12:27:50 +00:00
Sergio Martins
91ff29ae64 Fix build when KDDW is embedded within a project 2020-10-30 12:21:25 +00:00
Renato Araujo Oliveira Filho
523dfb0d12 Fix python bindings build 2020-10-30 08:36:15 -03:00
Sergio Martins
b84e0f3605 tests: More tests are now building with QtQuick
56 to go
2020-10-29 18:11:24 +00:00
Sergio Martins
f72e8e6a1c tests: Port 5 more tests 2020-10-28 22:57:28 +00:00
Sergio Martins
c6c1fe79c3 Fix QtQuick build
src/ is no longer in the include path
2020-10-28 22:15:32 +00:00
Sergio Martins
ea382dcc17 cmake: Install DockRegistry_p.h
It's available as private API if anyone wants to use it
2020-10-28 21:55:02 +00:00
Sergio Martins
c142eb8ecd trying to fix the python build
after the include paths changes
2020-10-27 21:40:51 +00:00
Sergio Martins
46c039153e cmake: Don't export src/ to include path
Otherwise applications can't have files named MainWindow.h or
DockWidget.h.

Now all kddockwidgets includes need to be scoped, example:
2020-10-27 17:01:18 +00:00
Sergio Martins
473f03360c tests: Fix Wweak-vtables warnings 2020-10-26 18:59:21 +00:00
Sergio Martins
3fb3984a72 Merge branch '1.1' 2020-10-26 18:23:17 +00:00
Sergio Martins
9c5e3cf5af tests: Port another test to QtQuick 2020-10-26 18:22:01 +00:00
Sergio Martins
7419ca806d tests: Port 2 more tests to QtQuick 2020-10-26 17:32:13 +00:00
Allen Winter
8a51c31663 OBS - update for 1.1.0 release 2020-10-26 12:14:46 -04:00
Sergio Martins
a586a7894b cmake: Qt5::GuiPrivate is only needed on Windows 2020-10-26 16:14:07 +00:00
Sergio Martins
31fcbebaff ChangeLog: Add the date of 1.1 release 2020-10-26 15:29:47 +00:00
Sergio Martins
c4cc6b3e6e tests: Port more tests to QtQuick 2020-10-25 22:39:10 +00:00
Sergio Martins
420ac9044e Merge branch '1.1' into master 2020-10-23 17:11:45 +01:00
Sergio Martins
392dac5691 Use Qt::WA_PendingMoveEvent instead of Qt::WA_Moved
We check if the user provided position before putting a floating
window in a default position. Qt::WA_Moved however has the problem
that it's not cleared, so second time you show a window, it will
have it, even if user didn't set geometry.

Use Qt::WA_PendingMoveEvent instead, which was made for tracking
moves made before the first show.
2020-10-23 17:07:14 +01:00
Sergio Martins
29744c01c6 ChangeLog: Added Config::setTabbingAllowedFunc 2020-10-23 10:33:37 +01:00
Sergio Martins
a16adb9578 Merge branch '1.1' 2020-10-23 10:32:10 +01:00
Sergio Martins
49e488df24 remove roadmap from 1.1 ChangeLog
We only have it for master
2020-10-23 10:29:57 +01:00
Sergio Martins
1a3f00eac8 ChangeLog: Added HDPI improvements 2020-10-23 10:29:00 +01:00
Sérgio Martins
c6ea8d5525 ChangeLog: Remove mention to 1.0.1 release
We're releasing 1.1 asap
2020-10-23 10:23:52 +01:00
Sergio Martins
7738585d01 tests: Enabled another batch of tests for QtQuick
83 of 175 are now building with QtQuick
2020-10-22 20:54:39 +01:00
Sergio Martins
61bc24c6ea tests: A bunch of drag/mouse functions are not compatible with qtquick
In the sense that they build.
2020-10-21 22:43:08 +01:00
Sergio Martins
ae90001d7a tests: Remove some QWidget ifdefs
Works fine for QtQuick too
2020-10-21 21:08:03 +01:00
Sergio Martins
c0ea3a097c tests: Port a test to qtquick 2020-10-21 21:05:03 +01:00
Sergio Martins
abca57be11 qtquick: Fix tst_stealFrame
Now has the same behaviour as with QWidgets
2020-10-20 22:36:45 +01:00
Sergio Martins
645201480c qtquick: Port two tests 2020-10-20 22:05:29 +01:00
Sergio Martins
8cffba3e22 Merge branch '1.1' into master 2020-10-19 21:25:39 +01:00
Sergio Martins
8f62004f5b cmake: Remove QTQUICK option for 1.1
Leave it in master only.
2020-10-19 21:23:44 +01:00
Sergio Martins
4fff6edb7b qtquick: Port another test 2020-10-19 21:03:47 +01:00
Sergio Martins
64cff4ba49 qtquick|tests: use auto instead of QPointer<QWidget> 2020-10-19 21:00:18 +01:00
Sergio Martins
ce3c4d5661 qtquick|tests: pass parent to main window
Those tests pass now
2020-10-19 20:58:00 +01:00
Sergio Martins
5f333f0934 qtquick: port more tests 2020-10-19 20:53:40 +01:00
Sergio Martins
f70722934c qtquick: Port more tests 2020-10-19 20:47:09 +01:00
Sergio Martins
b9eb9f10d4 qtquick: Port more tests 2020-10-19 20:26:09 +01:00
Sergio Martins
6f89d94eef qtquick: Emit Frame::layoutInvalidated() when needed
When min/max size changes it needs to be emitted, so layout
notices
2020-10-19 20:06:29 +01:00
Sergio Martins
3a494d706a qtquick: Implement missing QWidgetAdapter::floatingWindow() 2020-10-19 19:05:10 +01:00
Sergio Martins
2ffbe07bcf Updated README-Wayland.md 2020-10-19 11:46:22 +01:00
Sergio Martins
371fd361a8 Detach when double-click on tab bar background, if no title bar
When a title bar isn't visible, we need a way to detach an entire
group of tabs by double clicking, required for wayland.

Implemented for all platforms, as it makes sense
2020-10-19 11:42:39 +01:00
Sergio Martins
97416607e5 qtquick: Allow to set min-sizes on items 2020-10-18 23:27:10 +01:00
Sergio Martins
51ccbe7c7c Move TabBar::detachTab() to Frame
As the code doesn't have anything specific to QTabBar.
Means QtQuick can reuse it without needing a TabBarQuick.

tst_crash now passes for QtQuick too
2020-10-18 23:07:13 +01:00
Sergio Martins
65599399c7 qtquick: port a bunch of tests 2020-10-18 22:23:51 +01:00
Sergio Martins
c0816d4924 qtquick: Fix tst_negativeAnchorPosition5
min sizes weren't bounded
2020-10-18 21:58:46 +01:00
Sergio Martins
2988e97e14 qtquick: Fix build 2020-10-18 20:29:44 +01:00
Sergio Martins
046ceae500 Remove unneeded null check from DropArea::drop() 2020-10-18 12:31:04 +01:00
Sergio Martins
33c0cda582 Minor readability, just call setWindowBeingDragged(true)
It's always true here.
2020-10-18 12:28:10 +01:00
Sergio Martins
df7acc1075 Added README-Wayland.md to explain some differences
Fixes issue #10
2020-10-17 12:30:57 +01:00
Sergio Martins
3859e4b277 Added Wayland to the README 2020-10-17 12:18:05 +01:00
Sergio Martins
26253b65c7 wayland: Silence warning about qpa not supporting activating
"qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"
2020-10-17 12:13:58 +01:00
Sergio Martins
424453ab57 Double clicking on a tab will make it float
Also really required for Wayland, where we don't another way to
float a single tab, as the float button affects all of them.
2020-10-17 12:09:47 +01:00
Sergio Martins
d7de0ebd20 Updated ChangeLog
- Added wayland for 1.2, it's done. Might need more polishing.
- Removed 1.0.1, let's release 1.1 instead
- Moved QtQuick to 1.3. The december target is still there, but I want
to release 1.2 sooner, since the wayland support is done.
2020-10-17 11:44:45 +01:00
Sergio Martins
bf1c5140cd Wayland: Fix another missing mapToGlobal
Hovering indicators was fine, but dropping was off by a few pixels
vertically due to the menu bar
2020-10-17 11:41:19 +01:00
Sergio Martins
fb7a35db5b Wayland: Fix indicator hover being off
Drag events are in local coords, while DropArea wants it in
global
2020-10-17 11:05:56 +01:00
Sergio Martins
5872c2fbe3 Wayland: Support classical indicators too
The Indicator Window is top-level, but on Wayland that can't do,
as we have no way to position it. So parent it. The indicators
are now under the window being dragged, but that's no so bad, as it's
smi-transparent, so you still see the indicator.
2020-10-17 11:02:49 +01:00
Sergio Martins
ba04c70d5a Minor code re-ordering 2020-10-17 10:30:26 +01:00
Sergio Martins
de1e2d301f wayland: Support affinities too 2020-10-17 10:27:43 +01:00
Sergio Martins
b67bdf779e Don't dereference windowBeingDragged->floatingWindow()
Add a level of indirection instead, and support for the wayland case
2020-10-16 23:06:30 +01:00
Sergio Martins
78a3221395 Merge branch '1.1' into master 2020-10-16 22:05:14 +01:00
Sergio Martins
f698b56dad relax test
Seems to happen with 5.15 static. It's benign anyway
2020-10-16 22:02:45 +01:00
Sergio Martins
4ed18fdf1d Introduce Config::setTabbingAllowedFunc(<lambda>)
This gives a lot of power to the user to disallow tabbing two dock
widgets, while still allowing them to be docked side by side. As
this can't be achieved with affinities.

Instead of adding API which might not be enough soon, just allow
the user to pass a lambda and implement his very custom requirement.

Fixes #91
2020-10-16 21:56:33 +01:00
Sergio Martins
6e05f13c77 Added FloatingWindow::dockWidgets() 2020-10-16 21:48:47 +01:00
Sergio Martins
cb51d0af11 Merge branch '1.1' into master 2020-10-16 20:53:17 +01:00
Sergio Martins
a146cd81b2 Fix mac tests build 2020-10-16 20:52:22 +01:00
Sergio Martins
8dbd6df456 Merge branch '1.1' into master 2020-10-16 20:35:56 +01:00
Sergio Martins
ac6d845c8d Skip tests on macOS+offscreen if Qt <= 5.15.0
There's a bug in Qt qpa only fixed in 5.15.1
2020-10-16 20:35:04 +01:00
Sergio Martins
cfc9d1abcd cmake: Fix indentation 2020-10-16 19:43:50 +01:00
Sergio Martins
a66748f6ca relax test
Seems to happen with 5.15 static. It's benign anyway
2020-10-16 17:10:40 +01:00
Sergio Martins
e5f46ead3b qtquick: Fix build 2020-10-16 17:08:36 +01:00
Sergio Martins
171023558e Introduce WindowBeingDraggedWayland
This is just a cleanup. Instead of having WindowBeingDragged
having both code paths, let's keep the non-wayland platforms code
small and clean.
2020-10-16 17:08:36 +01:00
Allen Winter
059a424b88 Merge branch '1.1' 2020-10-16 08:39:26 -04:00
Sergio Martins
307b22deaf Wayland: Also allow to drag by tab widget
For when we don't have title bar
2020-10-16 13:30:20 +01:00
Sergio Martins
e405688f67 Fixing build with msvc 2013
error C2876: 'QMimeData' : not all overloads are accessible
2020-10-16 13:16:13 +01:00
Sergio Martins
68554119f4 Fix -Werror build on mac 2020-10-16 11:46:02 +01:00
Sergio Martins
86a4a740ef wayland: dropping a single tab or frame now works too 2020-10-16 11:33:09 +01:00
Sergio Martins
0a004b83ca Add WindowBeingDragged::draggable() getter 2020-10-16 11:16:49 +01:00
Sergio Martins
4fa47403c5 Make DragArea::drop() deal in WindowBeingDragged()
Preparing for wayland case.
2020-10-16 10:49:42 +01:00
Sergio Martins
9e418ced53 wayland: Finish WindowBeingDragged::maxSize() and minSize
Supports now single dock widgets or single frames
2020-10-16 10:40:34 +01:00
Sergio Martins
bc04863439 Wayland: Dragging a tab or a nested dock widget now shows pixmap
TODO: Supporting a drop for those two cases
2020-10-16 10:33:01 +01:00
Allen Winter
77f259a435 README.md - contact info 2020-10-15 16:06:26 -04:00
Sergio Martins
16bcace663 Add some transparency to the QDrag pixmap
Looks better
2020-10-15 19:15:40 +01:00
Sergio Martins
52fddaa1cd wayland: Set a pixmap while DND
Since our source window isn't moving, let's at least show a copy
of it on the pixmap

A bunch of tearing going on kwin_wayland/nvidia though.
Weston/nvidia is fine.
Haven't tried kwin_wayland/Intel yet
2020-10-15 19:11:12 +01:00
Sergio Martins
f3d7400c15 wayland: End the drag properly when user cancels the drag 2020-10-15 19:02:46 +01:00
Allen Winter
7ebc3f3533 src/DockWidgetBase.h - fix compile after merge from master 2020-10-15 12:43:29 -04:00
Renato Araujo Oliveira Filho
0ee94b425e Fixed python bindings generation 2020-10-15 12:38:32 -04:00
Sergio Martins
d44a173945 Wayland: Really end the drag after dropping
Fixes a crash on shutdown
2020-10-15 17:27:12 +01:00
Sergio Martins
ac001d6f32 wayland: Don't allow to drop onto itself
It was showing the drop indicators in the window being dragged
2020-10-15 16:50:46 +01:00
Sergio Martins
e3de2df69c wayland: Add initial drag and drop support
You can drag a floating window and drop it somewhere

- Only works with segmented indicator style. Classical indicators
require us being able to positioning an overlay, which wayland
doesn't allow us to.
- Detaching inner tabs and dock widgets not implemented yet.
- polishing needed
2020-10-15 16:50:46 +01:00
Sergio Martins
39d3c90b2f Minor variable rename for readability 2020-10-15 16:50:46 +01:00
Sergio Martins
bca21defa5 wayland: Don't grab mouse or set window opacity
Not supported by the QPA and not needed for wayland anyway
2020-10-15 16:50:46 +01:00
Sergio Martins
dd3116ae7f Minor: remove const, need to pass it somewhere else 2020-10-15 16:50:46 +01:00
Allen Winter
49b3765eae Merge branch '1.1' 2020-10-15 10:53:41 -04:00
Allen Winter
1f5dd35bec Merge pull request #93 from KDAB/fix-python-binding
Fixed python bindings generation
2020-10-15 10:52:44 -04:00
Allen Winter
e57b46979d buildsystem - fix installation path for Python bindings 2020-10-14 16:27:18 -04:00
Allen Winter
683b67abb0 buildsystem - install libs with symlinks for the major vers 2020-10-14 15:59:53 -04:00
187 changed files with 10961 additions and 6938 deletions

1
.gitignore vendored
View File

@@ -34,7 +34,6 @@ latex
html
custom_titlebar
libkddockwidgets.so*
libkddockwidgets_multisplitter.so*
*.depends
kddockwidgets_basic_quick
/src/KDDockWidgetsConfig.cmake

View File

@@ -11,12 +11,17 @@
# Pass the following variables to cmake to control the build:
#
# -DKDDockWidgets_QT6=[true|false]
# Build against Qt6 rather than Qt5
# Default=false (Qt5 will be used even if Qt6 is available)
#
# -DKDDockWidgets_STATIC=[true|false]
# Build static versions of the libraries
# Default=false
#
# -DKDDockWidgets_TESTS=[true|false]
# Build the test harness.
# Currently does nothing unless you also set KDDockWidgets_DEVELOPER_MODE=True
# Default=false
#
# -DKDDockWidgets_EXAMPLES=[true|false]
@@ -42,9 +47,13 @@
# Default=false
#
# -DKDDockWidgets_PYTHON_BINDINGS_INSTALL_PREFIX=[path]
# alternative install path for python bindings
# Set an alternative install path for Python bindings
# Default=CMAKE_INSTALL_PREFIX
#
# -DKDDockWidgets_UNITY_BUILD=[true|false]
# Build with CMake's UNITY_BUILD (requires CMake version 3.16 or higher)
# Default=true
#
cmake_minimum_required(VERSION 3.7)
if(POLICY CMP0020)
@@ -72,19 +81,26 @@ else()
endif()
set(${PROJECT_NAME}_VERSION_MAJOR 1)
set(${PROJECT_NAME}_VERSION_MINOR 1)
set(${PROJECT_NAME}_VERSION_PATCH 95)
set(${PROJECT_NAME}_VERSION_MINOR 2)
set(${PROJECT_NAME}_VERSION_PATCH 1)
set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
set(${PROJECT_NAME}_SOVERSION "1.2")
set(${PROJECT_NAME}_SOVERSION "1.3")
include(FeatureSummary)
option(${PROJECT_NAME}_QT6 "Build against Qt 6" OFF)
option(${PROJECT_NAME}_DEVELOPER_MODE "Developer Mode" OFF)
option(${PROJECT_NAME}_PYTHON_BINDINGS "Build python bindings" OFF)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
option(${PROJECT_NAME}_UNITY_BUILD "Build with CMake's UNITY_BUILD" ON)
endif()
if(${PROJECT_NAME}_PYTHON_BINDINGS AND (CMAKE_BUILD_TYPE MATCHES "^[Dd]eb" OR ${PROJECT_NAME}_STATIC))
message(FATAL_ERROR "** Python Bindings are disabled in debug or static builds.")
endif()
option(${PROJECT_NAME}_TESTS "Build the tests" OFF)
option(${PROJECT_NAME}_EXAMPLES "Build the examples" ON)
option(${PROJECT_NAME}_DOCS "Build the API documentation" OFF)
@@ -96,9 +112,18 @@ endif()
#option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
find_package(Qt5Widgets 5.9 REQUIRED)
if (${PROJECT_NAME}_QT6)
find_package(Qt6Widgets REQUIRED)
find_package(Qt6Test REQUIRED)
set(QT_MAJOR_VERSION 6)
else()
find_package(Qt5Widgets 5.9 REQUIRED)
find_package(Qt5Test 5.9 REQUIRED)
set(QT_MAJOR_VERSION 5)
endif()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(ECM_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ECM/modules/")
set(PYTHON_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake/Python")
@@ -111,8 +136,8 @@ include(QtInstallPaths) #to set QT_INSTALL_FOO variables
macro(set_compiler_flags targetName)
if(${PROJECT_NAME}_DEVELOPER_MODE)
target_compile_definitions(${targetName} PRIVATE DOCKS_DEVELOPER_MODE QT_FORCE_ASSERTS)
if(NOT MSVC)
target_compile_definitions(${targetName} PUBLIC DOCKS_DEVELOPER_MODE PRIVATE QT_FORCE_ASSERTS)
if(NOT MSVC AND NOT ${PROJECT_NAME}_QT6) # We're not warnings clean with Qt6 yet
target_compile_options(${targetName} PRIVATE -Wall -Wextra -Werror -Wno-error=deprecated-declarations)
if (APPLE)
target_compile_options(${targetName} PRIVATE -Wweak-vtables)
@@ -122,8 +147,8 @@ macro(set_compiler_flags targetName)
endmacro()
if(${PROJECT_NAME}_QTQUICK)
find_package(Qt5Quick)
find_package(Qt5QuickControls2)
find_package(Qt${QT_MAJOR_VERSION}Quick)
find_package(Qt${QT_MAJOR_VERSION}QuickControls2)
add_definitions(-DKDDOCKWIDGETS_QTQUICK)
else()
add_definitions(-DKDDOCKWIDGETS_QTWIDGETS)
@@ -163,7 +188,6 @@ endif()
if(${PROJECT_NAME}_EXAMPLES)
if (${PROJECT_NAME}_QTQUICK)
add_subdirectory(examples/qtquick)
set_compiler_flags(kddockwidgets_example_quick)
else()
add_subdirectory(examples/dockwidgets)
add_subdirectory(examples/minimal)
@@ -178,10 +202,9 @@ if(${PROJECT_NAME}_DEVELOPER_MODE)
enable_testing()
add_subdirectory(tests)
if (NOT ${PROJECT_NAME}_QTQUICK)
#Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
if(NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (140 tests), so split it in 6 runs so we can use threads.
# Require Qt5.15.1 or higher to run the tests_launcher tests on Mac
if (NOT APPLE OR Qt5Widgets_VERSION VERSION_GREATER 5.15.0)
# tst_docks.exe is pretty big (160 tests), so split it in more runs so we can use threads.
add_test(NAME tst_docks0 COMMAND tests_launcher 0 5)
add_test(NAME tst_docks1 COMMAND tests_launcher 1 5)
add_test(NAME tst_docks2 COMMAND tests_launcher 2 5)
@@ -204,15 +227,20 @@ if(${PROJECT_NAME}_DEVELOPER_MODE)
add_test(NAME tst_docks19 COMMAND tests_launcher 18 5)
add_test(NAME tst_docks20 COMMAND tests_launcher 19 5)
add_test(NAME tst_docks21 COMMAND tests_launcher 20 5) # one more for rounding leftovers
endif()
endif()
if (NOT ${PROJECT_NAME}_QTQUICK)
# tst_multisplitter depends on QWidget
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
endif()
add_test(NAME tst_multisplitter COMMAND tst_multisplitter)
add_test(NAME tst_common COMMAND tst_common)
add_test(NAME tst_docks COMMAND tst_docks)
endif()
endif()
if(${PROJECT_NAME}_DOCS)
add_subdirectory(docs) # needs to go last, in case there are build source files
endif()
if (${PROJECT_NAME}_UNITY_BUILD)
set_target_properties(kddockwidgets PROPERTIES UNITY_BUILD ON)
endif()

103
CMakePresets.json Normal file
View File

@@ -0,0 +1,103 @@
{
"version": 1,
"configurePresets": [
{
"name": "dev-qtwidgets",
"displayName": "dev-qtwidgets",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-dev-qtwidgets",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_DEVELOPER_MODE": "ON"
}
},
{
"name": "qtwidgets",
"displayName": "qtwidgets",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-qtwidgets",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "qtquick",
"displayName": "qtquick",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-qtquick",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QTQUICK": "ON"
}
},
{
"name": "dev-qtquick",
"displayName": "dev-qtquick",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-dev-qtquick",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"KDDockWidgets_QTQUICK": "ON",
"KDDockWidgets_DEVELOPER_MODE": "ON"
}
},
{
"name": "python",
"displayName": "python",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-python",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_PYTHON_BINDINGS": "ON"
}
},
{
"name": "static-qtwidgets",
"displayName": "static-qtwidgets",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-static-qtwidgets",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_STATIC": "ON"
}
},
{
"name": "static-qtquick",
"displayName": "static-qtquick",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-static-qtquick",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_STATIC": "ON",
"KDDockWidgets_QTQUICK": "ON"
}
},
{
"name": "qtwidgets6",
"displayName": "qtwidgets6",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-qtwidgets6",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QT6": "ON"
},
"environment": {
"PATH": "$env{HOME}/Qt/6.0.0/gcc_64/bin:$penv{PATH}"
}
},
{
"name": "qtquick6",
"displayName": "qtquick6",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build-qtquick6",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"KDDockWidgets_QTQUICK": "ON",
"KDDockWidgets_QT6": "ON"
},
"environment": {
"PATH": "$env{HOME}/Qt/6.0.0/gcc_64/bin:$penv{PATH}"
}
}
]
}

View File

@@ -1,7 +1,22 @@
* v1.2.0 (unreleased)
- QtQuick support
* v1.3.0 (unreleased)
- [TODO] QtQuick support
* v1.1.0 (unreleased)
* v1.2.1 (unreleased)
- Support for resizing dock widgets when they are in overlay/popup mode (autohide/sidebar feature)
* v1.2.0 (17 December 2020)
- Wayland support
- Added Flag_KeepAboveIfNotUtilityWindow, so you can have keep above even if
floating window is shown in the task-bar.
- Added DockWidget::windowActiveAboutToChange() signal
- Added support for Qt6 (PySide6 support is lacking)
- Added WASM support
* v1.1.1 (11 December 2020)
- Windows: Fixed a crash when clicking on the close button for floating windows in some situations (#110)
- Don't show dock/undock icon when dockwidget is not dockable (#99)
* v1.1.0 (26 October 2020)
- New drop indicator style type: Segmented Indicators
- Windows: Drop Shadow for floating windows
- Added AutoHide / SideBar support
@@ -12,6 +27,8 @@
- Added Config::Flag_TitleBarHasMinimizeButton
- Added Config::Flag_TitleBarNoFloatButton
- Added Config::Flag_AutoHideSupport
- Added Config::setTabbingAllowedFunc(TabbingAllowedFunc func)
- HDPI improvements, new high-res icons
- Bugfixes:
- Windows: Fixed windows not having proper minimum size.
- Windows: Fixed moving windows across screens with different DPI (#72)
@@ -19,9 +36,7 @@
- Fixed floating window's title not being correct (#74)
- Fixed focus scope not reacting when clicking on current tab (#71)
- Fixed floating window borders not being rendered correctly on HDPI due to rounding errors.
* v1.0.1 (unreleased)
- cmake/Python - don't require pkg-config, only use if available (#68)
- cmake/Python - don't require pkg-config, only use if available (#68)
* v1.0.0 (2 September 2020)
- PySide2 bindings

29
README-WASM.md Normal file
View File

@@ -0,0 +1,29 @@
WebAssembly
===========
KDDockWidgets works with WebAssembly with the following known limitations:
- Classic drop indicators are not supported, only the segmented ones. This is because
WASM doesn't support windows with translucency.
- It's slow while dragging Windows and resizing.
Please file a bug with Qt, as it's out of scope for KDDW to fix.
Build tips for KDDW:
====================
- Visit https://doc.qt.io/qt-5/wasm.html if you haven't yet
- Open a terminal suitable for WASM development (with the correct Qt and toolchain in PATH, etc)
- KDDockWidgets can be built with `cmake -DCMAKE_TOOLCHAIN_FILE=/usr/local/emsdk-1.39.8/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_FIND_ROOT_PATH=~/Qt/5.15.1/wasm_32/ -DKDDockWidgets_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Release`
(Adapt the paths to your own situation)
Builds tips for your own app:
=============================
- Link to KDDW (libkddockwidgets.so, or similar)
- As the build is static, don't forget to initialize KDDW's resources:
```
#ifdef QT_STATIC
Q_INIT_RESOURCE(kddockwidgets_resources);
#endif
```

34
README-Wayland.md Normal file
View File

@@ -0,0 +1,34 @@
Wayland support is done and has been tested on KDE (Kwin) and weston.
Limitations
============
Wayland works very differently than traditional desktops and imposes us some,
limitations. Here's a list of different behaviours which KDDockWidgets will have
when running on Wayland:
- A title bar can either be used for drag&dock or for moving the window around.
- For this reason, floating windows now have two title bars.
The native one, drawn by the server and the client one, drawn by KDDockWidgets.
The native one allows you to drag the window around but not drop/dock.
The client title bar allows you to perform a drag and drop/dock but not move the window around.
- You can detach a window by:
- Clicking the title-bar's float button
- Double-clicking the title bar of a docked widget
- Double-clicking a tab
- If no title bar is shown, double-clicking the empty space of the tab bar will detach
the entire group of tabbed dock widgets
- Layout save/restore won't restore the position of floating windows, as wayland
doesn't allow us to set geometry.
- Kwin specific:
- The pixmap that's shown during a drag can't be bigger than 250x250. Might be a bug.
All in all it's pretty decent and usable. Any further improvements should be done at the server or
protocol level now.

3
README-troubleshooting Normal file
View File

@@ -0,0 +1,3 @@
# PySide: `ImportError: DLL load failed while importing KDDockWidgets`
If you're getting this error with Python see issue #114 and the proposed solution there.

View File

@@ -43,6 +43,8 @@ Features
- Customize title bars
- Customize window frames
- Custom widget separators
- Crossplatform (macOS, Linux, Windows, WebAssembly, Wayland, X11/XCB, EGLFS are working)
See README-Wayland.md and README-WASM.md for platform specific information.
- Layouting engine honouring min/max size constraints and some size policies
- PySide2 bindings
- Clean codebase
@@ -98,6 +100,17 @@ The installation directory defaults to `c:\KDAB\KDDockWidgets-<version>` on Wind
and `/usr/local/KDAB/KDDockWidgets-<version>` on non-Windows. You can change this
location by passing the option `-DCMAKE_INSTALL_PREFIX=/install/path` to cmake.
== Using ==
From your CMake project, add
find_package(KDDockWidgets CONFIG)
and link to the imported target KDAB::kddockwidgets.
That's all you need to do (the imported target also brings in the include directories)
You may also need to point the CMAKE_MODULE_PATH environment variable depending
on where you installed KDDockWidgets.
Python Bindings
================
@@ -136,6 +149,7 @@ Supported Qt versions
======================
KDDockWidgets requires Qt >= 5.9 (or >=5.12 if Python bindings are enabled).
The QtQuick support will require Qt >= 5.15.
Qt 6 will be support as long as QTBUG-88611 is finished, most likely for 6.1.
Licensing
=========
@@ -170,3 +184,9 @@ 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 https://www.kdab.com to meet the people who write code like this.
Stay up-to-date with KDAB product announcements:
* [KDAB Newsletter](https://news.kdab.com)
* [KDAB Blogs](https://www.kdab.com/category/blogs)
* [KDAB on Twitter](https://twitter.com/KDABQt)

View File

@@ -11,7 +11,7 @@ if (NOT ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX)
SET(${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FILEPATH "Custom path to install python bindings.")
endif()
message(STATUS "PYTHON INSTALL PREFIX ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}")
message(STATUS "PYTHON INSTALL PREFIX ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}")
if (WIN32)
set(PATH_SEP "\;")
@@ -153,5 +153,5 @@ macro(CREATE_PYTHON_BINDINGS
LINK_FLAGS "-undefined dynamic_lookup")
endif()
install(TARGETS ${TARGET_NAME}
LIBRARY DESTINATION ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
LIBRARY DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/${TARGET_NAME})
endmacro()

View File

@@ -30,12 +30,12 @@
#
# Create variables for all the various install paths for the Qt version in use
# Make sure to have found Qt5 before using this.
# Make sure to have found Qt before using this.
# sets variables like QT_INSTALL_PREFIX, QT_INSTALL_DATA, QT_INSTALL_DOCS, etc.
# run qmake -query to see a full list
if(TARGET Qt5::qmake)
get_target_property(QT_QMAKE_EXECUTABLE Qt5::qmake LOCATION)
if(TARGET Qt${QT_MAJOR_VERSION}::qmake)
get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_MAJOR_VERSION}::qmake LOCATION)
else()
message(FATAL_ERROR "No supported Qt version found. Make sure you find Qt before calling this")
endif()

View File

@@ -11,7 +11,7 @@ from conans import ConanFile, CMake, tools
class KDDockWidgetsConan(ConanFile):
name = "kddockwidgets"
version = "1.1.95"
version = "1.2.0"
default_user = "kdab"
default_channel = "stable"
license = ("https://raw.githubusercontent.com/KDAB/KDDockWidgets/master/LICENSES/GPL-2.0-only.txt",

View File

@@ -1,3 +1,21 @@
kddockwidgets (1.2.0) release candidate; urgency=high
* 1.2.0 final
-- Allen Winter <allen.winter@kdab.com> Thu, 17 Dec 2020 12:00:00 -0500
kddockwidgets (1.1.1) release candidate; urgency=high
* 1.1.1 final
-- Allen Winter <allen.winter@kdab.com> Fri, 11 Dec 2020 12:00:00 -0500
kddockwidgets (1.1.0) release candidate; urgency=high
* 1.1.0 final
-- Allen Winter <allen.winter@kdab.com> Mon, 26 Oct 2020 12:00:00 -0500
kddockwidgets (1.0.0) release candidate; urgency=high
* 1.0.0 final

161
dev-scripts/build-all.dart Normal file
View File

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

View File

@@ -31,7 +31,7 @@ add_custom_command(
OUTPUT ${DOXYGEN_OUTPUT_DIR}/qch/kddockwidgets-api.qch
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
#handle a bug in doxygen where image files referred to in markdown are not copied the output
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/screencap.gif ${DOXYGEN_OUTPUT_DIR}/html
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/../../screencap.gif ${DOXYGEN_OUTPUT_DIR}/html
DEPENDS ${_dox_deps} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

@@ -13,6 +13,7 @@ cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_example)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
@@ -21,7 +22,7 @@ if(NOT TARGET kddockwidgets)
find_package(KDDockWidgets REQUIRED)
endif()
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
set(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
add_executable(kddockwidgets_example
main.cpp

View File

@@ -24,6 +24,8 @@
#include <QRandomGenerator>
#endif
#include <QApplication>
#include <stdlib.h>
#include <time.h>

View File

@@ -26,8 +26,10 @@ using namespace KDDockWidgets;
int main(int argc, char **argv)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication app(argc, argv);
app.setOrganizationName(QStringLiteral("KDAB"));
@@ -91,6 +93,9 @@ int main(int argc, char **argv)
QCommandLineOption noUtilityWindows("u", QCoreApplication::translate("main", "FloatingWindows will be normal windows instead of utility windows"));
parser.addOption(noUtilityWindows);
QCommandLineOption keepAbove("o", QCoreApplication::translate("main", "FloatingWindows will have Qt::WindowStaysOnTopHint. Implies not being an utility window (try it with -u too)"));
parser.addOption(keepAbove);
parser.addPositionalArgument("savedlayout", QCoreApplication::translate("main", "loads the specified json file at startup"));
#ifdef KDDOCKWIDGETS_SUPPORTS_NESTED_MAINWINDOWS
@@ -111,8 +116,13 @@ int main(int argc, char **argv)
QCommandLineOption noQtTool("no-qttool", QCoreApplication::translate("main", "(internal) Don't use Qt::Tool"));
QCommandLineOption noParentForFloating("no-parent-for-floating", QCoreApplication::translate("main", "(internal) FloatingWindows won't have a parent"));
QCommandLineOption nativeTitleBar("native-title-bar", QCoreApplication::translate("main", "(internal) FloatingWindows a native title bar"));
QCommandLineOption noDropIndicators("no-drop-indicators", QCoreApplication::translate("main", "(internal) Don't use any drop indicators"));
parser.addOption(noQtTool);
parser.addOption(noParentForFloating);
parser.addOption(nativeTitleBar);
parser.addOption(noDropIndicators);
# if defined(Q_OS_WIN)
QCommandLineOption noAeroSnap("no-aero-snap", QCoreApplication::translate("main", "(internal) Disable AeroSnap"));
@@ -143,12 +153,21 @@ int main(int argc, char **argv)
if (parser.isSet(noQtTool))
flags |= KDDockWidgets::Config::Flag_internal_DontUseQtToolWindowsForFloatingWindows;
if (parser.isSet(keepAbove))
flags |= KDDockWidgets::Config::Flag_KeepAboveIfNotUtilityWindow;
if (parser.isSet(noParentForFloating))
flags |= KDDockWidgets::Config::Flag_internal_DontUseParentForFloatingWindows;
if (parser.isSet(nativeTitleBar))
flags |= KDDockWidgets::Config::Flag_NativeTitleBar;
if (parser.isSet(noDropIndicators))
KDDockWidgets::DefaultWidgetFactory::s_dropIndicatorType = KDDockWidgets::DropIndicatorType::None;
# if defined(Q_OS_WIN)
if (parser.isSet(noAeroSnap))
flags &= ~KDDockWidgets::Config::Flag_AeroSnapWithClientDecos;
flags |= KDDockWidgets::Config::Flag_internal_NoAeroSnap;
# endif
#endif

View File

@@ -13,6 +13,7 @@ cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_minimal_example)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
@@ -21,7 +22,7 @@ if(NOT TARGET kddockwidgets)
find_package(KDDockWidgets REQUIRED)
endif()
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../dockwidgets/resources_example.qrc)
set(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../dockwidgets/resources_example.qrc)
add_executable(kddockwidgets_minimal_example
main.cpp

View File

@@ -15,6 +15,7 @@
#include <kddockwidgets/MainWindow.h>
#include <QStyleFactory>
#include <QApplication>
// clazy:excludeall=qstring-allocations
@@ -22,8 +23,10 @@ using namespace KDDockWidgets;
int main(int argc, char **argv)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication app(argc, argv);
app.setOrganizationName(QStringLiteral("KDAB"));

View File

@@ -1,7 +1,6 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Sergio Martins <sergio.martins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
@@ -9,26 +8,8 @@
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_example_quick)
add_subdirectory(customtitlebar)
add_subdirectory(dockwidgets)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
add_executable(kddockwidgets_example_quick
main.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_example_quick
PRIVATE
KDAB::kddockwidgets
)
set_compiler_flags(kddockwidgets_example_quick)
set_compiler_flags(kddockwidgets_customtitlebar_quick)

View File

@@ -0,0 +1,37 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Sergio Martins <sergio.martins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_customtitlebar_quick)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
set(RESOURCES_EXAMPLE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/resources_qtquick_example.qrc
${CMAKE_CURRENT_SOURCE_DIR}/../../dockwidgets/resources_example.qrc)
add_executable(kddockwidgets_customtitlebar_quick
main.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_customtitlebar_quick
PRIVATE
KDAB::kddockwidgets
)

View File

@@ -0,0 +1,36 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Item {
anchors.fill: parent
property alias background: background.source
property alias logo: logo.source
Image {
id: background
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
Image {
id: logo
fillMode: Image.PreserveAspectFit
anchors {
fill: parent
margins: 50
}
}
}
}

View File

@@ -0,0 +1,18 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Guest {
anchors.fill: parent
background: "qrc:/assets/triangles.png"
logo: "qrc:/assets/KDAB_bubble_white.png"
}

View File

@@ -11,10 +11,7 @@
import QtQuick 2.9
Rectangle {
color: "pink"
Guest {
anchors.fill: parent
Text {
text: "Guest Widget #2!"
}
logo: "qrc:/assets/KDAB_bubble_blue.png"
}

View File

@@ -11,10 +11,8 @@
import QtQuick 2.9
Rectangle {
color: "lightblue"
Guest {
anchors.fill: parent
Text {
text: "Guest Widget #1 !"
}
background: "qrc:/assets/base.png"
logo: "qrc:/assets/KDAB_bubble_fulcolor.png"
}

View File

@@ -0,0 +1,54 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.6
// Will be moved to a plugin in the future
import "qrc:/kddockwidgets/private/quick/qml/" as KDDW
KDDW.TitleBarBase {
id: root
color: "black"
border.color: "orange"
border.width: 2
heightWhenVisible: 50
Text {
color: "orange"
font.bold: true
text: root.title
anchors {
left: parent.left
leftMargin: 10
verticalCenter: root.verticalCenter
}
}
Rectangle {
id: closeButton
enabled: root.closeButtonEnabled
radius: 5
color: "green"
height: root.height - 20
width: height
anchors {
right: root.right
rightMargin: 10
verticalCenter: root.verticalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
root.closeButtonClicked();
}
}
}
}

View File

@@ -10,17 +10,41 @@
*/
#include "private/DockRegistry_p.h"
#include "private/quick/DockWidgetQuick.h"
#include "Config.h"
#include <kddockwidgets/Config.h>
#include <kddockwidgets/DockWidgetQuick.h>
#include <kddockwidgets/private/DockRegistry_p.h>
#include <kddockwidgets/FrameworkWidgetFactory.h>
#include <QQuickView>
#include <QGuiApplication>
class CustomFrameworkWidgetFactory : public KDDockWidgets::DefaultWidgetFactory
{
public:
~CustomFrameworkWidgetFactory() override;
QUrl titleBarFilename() const override
{
return QUrl("qrc:/MyTitleBar.qml");
}
};
CustomFrameworkWidgetFactory::~CustomFrameworkWidgetFactory() = default;
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
QGuiApplication app(argc, argv);
auto &config = KDDockWidgets::Config::self();
auto flags = config.flags();
config.setFlags(flags);
config.setFrameworkWidgetFactory(new CustomFrameworkWidgetFactory());
QQuickView view;
view.setObjectName("MainWindow QQuickView");
KDDockWidgets::Config::self().setQmlEngine(view.engine());
@@ -42,13 +66,10 @@ int main(int argc, char *argv[])
auto dw3 = new KDDockWidgets::DockWidgetQuick("Dock #3");
dw3->setWidget(QStringLiteral("qrc:/Guest3.qml"));
dw3->resize(QSize(800, 800));
dw3->show();
dw1->addDockWidgetToContainingWindow(dw3, KDDockWidgets::Location_OnRight);
KDDockWidgets::MainWindowBase *mainWindow = KDDockWidgets::DockRegistry::self()->mainwindows().constFirst();
mainWindow->addDockWidget(dw2, KDDockWidgets::Location_OnTop);
return app.exec();
}

View File

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

View File

@@ -12,9 +12,7 @@
import QtQuick 2.9
Rectangle {
color: "blue"
id: root
color: "green"
anchors.fill: parent
Text {
text: "Guest Widget!"
}
}

View File

@@ -0,0 +1,37 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Sergio Martins <sergio.martins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_example_quick)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
set(RESOURCES_EXAMPLE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/resources_qtquick_example.qrc
${CMAKE_CURRENT_SOURCE_DIR}/../../dockwidgets/resources_example.qrc)
add_executable(kddockwidgets_example_quick
main.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_example_quick
PRIVATE
KDAB::kddockwidgets
)

View File

@@ -0,0 +1,36 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Item {
anchors.fill: parent
property alias background: background.source
property alias logo: logo.source
Image {
id: background
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
Image {
id: logo
fillMode: Image.PreserveAspectFit
anchors {
fill: parent
margins: 50
}
}
}
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
import QtQuick.Controls 2.12
import com.kdab.dockwidgets 1.0 as KDDW
Guest {
anchors.fill: parent
background: "qrc:/assets/triangles.png"
logo: "qrc:/assets/KDAB_bubble_white.png"
KDDW.DockWidget {
id: another
uniqueName: "another1"
source: ":/Another.qml"
}
Button {
text: "Toggle Another"
anchors {
bottom: parent.bottom
left: parent.left
margins: 5
}
onClicked: {
if (another.dockWidget.visible) {
another.dockWidget.close();
} else {
another.dockWidget.show();
}
}
}
}

View File

@@ -11,10 +11,7 @@
import QtQuick 2.9
Rectangle {
color: "gray"
Guest {
anchors.fill: parent
Text {
text: "Guest Widget #3!"
}
logo: "qrc:/assets/KDAB_bubble_blue.png"
}

View File

@@ -0,0 +1,18 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Guest {
anchors.fill: parent
background: "qrc:/assets/base.png"
logo: "qrc:/assets/KDAB_bubble_fulcolor.png"
}

View File

@@ -0,0 +1,103 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include <kddockwidgets/Config.h>
#include <kddockwidgets/DockWidgetQuick.h>
#include <kddockwidgets/private/DockRegistry_p.h>
#include <kddockwidgets/FrameworkWidgetFactory.h>
#include <QQuickView>
#include <QGuiApplication>
#include <QCommandLineParser>
int main(int argc, char *argv[])
{
#ifdef Q_OS_WIN
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
QGuiApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("KDDockWidgets example application");
parser.addHelpOption();
#if defined(DOCKS_DEVELOPER_MODE)
QCommandLineOption noQtTool("no-qttool", QCoreApplication::translate("main", "(internal) Don't use Qt::Tool"));
QCommandLineOption noParentForFloating("no-parent-for-floating", QCoreApplication::translate("main", "(internal) FloatingWindows won't have a parent"));
QCommandLineOption nativeTitleBar("native-title-bar", QCoreApplication::translate("main", "(internal) FloatingWindows a native title bar"));
QCommandLineOption noDropIndicators("no-drop-indicators", QCoreApplication::translate("main", "(internal) Don't use any drop indicators"));
parser.addOption(noQtTool);
parser.addOption(noParentForFloating);
parser.addOption(nativeTitleBar);
parser.addOption(noDropIndicators);
# if defined(Q_OS_WIN)
QCommandLineOption noAeroSnap("no-aero-snap", QCoreApplication::translate("main", "(internal) Disable AeroSnap"));
parser.addOption(noAeroSnap);
# endif
#endif
auto flags = KDDockWidgets::Config::self().flags();
#if defined(DOCKS_DEVELOPER_MODE)
parser.process(app);
if (parser.isSet(noQtTool))
flags |= KDDockWidgets::Config::Flag_internal_DontUseQtToolWindowsForFloatingWindows;
if (parser.isSet(noParentForFloating))
flags |= KDDockWidgets::Config::Flag_internal_DontUseParentForFloatingWindows;
if (parser.isSet(nativeTitleBar))
flags |= KDDockWidgets::Config::Flag_NativeTitleBar;
else if (parser.isSet(noDropIndicators))
KDDockWidgets::DefaultWidgetFactory::s_dropIndicatorType = KDDockWidgets::DropIndicatorType::None;
# if defined(Q_OS_WIN)
if (parser.isSet(noAeroSnap))
flags |= KDDockWidgets::Config::Flag_internal_NoAeroSnap;
# endif
#endif
KDDockWidgets::Config::self().setFlags(flags);
QQuickView view;
view.setObjectName("MainWindow QQuickView");
KDDockWidgets::Config::self().setQmlEngine(view.engine());
view.resize(1000, 800);
view.show();
view.setResizeMode(QQuickView::SizeRootObjectToView);
auto dw1 = new KDDockWidgets::DockWidgetQuick("Dock #1");
view.setSource(QUrl("qrc:/main.qml"));
dw1->setWidget(QStringLiteral("qrc:/Guest1.qml"));
dw1->resize(QSize(800, 800));
dw1->show();
auto dw2 = new KDDockWidgets::DockWidgetQuick("Dock #2");
dw2->setWidget(QStringLiteral("qrc:/Guest2.qml"));
dw2->resize(QSize(800, 800));
dw2->show();
auto dw3 = new KDDockWidgets::DockWidgetQuick("Dock #3");
dw3->setWidget(QStringLiteral("qrc:/Guest3.qml"));
dw1->addDockWidgetToContainingWindow(dw3, KDDockWidgets::Location_OnRight);
KDDockWidgets::MainWindowBase *mainWindow = KDDockWidgets::DockRegistry::self()->mainwindows().constFirst();
mainWindow->addDockWidget(dw2, KDDockWidgets::Location_OnTop);
return app.exec();
}

View File

@@ -0,0 +1,23 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.6
import com.kdab.dockwidgets 1.0 as KDDW
KDDW.MainWindow {
id: root
Component.onCompleted: {
root.init("MyWindowName-1");
}
}

View File

@@ -4,5 +4,7 @@
<file>Guest1.qml</file>
<file>Guest2.qml</file>
<file>Guest3.qml</file>
<file>Guest.qml</file>
<file>Another.qml</file>
</qresource>
</RCC>

1
kddockwidgets-rpmlintrc Normal file
View File

@@ -0,0 +1 @@
addFilter("E: shlib-policy-name-error")

View File

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

View File

@@ -1,9 +1,10 @@
Name: kddockwidgets
Version: 1.0.0
Version: 1.2.0
Release: 1
Summary: KDAB's Dock Widget Framework for Qt
Source0: %{name}-%{version}.tar.gz
Source1: %{name}-%{version}.tar.gz.asc
Source2: %{name}-rpmlintrc
URL: https://github.com/KDAB/KDDockWidgets
Group: System/Libraries
License: GPL-2.0-only OR GPL-3.0-only
@@ -75,7 +76,6 @@ cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
%defattr(-,root,root)
%{_prefix}/share/doc/KDDockWidgets
%{_libdir}/libkddockwidgets.so.*
%{_libdir}/libkddockwidgets_multisplitter.so.*
%files devel
%defattr(-,root,root,-)
@@ -85,10 +85,15 @@ cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
%dir %{_libdir}/cmake/KDDockWidgets
%{_libdir}/cmake/KDDockWidgets/*
%{_libdir}/libkddockwidgets.so
%{_libdir}/libkddockwidgets_multisplitter.so
%changelog
* Wed Sep 02 2020 Allen Winter <allen.winter@kdb.com> 1.0.0
* Thu Dec 17 2020 Allen Winter <allen.winter@kdab.com> 1.2.0
1.2.0 final
* Fri Dec 11 2020 Allen Winter <allen.winter@kdab.com> 1.1.1
1.1.1 final
* Mon Oct 26 2020 Allen Winter <allen.winter@kdab.com> 1.1.0
1.1.0 final
* Wed Sep 02 2020 Allen Winter <allen.winter@kdab.com> 1.0.0
1.0.0 final
* Thu Aug 06 2020 Allen Winter <allen.winter@kdb.com> 0.99.9
* Thu Aug 06 2020 Allen Winter <allen.winter@kdab.com> 0.99.9
1.0.0 release candidate

View File

@@ -41,7 +41,7 @@ set(PyKDDockWidgets_typesystem_paths
# Include flags/path that will be set in 'target_include_directories'
set(PyKDDockWidgets_target_include_directories
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
# Libraries that will be necessary to link the target, this will used in the command 'target_link_libraries'
@@ -74,8 +74,8 @@ create_python_bindings(
${CMAKE_CURRENT_BINARY_DIR}
)
# Make moduled import from build dir works
# Make module import from build dir work
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py)
# install
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION ${${PROJECT_NAME}_PYTHON_BINDINGS_INSTALL_PREFIX}/PyKDDockWidgets)

View File

@@ -17,8 +17,11 @@
// Define PYTHON_BINDINGS this will be used in some part of c++ to skip problematic parts
#define PYTHON_BINDINGS
#include <MainWindowBase.h>
#include <MainWindow.h>
#include <DockWidgetBase.h>
#include <DockWidget.h>
#ifndef QT_WIDGETS_LIB
# define QT_WIDGETS_LIB
#endif
#include <kddockwidgets/MainWindowBase.h>
#include <kddockwidgets/MainWindow.h>
#include <kddockwidgets/DockWidgetBase.h>
#include <kddockwidgets/DockWidget.h>

View File

@@ -23,30 +23,68 @@ add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS
set(DOCKSLIBS_SRCS
Config.cpp
Config.h
Qt5Qt6Compat_p.h
FocusScope.cpp
FocusScope.h
FrameworkWidgetFactory.cpp
FrameworkWidgetFactory.h
DockWidgetBase.cpp
DockWidgetBase.h
MainWindowBase.cpp
MainWindowBase.h
LayoutSaver.cpp
LayoutSaver.h
LayoutSaver_p.h
private/MultiSplitter.cpp
private/MultiSplitter_p.h
private/Position.cpp
private/ObjectViewer.cpp
private/Position_p.h
private/DropIndicatorOverlayInterface.cpp
private/DropIndicatorOverlayInterface_p.h
private/DropArea.cpp
private/DropArea_p.h
private/FloatingWindow.cpp
private/FloatingWindow_p.h
private/Logging.cpp
private/Logging_p.h
private/TabWidget.cpp
private/TabWidget_p.h
private/TitleBar.cpp
private/TitleBar_p.h
private/SideBar.cpp
private/SideBar_p.h
private/DockRegistry.cpp
private/DockRegistry_p.h
private/Draggable.cpp
private/Draggable_p.h
private/WindowBeingDragged.cpp
private/WindowBeingDragged_p.h
private/DragController.cpp
private/DragController_p.h
private/Frame.cpp
private/Frame_p.h
private/DropAreaWithCentralFrame.cpp
private/DropAreaWithCentralFrame_p.h
private/WidgetResizeHandler.cpp
private/WidgetResizeHandler_p.h
private/indicators/NullIndicators.cpp
private/indicators/NullIndicators_p.h
private/indicators/ClassicIndicators.cpp
private/indicators/ClassicIndicators_p.h
private/indicators/ClassicIndicatorsWindow.cpp
private/indicators/ClassicIndicatorsWindow_p.h
private/multisplitter/Item.cpp
private/multisplitter/Item_p.h
private/multisplitter/Logging.cpp
private/multisplitter/Logging_p.h
private/multisplitter/MultiSplitterConfig.cpp
private/multisplitter/MultiSplitterConfig.h
private/multisplitter/Separator.cpp
private/multisplitter/Separator_p.h
private/multisplitter/Widget.cpp
private/multisplitter/Widget.h
)
set(DOCKS_INSTALLABLE_INCLUDES
@@ -55,6 +93,7 @@ set(DOCKS_INSTALLABLE_INCLUDES
FrameworkWidgetFactory.h
DockWidgetBase.h
KDDockWidgets.h
Qt5Qt6Compat_p.h
FocusScope.h
QWidgetAdapter.h
LayoutSaver.h
@@ -70,6 +109,7 @@ set(DOCKS_INSTALLABLE_PRIVATE_INCLUDES
private/SideBar_p.h
private/TitleBar_p.h
private/WindowBeingDragged_p.h
private/DockRegistry_p.h
)
set(DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES
@@ -80,38 +120,74 @@ set(DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES
private/widgets/FrameWidget_p.h
private/widgets/TabBarWidget_p.h
private/widgets/TabWidgetWidget_p.h
private/widgets/TabWidget_p.h
private/TabWidget_p.h
)
if(${PROJECT_NAME}_QTQUICK)
set(DOCKSLIBS_SRCS ${DOCKSLIBS_SRCS}
private/quick/DockWidgetQuick.cpp
DockWidgetQuick.cpp
DockWidgetQuick.h
private/quick/DockWidgetInstantiator.cpp
private/quick/DockWidgetInstantiator_p.h
private/quick/QWidgetAdapter_quick.cpp
private/quick/QWidgetAdapter_quick_p.h
private/quick/FloatingWindowQuick.cpp
private/quick/FloatingWindowQuick_p.h
private/quick/TabWidgetQuick.cpp
private/quick/TabWidgetQuick_p.h
private/quick/TabBarQuick.cpp
private/quick/TabBarQuick_p.h
private/quick/TitleBarQuick.cpp
private/quick/TitleBarQuick_p.h
private/quick/QmlTypes.cpp
private/quick/QmlTypes.h
private/quick/FrameQuick.cpp
private/quick/FrameQuick_p.h
private/quick/RubberBandQuick.cpp
private/quick/RubberBandQuick.h
private/quick/MainWindowQuick.cpp
private/quick/MainWindowQuick_p.h
private/quick/MainWindowWrapper.cpp
)
qt5_add_resources(RESOURCES_QUICK ${CMAKE_CURRENT_SOURCE_DIR}/qtquick.qrc)
private/quick/MainWindowWrapper_p.h
private/multisplitter/Widget_quick.cpp
private/multisplitter/Widget_quick.h
private/multisplitter/Separator_quick.cpp
private/multisplitter/Separator_quick.h
private/multisplitter/Rubberband_quick.cpp
private/multisplitter/Rubberband_quick.h
kddockwidgets_qtquick.qrc)
else()
set(DOCKSLIBS_SRCS ${DOCKSLIBS_SRCS}
private/DebugWindow.cpp
private/DebugWindow_p.h
private/ObjectViewer.cpp
private/ObjectViewer_p.h
MainWindow.cpp
private/widgets/TabWidget.cpp
MainWindow.h
DockWidget.h
private/multisplitter/Widget_qwidget.cpp
private/multisplitter/Widget_qwidget.h
private/multisplitter/Separator_qwidget.cpp
private/multisplitter/Separator_qwidget.h
private/widgets/TabBarWidget.cpp
private/widgets/TabBarWidget_p.h
private/widgets/FloatingWindowWidget.cpp
private/widgets/FloatingWindowWidget_p.h
private/widgets/FrameWidget.cpp
private/widgets/FrameWidget_p.h
private/widgets/SideBarWidget.cpp
private/widgets/SideBarWidget_p.h
private/widgets/TabWidgetWidget.cpp
private/widgets/TabWidgetWidget_p.h
private/widgets/TitleBarWidget.cpp
private/widgets/TitleBarWidget_p.h
private/widgets/DockWidget.cpp
private/widgets/QWidgetAdapter_widgets.cpp
private/widgets/QWidgetAdapter_widgets_p.h
private/indicators/SegmentedIndicators.cpp
private/indicators/SegmentedIndicators_p.h
# private/indicators/AnimatedIndicators.cpp
)
)
set(DOCKS_INSTALLABLE_INCLUDES
${DOCKS_INSTALLABLE_INCLUDES}
@@ -127,9 +203,7 @@ else()
set(IS_CLANG_BUILD FALSE)
endif()
add_subdirectory(private/multisplitter)
qt5_add_resources(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc)
set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/kddockwidgets_resources.qrc)
add_library(kddockwidgets ${KDDockWidgets_LIBRARY_MODE} ${DOCKSLIBS_SRCS} ${DOCKS_INSTALLABLE_INCLUDES} ${RESOURCES} ${RESOURCES_QUICK})
add_library(KDAB::kddockwidgets ALIAS kddockwidgets)
@@ -138,12 +212,11 @@ set_compiler_flags(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
${CMAKE_CURRENT_SOURCE_DIR}/private/multisplitter/
)
target_compile_definitions(kddockwidgets
@@ -168,19 +241,23 @@ if(CMAKE_COMPILER_IS_GNUCXX OR IS_CLANG_BUILD)
endif()
if(${PROJECT_NAME}_QTQUICK)
target_link_libraries(kddockwidgets PUBLIC Qt5::Widgets Qt5::Quick Qt5::QuickControls2 kddockwidgets_multisplitter PRIVATE Qt5::GuiPrivate)
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::Widgets Qt${QT_MAJOR_VERSION}::Quick Qt${QT_MAJOR_VERSION}::QuickControls2)
else()
target_link_libraries(kddockwidgets PUBLIC Qt5::Widgets kddockwidgets_multisplitter PRIVATE Qt5::GuiPrivate)
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::Widgets)
endif()
if (WIN32)
target_link_libraries(kddockwidgets PRIVATE Dwmapi)
elseif(NOT APPLE)
find_package(Qt5X11Extras)
target_link_libraries(kddockwidgets PUBLIC Qt5::X11Extras)
target_link_libraries(kddockwidgets PRIVATE Qt${QT_MAJOR_VERSION}::GuiPrivate Dwmapi)
elseif(NOT APPLE AND NOT EMSCRIPTEN AND NOT ${PROJECT_NAME}_QT6)
find_package(Qt${QT_MAJOR_VERSION}X11Extras)
target_link_libraries(kddockwidgets PUBLIC Qt${QT_MAJOR_VERSION}::X11Extras)
endif()
set_target_properties(kddockwidgets PROPERTIES VERSION ${${PROJECT_NAME}_SOVERSION})
set_target_properties(kddockwidgets PROPERTIES
SOVERSION ${${PROJECT_NAME}_SOVERSION}
VERSION ${${PROJECT_NAME}_VERSION}
)
#version libraries on Windows
if(WIN32)
set(postfix ${${PROJECT_NAME}_VERSION_MAJOR})
@@ -208,7 +285,6 @@ install(FILES private/multisplitter/Widget.h DESTINATION include/kddockwidgets/p
install(FILES private/multisplitter/Widget_qwidget.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Separator_p.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/Separator_qwidget.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES private/multisplitter/multisplitter_export.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION include/kddockwidgets/private/widgets)
install(FILES private/indicators/ClassicIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
@@ -238,6 +314,7 @@ if(${PROJECT_NAME}_DEVELOPER_MODE)
if (NOT ${PROJECT_NAME}_QTQUICK) # TODO: We can support it
add_executable(kddockwidgets_linter layoutlinter_main.cpp)
target_link_libraries(kddockwidgets_linter kddockwidgets kddockwidgets_multisplitter Qt5::Widgets)
target_link_libraries(kddockwidgets_linter kddockwidgets Qt${QT_MAJOR_VERSION}::Widgets)
endif()
endif()

View File

@@ -18,15 +18,19 @@
#include "Config.h"
#include "multisplitter/MultiSplitterConfig.h"
#include "multisplitter/Widget_qwidget.h"
#include "multisplitter/Widget.h"
#include "DockRegistry_p.h"
#include "FrameworkWidgetFactory.h"
#include "Utils_p.h"
#include <QApplication>
#include <QDebug>
#include <QOperatingSystemVersion>
#ifdef KDDOCKWIDGETS_QTQUICK
# include <QQmlEngine>
# include <QQmlContext>
#endif
namespace KDDockWidgets
{
@@ -48,7 +52,8 @@ public:
QQmlEngine *m_qmlEngine = nullptr;
DockWidgetFactoryFunc m_dockWidgetFactoryFunc = nullptr;
MainWindowFactoryFunc m_mainWindowFactoryFunc = nullptr;
FrameworkWidgetFactory *m_frameworkWidgetFactory;
TabbingAllowedFunc m_tabbingAllowedFunc = nullptr;
FrameworkWidgetFactory *m_frameworkWidgetFactory = nullptr;
Flags m_flags = Flag_Default;
qreal m_draggedWindowOpacity = Q_QNAN;
};
@@ -157,6 +162,47 @@ qreal Config::draggedWindowOpacity() const
return d->m_draggedWindowOpacity;
}
void Config::setTabbingAllowedFunc(TabbingAllowedFunc func)
{
d->m_tabbingAllowedFunc = func;
}
TabbingAllowedFunc Config::tabbingAllowedFunc() const
{
return d->m_tabbingAllowedFunc;
}
void Config::setAbsoluteWidgetMinSize(QSize size)
{
if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/ false)) {
qWarning() << Q_FUNC_INFO << "Only use this function at startup before creating any DockWidget or MainWindow";
return;
}
Layouting::Item::hardcodedMinimumSize = size;
}
QSize Config::absoluteWidgetMinSize() const
{
return Layouting::Item::hardcodedMinimumSize;
}
void Config::setAbsoluteWidgetMaxSize(QSize size)
{
if (!DockRegistry::self()->isEmpty(/*excludeBeingDeleted=*/ false)) {
qWarning() << Q_FUNC_INFO << "Only use this function at startup before creating any DockWidget or MainWindow";
return;
}
Layouting::Item::hardcodedMaximumSize = size;
}
QSize Config::absoluteWidgetMaxSize() const
{
return Layouting::Item::hardcodedMaximumSize;
}
#ifdef KDDOCKWIDGETS_QTQUICK
void Config::setQmlEngine(QQmlEngine *qmlEngine)
{
if (d->m_qmlEngine) {
@@ -164,13 +210,22 @@ void Config::setQmlEngine(QQmlEngine *qmlEngine)
return;
}
if (!qmlEngine) {
qWarning() << Q_FUNC_INFO << "Null QML engine";
return;
}
d->m_qmlEngine = qmlEngine;
QQmlContext *context = qmlEngine->rootContext();
context->setContextProperty(QStringLiteral("_kddw_widgetFactory"), d->m_frameworkWidgetFactory);
}
QQmlEngine *Config::qmlEngine() const
{
return d->m_qmlEngine;
}
#endif
void Config::Private::fixFlags()
{
@@ -206,9 +261,19 @@ void Config::Private::fixFlags()
}
#endif
#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
#if (!defined(Q_OS_WIN) && !defined(Q_OS_MACOS))
// QtQuick doesn't support AeroSnap yet. Some problem with the native events not being received...
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
#endif
#if defined(DOCKS_DEVELOPER_MODE)
// We allow to disable aero-snap during development
if (m_flags & Flag_internal_NoAeroSnap) {
// The only way to disable AeroSnap
m_flags = m_flags & ~Flag_AeroSnapWithClientDecos;
}
#endif
}
}

View File

@@ -23,6 +23,7 @@
QT_BEGIN_NAMESPACE
class QQmlEngine;
class QSize;
QT_END_NAMESPACE
namespace KDDockWidgets
@@ -35,6 +36,14 @@ class FrameworkWidgetFactory;
typedef KDDockWidgets::DockWidgetBase* (*DockWidgetFactoryFunc)(const QString &name);
typedef KDDockWidgets::MainWindowBase* (*MainWindowFactoryFunc)(const QString &name);
/// @brief Function to allow the user more granularity to disallow dock widgets to tab together
/// @param source The dock widgets being dragged
/// @param target The dock widgets within an existing docked tab group
/// @return true if the docking is allowed.
/// @sa setTabbingAllowedFunc
typedef bool (*TabbingAllowedFunc)(const QVector<DockWidgetBase*> &source,
const QVector<DockWidgetBase*> &target);
/**
* @brief Singleton to allow to choose certain behaviours of the framework.
*
@@ -53,29 +62,31 @@ public:
///@brief Flag enum to tune certain behaviours, the defaults are Flag_Default
enum Flag {
Flag_None = 0, ///> No option set
Flag_NativeTitleBar = 1, ///> Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise. This is mutually exclusive with Flag_AeroSnap
Flag_AeroSnapWithClientDecos = 2, ///> Deprecated. This is now default and cannot be turned off. Moving a window on Windows 10 uses native moving, as that works well across screens with different HDPI settings. There's no reason to use manual client/Qt window moving.
Flag_AlwaysTitleBarWhenFloating = 4, ///> Floating windows will have a title bar even if Flag_HideTitleBarWhenTabsVisible is specified. Unneeded if Flag_HideTitleBarWhenTabsVisible isn't specified, as that's the default already.
Flag_HideTitleBarWhenTabsVisible = 8, ///> Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one,
Flag_AllowReorderTabs = 32, /// Allows user to re-order tabs by dragging them
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_TitleBarIsFocusable = 512, /// You can click the title bar and it will focus the last focused widget in the focus scope. If no previously focused widget then it focuses the user's dock widget guest, which should accept focus or use a focus proxy.
Flag_LazyResize = 1024, /// The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
Flag_None = 0, ///< No option set
Flag_NativeTitleBar = 1, ///< Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise. This is mutually exclusive with Flag_AeroSnap
Flag_AeroSnapWithClientDecos = 2, ///< Deprecated. This is now default and cannot be turned off. Moving a window on Windows 10 uses native moving, as that works well across screens with different HDPI settings. There's no reason to use manual client/Qt window moving.
Flag_AlwaysTitleBarWhenFloating = 4, ///< Floating windows will have a title bar even if Flag_HideTitleBarWhenTabsVisible is specified. Unneeded if Flag_HideTitleBarWhenTabsVisible isn't specified, as that's the default already.
Flag_HideTitleBarWhenTabsVisible = 8, ///< Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
Flag_AlwaysShowTabs = 16, ///< Always show tabs, even if there's only one,
Flag_AllowReorderTabs = 32, ///< Allows user to re-order tabs by dragging them
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_TitleBarIsFocusable = 512, ///< You can click the title bar and it will focus the last focused widget in the focus scope. If no previously focused widget then it focuses the user's dock widget guest, which should accept focus or use a focus proxy.
Flag_LazyResize = 1024, ///< The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
// These two are internal, for testing purposes across platforms. Use Flag_DontUseUtilityFloatingWindows instead.
Flag_internal_DontUseQtToolWindowsForFloatingWindows = 0x800, ///> FloatingWindows will use Qt::Window instead of Qt::Tool. Internal, use Flag_DontUseUtilityFloatingWindows instead.
Flag_internal_DontUseParentForFloatingWindows = 0x1000, ///> FloatingWindows won't have a parent top-level. Internal, use Flag_DontUseUtilityFloatingWindows instead.
Flag_internal_DontUseQtToolWindowsForFloatingWindows = 0x800, ///< FloatingWindows will use Qt::Window instead of Qt::Tool. Internal, use Flag_DontUseUtilityFloatingWindows instead.
Flag_internal_DontUseParentForFloatingWindows = 0x1000, ///< FloatingWindows won't have a parent top-level. Internal, use Flag_DontUseUtilityFloatingWindows instead.
Flag_DontUseUtilityFloatingWindows = Flag_internal_DontUseQtToolWindowsForFloatingWindows | Flag_internal_DontUseParentForFloatingWindows,
Flag_TitleBarHasMinimizeButton = 0x2000 | Flag_DontUseUtilityFloatingWindows, ///> The title bar will have a minimize button when floating. This implies Flag_DontUseUtilityFloatingWindows too, otherwise they wouldn't appear in the task bar.
Flag_TitleBarNoFloatButton = 0x4000, ///> The TitleBar won't show the float button
Flag_AutoHideSupport = 0x8000 | Flag_TitleBarNoFloatButton, ///> Supports minimizing dock widgets to the side-bar.
///> By default it also turns off the float button, but you can remove Flag_TitleBarNoFloatButton to have both.
Flag_Default = Flag_AeroSnapWithClientDecos ///> The defaults
Flag_TitleBarHasMinimizeButton = 0x2000 | Flag_DontUseUtilityFloatingWindows, ///< The title bar will have a minimize button when floating. This implies Flag_DontUseUtilityFloatingWindows too, otherwise they wouldn't appear in the task bar.
Flag_TitleBarNoFloatButton = 0x4000, ///< The TitleBar won't show the float button
Flag_AutoHideSupport = 0x8000 | Flag_TitleBarNoFloatButton, ///< Supports minimizing dock widgets to the side-bar.
///< By default it also turns off the float button, but you can remove Flag_TitleBarNoFloatButton to have both.
Flag_KeepAboveIfNotUtilityWindow = 0x10000, ///< Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows, you might still want them to keep above and not minimize when you focus the main window.
Flag_internal_NoAeroSnap = 0x20000, ///< Internal flag, only for development. Disables Aero-snap.
Flag_Default = Flag_AeroSnapWithClientDecos ///< The defaults
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -150,9 +161,50 @@ public:
///By default it's 1.0, fully opaque
qreal draggedWindowOpacity() const;
/**
* @brief Allows the user to intercept a docking attempt to center (tabbed) and disallow it.
*
* Whenever the user tries to tab two widgets together, the framework will call @p func. If
* it returns true, then tabbing is allowed, otherwise not.
*
* Example:
* @code
* #include <kddockwidgets/Config.h>
* (...)
*
* auto func = [] (const KDDockWidgets::DockWidgetBase::List &source,
* const KDDockWidgets::DockWidgetBase::List &target)
* {
* // disallows dockFoo to be tabbed with dockBar.
* return !(source.contains(dockFoo) && target.contains(dockBar));
* }
* @endcode
* KDDockWidgets::Config::self()->setTabbingAllowedFunc(func);
*/
void setTabbingAllowedFunc(TabbingAllowedFunc func);
///@brief Used internally by the framework. Returns the function which was passed to setTabbingAllowedFunc()
///By default it's nullptr.
///@sa setTabbingAllowedFunc().
TabbingAllowedFunc tabbingAllowedFunc() const;
///@brief Sets the minimum size a dock widget can have.
/// Widgets can still provide their own min-size and it will be respected, however it can never be
/// smaller than this one.
void setAbsoluteWidgetMinSize(QSize size);
QSize absoluteWidgetMinSize() const;
///@brief Sets the maximum size a dock widget can have.
/// Widgets can still provide their own max-size and it will be respected, however it can never be
/// bigger than this one.
void setAbsoluteWidgetMaxSize(QSize size);
QSize absoluteWidgetMaxSize() const;
#ifdef KDDOCKWIDGETS_QTQUICK
///@brief Sets the QQmlEngine to use. Applicable only when using QtQuick.
void setQmlEngine(QQmlEngine *);
QQmlEngine* qmlEngine() const;
#endif
private:
Q_DISABLE_COPY(Config)

View File

@@ -16,7 +16,6 @@
#include "Utils_p.h"
#include "DockRegistry_p.h"
#include "DropArea_p.h"
#include "multisplitter/Item_p.h"
#include "Config.h"
#include "TitleBar_p.h"
#include "FrameworkWidgetFactory.h"
@@ -24,7 +23,6 @@
#include "WindowBeingDragged_p.h"
#include "SideBar_p.h"
#include <QAction>
#include <QEvent>
#include <QCloseEvent>
#include <QTimer>
@@ -134,6 +132,7 @@ public:
bool m_updatingToggleAction = false;
bool m_updatingFloatAction = false;
bool m_isForceClosing = false;
QSize m_lastOverlayedSize = QSize(0, 0);
};
DockWidgetBase::DockWidgetBase(const QString &name, Options options)
@@ -146,6 +145,9 @@ DockWidgetBase::DockWidgetBase(const QString &name, Options options)
if (name.isEmpty())
qWarning() << Q_FUNC_INFO << "Name can't be null";
setAttribute(Qt::WA_PendingMoveEvent, false);
qApp->installEventFilter(this);
}
DockWidgetBase::~DockWidgetBase()
@@ -183,7 +185,7 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, AddingOption addi
Frame *frame = this->frame();
if (frame) {
if (frame->contains(other)) {
if (frame->containsDockWidget(other)) {
qWarning() << Q_FUNC_INFO << "Already contains" << other;
return;
}
@@ -515,6 +517,11 @@ bool DockWidgetBase::hasPreviousDockedLocation() const
return d->m_lastPositions.isValid();
}
QSize DockWidgetBase::lastOverlayedSize() const
{
return d->m_lastOverlayedSize;
}
FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
{
qCDebug(creation) << "DockWidget::morphIntoFloatingWindow() this=" << this
@@ -528,7 +535,7 @@ FloatingWindow *DockWidgetBase::morphIntoFloatingWindow()
if (geo.isNull()) {
geo = geometry();
if (!testAttribute(Qt::WA_Moved)) { // If user already moved it, we don't interfere
if (!testAttribute(Qt::WA_PendingMoveEvent)) { // If user already moved it, we don't interfere
const QPoint center = d->defaultCenterPosForFloating();
if (!center.isNull())
geo.moveCenter(center);
@@ -594,6 +601,16 @@ void DockWidgetBase::updateFloatAction()
d->updateFloatAction();
}
bool DockWidgetBase::eventFilter(QObject *watched, QEvent *event)
{
const bool isWindowActivate = event->type() == QEvent::WindowActivate;
const bool isWindowDeactivate = event->type() == QEvent::WindowDeactivate;
if ((isWindowActivate || isWindowDeactivate) && watched == window())
Q_EMIT windowActiveAboutToChange(isWindowActivate);
return QWidgetAdapter::eventFilter(watched, event);
}
QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
{
MainWindowBase::List mainWindows = DockRegistry::self()->mainwindows();
@@ -653,14 +670,12 @@ 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();
}
void DockWidgetBase::Private::close()
@@ -786,6 +801,19 @@ void DockWidgetBase::onHidden(bool spontaneous)
}
}
bool DockWidgetBase::onResize(QSize newSize)
{
if (isOverlayed()) {
if (auto frame = this->frame()) {
d->m_lastOverlayedSize = frame->QWidgetAdapter::size();
} else {
qWarning() << Q_FUNC_INFO << "Overlayed dock widget without frame shouldn't happen";
}
}
return QWidgetAdapter::onResize(newSize);
}
void DockWidgetBase::onCloseEvent(QCloseEvent *e)
{
e->accept(); // By default we accept, means DockWidget closes

View File

@@ -30,10 +30,81 @@ QT_BEGIN_NAMESPACE
class QAction;
QT_END_NAMESPACE
class TestDocks;
namespace Layouting {
class Item;
}
#if defined(QT_WIDGETS_LIB)
# include <QAction>
#else
// A QAction for QtQuick. Just so it compiles, for now
class QAction : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
bool isChecked() const {
return m_isChecked ;
}
void setCheckable(bool is) {
m_isCheckable = is;
}
void setText(const QString &text) {
m_text = text;
}
void setToolTip(const QString &text) {
m_toolTip = text;
}
QString toolTip() const {
returm m_toolTip;
}
bool enabled() const {
return m_enabled;
}
void setEnabled(bool enabled) {
m_enabled = enabled;
}
bool checked() const {
return m_checked;
}
void setChecked(bool checked) {
m_checked = checked;
}
bool isEnabled() const {
return m_enabled;
}
void toggle() {
m_enabled = !m_enabled;
Q_EMIT toggled(m_enabled);
}
Q_SIGNALS:
bool toggled(bool);
private:
QString m_text;
QString m_toolTip;
bool m_isChecked = false;
bool m_isCheckable = false;
bool m_enabled = false;
bool m_checked = false;
};
#endif
namespace KDDockWidgets {
struct LastPositions;
@@ -46,6 +117,7 @@ class TabWidget;
class TitleBar;
class MainWindowBase;
class StateDragging;
class FrameQuick;
/**
* @brief The DockWidget base-class. DockWidget and DockWidgetBase are only
@@ -256,7 +328,7 @@ public:
* @brief Like QWidget::close() but the hosted widget won't be asked if we
* should close.
*/
void forceClose();
Q_INVOKABLE void forceClose();
/**
* @brief Returns this dock widget's title bar.
@@ -301,7 +373,7 @@ public:
QStringList affinities() const;
/// @brief Equivalent to QWidget::show(), but it's optimized to reduce flickering on some platforms
void show();
Q_INVOKABLE void show();
/// @brief Brings the dock widget to the front.
///
@@ -310,7 +382,7 @@ public:
/// - 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_INVOKABLE void raise();
/**
* @brief Returns whether widget() is a KDDockWidget::MainWindow
@@ -368,6 +440,10 @@ public:
/// When you call dockWidget->setFloating(false) it will only dock if it knows where to.
bool hasPreviousDockedLocation() const;
/// @brief returns the last size the widget has when overlayed
/// Empty otherwise
QSize lastOverlayedSize() const;
Q_SIGNALS:
///@brief signal emitted when the parent changed
void parentChanged();
@@ -407,12 +483,22 @@ Q_SIGNALS:
///Only relevant for the auto-hide/sidebar feature
void removedFromSideBar();
///@brief Emitted when the top-level window this dock widget is in is activated or deactivated
///This is convenience to replace tracking dockWidget->window(), since the window changes when
///docking and undocking
///
/// It's called 'aboutTo' because it's done in an event filter and the target window doesn't
/// have it's 'activeWindow' property updated yet at this point.
void windowActiveAboutToChange(bool activated);
protected:
void onParentChanged();
void onShown(bool spontaneous);
void onHidden(bool spontaneous);
#ifndef PYTHON_BINDINGS //Pyside bug: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1327
void onCloseEvent(QCloseEvent *e) override;
bool onResize(QSize newSize) override;
#endif
#if defined(DOCKS_DEVELOPER_MODE)
@@ -439,7 +525,7 @@ private:
friend class MultiSplitter;
friend class Frame;
friend class DropArea;
friend class TestDocks;
friend class ::TestDocks;
friend class StateDragging;
friend class KDDockWidgets::TabWidget;
friend class KDDockWidgets::TitleBar;
@@ -447,6 +533,7 @@ private:
friend class KDDockWidgets::DockRegistry;
friend class KDDockWidgets::LayoutSaver;
friend class KDDockWidgets::MainWindowBase;
friend class KDDockWidgets::FrameQuick;
/**
* @brief the Frame which contains this dock widgets.
@@ -478,6 +565,9 @@ private:
///@brief Updates the floatAction state
void updateFloatAction();
///@reimp
bool eventFilter(QObject *, QEvent *) override;
class Private;
Private *const d;
};

View File

@@ -66,7 +66,7 @@ void DockWidgetQuick::setWidget(const QString &qmlFilename)
setWidget(adapter);
}
void DockWidgetQuick::setWidget(QWidgetOrQuick *widget)
void DockWidgetQuick::setWidget(QWidgetAdapter *widget)
{
widget->QWidgetAdapter::setParent(this);
QWidgetAdapter::makeItemFillParent(widget);
@@ -87,3 +87,23 @@ bool DockWidgetQuick::event(QEvent *e)
return DockWidgetBase::event(e);
}
QSize DockWidgetQuick::minimumSize() const
{
if (QWidgetAdapter *guestWidget = widget()) {
// The guests min-size is the same as the widget's, there's no spacing or margins.
return guestWidget->minimumSize();
}
return DockWidgetBase::minimumSize();
}
QSize DockWidgetQuick::maximumSize() const
{
if (QWidgetAdapter *guestWidget = widget()) {
// The guests max-size is the same as the widget's, there's no spacing or margins.
return guestWidget->maximumSize();
}
return DockWidgetBase::maximumSize();
}

View File

@@ -55,7 +55,13 @@ public:
void setWidget(const QString &qmlFilename);
/// @reimp
void setWidget(QWidgetOrQuick *widget) override;
void setWidget(QWidgetAdapter *widget) override;
/// @reimp
QSize minimumSize() const override;
/// @reimp
QSize maximumSize() const override;
protected:
bool event(QEvent *e) override;

View File

@@ -18,9 +18,12 @@
#include "FocusScope.h"
#include "TitleBar_p.h"
#include "Frame_p.h"
#include "DockWidgetBase.h"
#include "DockRegistry_p.h"
#include <QObject>
#include <QApplication>
#include <QGuiApplication>
#include <QPointer>
using namespace KDDockWidgets;
@@ -45,6 +48,7 @@ public:
void setIsFocused(bool);
void onFocusObjectChanged(QObject *);
bool isInFocusScope(WidgetType *) const;
void emitDockWidgetFocusChanged();
FocusScope *const q;
QWidgetAdapter *const m_thisWidget;
@@ -101,24 +105,30 @@ void FocusScope::Private::setIsFocused(bool is)
if (is != m_isFocused) {
m_isFocused = is;
if (is)
emitDockWidgetFocusChanged();
if (!m_inCtor) // Hack so we don't call pure-virtual
Q_EMIT q->isFocusedChanged();
/* Q_EMIT */ q->isFocusedChangedCallback();
}
}
void FocusScope::Private::onFocusObjectChanged(QObject *obj)
{
auto widget = qobject_cast<WidgetType*>(obj);
if (!widget)
if (!widget) {
setIsFocused(false);
return;
}
const bool is = isInFocusScope(widget);
if (is && m_lastFocusedInScope != widget && !qobject_cast<TitleBar*>(obj)) {
m_lastFocusedInScope = widget;
Q_EMIT q->focusedWidgetChanged();
setIsFocused(is);
/* Q_EMIT */ q->focusedWidgetChangedCallback();
} else {
setIsFocused(is);
}
setIsFocused(is);
}
bool FocusScope::Private::isInFocusScope(WidgetType *widget) const
@@ -133,3 +143,33 @@ bool FocusScope::Private::isInFocusScope(WidgetType *widget) const
return false;
}
void FocusScope::Private::emitDockWidgetFocusChanged()
{
auto p = qobject_cast<WidgetType*>(qApp->focusObject());
if (!p) return;
// Find the nearest DockWidget and send the focusChangedSignal
while (p) {
if (auto frame = qobject_cast<Frame*>(p)) {
// Special case: The focused widget is inside the frame but not inside the dockwidget.
// For example, it's a line edit in the QTabBar. We still need to send the signal for
// the current dw in the tab group
if (auto dw = frame->currentDockWidget()) {
DockRegistry::self()->setFocusedDockWidget(dw);
}
break;
}
if (p == m_thisWidget)
break;
if (auto dw = qobject_cast<DockWidgetBase*>(p)) {
DockRegistry::self()->setFocusedDockWidget(dw);
break;
}
p = KDDockWidgets::Private::parentWidget(p);
}
}

View File

@@ -25,7 +25,7 @@
namespace KDDockWidgets
{
///@brief Allows to implement a similar functionality to QtQuick's FocusScope item, in QtWidgets
class FocusScope
class DOCKS_EXPORT FocusScope
{
public:
///@brief constructor
@@ -48,9 +48,10 @@ public:
void focus(Qt::FocusReason = Qt::OtherFocusReason);
/*Q_SIGNALS:*/
protected:
///@brief reimplement in the 1st QObject derived class
virtual void isFocusedChanged() = 0;
virtual void focusedWidgetChanged() = 0;
virtual void isFocusedChangedCallback() = 0;
virtual void focusedWidgetChangedCallback() = 0;
private:
class Private;

View File

@@ -16,10 +16,11 @@
#include "FloatingWindow_p.h"
#include "Config.h"
#include "indicators/ClassicIndicators_p.h"
#include "indicators/NullIndicators_p.h"
#include "Utils_p.h"
#include "TabWidget_p.h"
#ifdef KDDOCKWIDGETS_QTWIDGETS
# include "indicators/ClassicIndicators_p.h"
# include "widgets/TabWidget_p.h"
# include "widgets/FrameWidget_p.h"
# include "widgets/TitleBarWidget_p.h"
# include "widgets/TabBarWidget_p.h"
@@ -32,10 +33,13 @@
# include <QRubberBand>
# include <QToolButton>
#else
# include "DockWidgetQuick.h"
# include "quick/FrameQuick_p.h"
# include "quick/DockWidgetQuick.h"
# include "quick/TitleBarQuick_p.h"
# include "quick/TabWidgetQuick_p.h"
# include "quick/TabBarQuick_p.h"
# include "quick/FloatingWindowQuick_p.h"
# include "quick/RubberBandQuick.h"
# include "multisplitter/Separator_quick.h"
#endif
@@ -90,11 +94,18 @@ FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWin
DropIndicatorOverlayInterface *DefaultWidgetFactory::createDropIndicatorOverlay(DropArea *dropArea) const
{
#ifdef Q_OS_WASM
// On WASM windows don't support translucency, which is required for the classic indicators.
return new SegmentedIndicators(dropArea);
#endif
switch (s_dropIndicatorType) {
case DropIndicatorType::Classic:
return new ClassicIndicators(dropArea);
case DropIndicatorType::Segmented:
return new SegmentedIndicators(dropArea);
case DropIndicatorType::None:
return new NullIndicators(dropArea);
}
return new ClassicIndicators(dropArea);
@@ -157,21 +168,27 @@ FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWin
DropIndicatorOverlayInterface *DefaultWidgetFactory::createDropIndicatorOverlay(DropArea *dropArea) const
{
switch (s_dropIndicatorType) {
case DropIndicatorType::Classic:
return new ClassicIndicators(dropArea);
case DropIndicatorType::Segmented:
qWarning() << "Segmented indicators not supported for QtQuick yet";
return new NullIndicators(dropArea);
case DropIndicatorType::None:
return new NullIndicators(dropArea);
}
return new ClassicIndicators(dropArea);
}
TabBar *DefaultWidgetFactory::createTabBar(TabWidget *parent) const
{
Q_UNUSED(parent)
Q_ASSERT(false);
return nullptr;
return new TabBarQuick(parent);
}
TabWidget *DefaultWidgetFactory::createTabWidget(Frame *parent) const
{
Q_UNUSED(parent)
Q_ASSERT(false);
return nullptr;
return new TabWidgetQuick(parent);
}
Layouting::Separator *DefaultWidgetFactory::createSeparator(Layouting::Widget *parent) const
@@ -181,7 +198,7 @@ Layouting::Separator *DefaultWidgetFactory::createSeparator(Layouting::Widget *p
QWidgetOrQuick *DefaultWidgetFactory::createRubberBand(QWidgetOrQuick *parent) const
{
return new QWidgetOrQuick(parent);
return new RubberBandQuick(parent);
}
SideBar *DefaultWidgetFactory::createSideBar(SideBarLocation loc, MainWindowBase *parent) const
@@ -193,6 +210,11 @@ SideBar *DefaultWidgetFactory::createSideBar(SideBarLocation loc, MainWindowBase
return nullptr;
}
QUrl DefaultWidgetFactory::titleBarFilename() const
{
return QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/TitleBar.qml"));
}
#endif // QtQuick
// iconForButtonType impl is the same for QtQuick and QtWidgets
@@ -233,10 +255,11 @@ QIcon DefaultWidgetFactory::iconForButtonType(TitleBarButtonType type, qreal dpr
const bool isFractional = int(dpr) != dpr;
if (isFractional) {
// We don't support 1.5x yet.
// Linux is the only one affected as Windows and macOS use integral factors.
// Problem with Linux is that rendering is off due to a rounding bug only fixed in 5.15.2
// Will enable for fractional later.
// QTBUG-86170
// Mostly affects Linux. Unless you're using Qt::HighDpiScaleFactorRoundingPolicy::PassThrough, in which case it will
// affect other OSes too.
return icon;
}
#else

View File

@@ -43,6 +43,7 @@ class Frame;
class DropArea;
class SideBar;
class TabBar;
class TabWidgetQuick;
/**
* @brief A factory class for allowing the user to customize some internal widgets.
@@ -61,7 +62,12 @@ class TabBar;
*
* @sa Config::setFrameworkWidgetFactory()
*/
class DOCKS_EXPORT FrameworkWidgetFactory {
class DOCKS_EXPORT FrameworkWidgetFactory : public QObject
{
#ifdef KDDOCKWIDGETS_QTQUICK
Q_PROPERTY(QUrl titleBarFilename READ titleBarFilename CONSTANT)
#endif
Q_OBJECT
public:
FrameworkWidgetFactory() = default;
@@ -89,16 +95,16 @@ public:
///@param floatingWindow Just forward to TitleBar's constructor.
virtual TitleBar* createTitleBar(FloatingWindow *floatingWindow) const = 0;
///@brief Called internally by the framework to create a TabBar
/// Override to provide your own TabBar sub-class.
///@param parent Just forward to TabBar's's constructor.
virtual TabBar* createTabBar(TabWidget *parent = nullptr) const = 0;
///@brief Called internally by the framework to create a TabWidget
/// Override to provide your own TabWidget sub-class.
///@param parent Just forward to TabWidget's constructor.
virtual TabWidget* createTabWidget(Frame *parent) const = 0;
///@brief Called internally by the framework to create a TabBar
/// Override to provide your own TabBar sub-class.
///@param parent Just forward to TabBar's's constructor.
virtual TabBar* createTabBar(TabWidget *parent = nullptr) const = 0;
///@brief Called internally by the framework to create a Separator
/// Override to provide your own Separator sub-class. The Separator allows
/// the user to resize nested dock widgets.
@@ -134,10 +140,10 @@ public:
#ifdef KDDOCKWIDGETS_QTWIDGETS
///@brief Called internally by the framework to create a title bar button
///@parent the button's parent
///@p parent the button's parent
virtual QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const = 0;
#else
// QtQuick will have some other base class for buttons
virtual QUrl titleBarFilename() const = 0;
#endif
/// @brief Returns the icon to be used with the specified @p type
@@ -157,8 +163,8 @@ public:
Frame *createFrame(QWidgetOrQuick *parent, FrameOptions) const override;
TitleBar *createTitleBar(Frame *) const override;
TitleBar *createTitleBar(FloatingWindow *) const override;
TabBar *createTabBar(TabWidget *parent) const override;
TabWidget *createTabWidget(Frame *parent) const override;
TabBar *createTabBar(TabWidget *parent) const override;
Layouting::Separator *createSeparator(Layouting::Widget *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
@@ -168,6 +174,8 @@ public:
#ifdef KDDOCKWIDGETS_QTWIDGETS
QAbstractButton* createTitleBarButton(QWidget *parent, TitleBarButtonType) const override;
#else
QUrl titleBarFilename() const override;
#endif
QIcon iconForButtonType(TitleBarButtonType type, qreal dpr) const override;

View File

@@ -19,15 +19,15 @@
#ifndef KD_KDDOCKWIDGETS_H
#define KD_KDDOCKWIDGETS_H
#include "Qt5Qt6Compat_p.h"
#include <QObject>
#ifdef Q_OS_WIN
// Only on Windows, where this is popular. On linux it the Qt::Tool windows need reparenting. Untested on macOS.
// Only on Windows, where this is popular. On linux the Qt::Tool windows need reparenting. Untested on macOS.
# define KDDOCKWIDGETS_SUPPORTS_NESTED_MAINWINDOWS
#endif
namespace KDDockWidgets
{
enum Location {
@@ -76,7 +76,8 @@ namespace KDDockWidgets
enum class DropIndicatorType {
Classic, ///< The default
Segmented
Segmented, ///< Segmented indicators
None ///< Don't show any drop indicators while dragging
};
///@internal
@@ -119,7 +120,7 @@ namespace KDDockWidgets
};
///@internal
inline uint qHash(SideBarLocation loc, uint seed)
inline Qt5Qt6Compat::qhashtype qHash(SideBarLocation loc, Qt5Qt6Compat::qhashtype seed)
{
return ::qHash(static_cast<uint>(loc), seed);
}

View File

@@ -25,14 +25,13 @@
#include "Logging_p.h"
#include "Frame_p.h"
#include "Position_p.h"
#include "multisplitter/Item_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "FloatingWindow_p.h"
#include <qmath.h>
#include <QDebug>
#include <QSettings>
#include <QApplication>
#include <QFile>
#include <memory>
@@ -700,6 +699,12 @@ QVariantMap LayoutSaver::MainWindow::toVariantMap() const
map.insert(QStringLiteral("isVisible"), isVisible);
map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc);
if (!dockWidgets.isEmpty())
map.insert(QStringLiteral("sidebar-%1").arg(int(loc)), stringListToVariant(dockWidgets));
}
return map;
}
@@ -720,6 +725,13 @@ void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
affinities.push_back(affinityName);
}
// Load the SideBars:
dockWidgetsPerSideBar.clear();
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
const QVariantList dockWidgets = map.value(QStringLiteral("sidebar-%1").arg(int(loc))).toList();
if (!dockWidgets.isEmpty())
dockWidgetsPerSideBar.insert(loc, variantToStringList(dockWidgets));
}
}
bool LayoutSaver::MultiSplitter::isValid() const

View File

@@ -27,6 +27,8 @@ QT_BEGIN_NAMESPACE
class QByteArray;
QT_END_NAMESPACE
class TestDocks;
namespace KDDockWidgets {
class DockWidgetBase;
@@ -105,7 +107,7 @@ public:
struct ScreenInfo;
private:
Q_DISABLE_COPY(LayoutSaver)
friend class TestDocks;
friend class ::TestDocks;
class Private;
Private *const d;

View File

@@ -18,7 +18,7 @@
#include <QRect>
#include <QDebug>
#include <QScreen>
#include <QApplication>
#include <QGuiApplication>
#include <QJsonDocument>
#include <memory>
@@ -236,6 +236,7 @@ public:
QVariantMap toVariantMap() const;
void fromVariantMap(const QVariantMap &map);
QHash<SideBarLocation, QStringList> dockWidgetsPerSideBar;
KDDockWidgets::MainWindowOptions options;
LayoutSaver::MultiSplitter multiSplitterLayout;
QString uniqueName;

View File

@@ -25,7 +25,6 @@
#include "DropAreaWithCentralFrame_p.h"
#include "FrameworkWidgetFactory.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QPainter>
@@ -99,8 +98,6 @@ MainWindow::MainWindow(const QString &name, MainWindowOptions options,
}
setCentralWidget(centralWidget);
// qApp->installEventFilter(this);
}
MainWindow::~MainWindow()

View File

@@ -24,7 +24,7 @@
#include "Utils_p.h"
#include "SideBar_p.h"
#include "Logging_p.h"
#include "Item_p.h"
#include "WidgetResizeHandler_p.h"
#include "FrameworkWidgetFactory.h"
#include "DropAreaWithCentralFrame_p.h"
@@ -45,9 +45,12 @@ public:
return m_options & MainWindowOption_HasCentralFrame;
}
WidgetResizeHandler::CursorPositions allowedResizeSides(SideBarLocation loc) const;
QRect rectForOverlay(Frame *, SideBarLocation) const;
SideBarLocation preferredSideBar(DockWidgetBase *) const;
void updateOverlayGeometry();
void updateOverlayGeometry(bool reusePreviousSize = false);
void clearSideBars();
QString name;
QStringList affinities;
@@ -157,6 +160,27 @@ void MainWindowBase::layoutParentContainerEqually(DockWidgetBase *dockWidget)
dropArea()->layoutParentContainerEqually(dockWidget);
}
WidgetResizeHandler::CursorPositions MainWindowBase::Private::allowedResizeSides(SideBarLocation loc) const
{
// When a sidebar is on top, you can only resize its bottom.
// and so forth...
switch (loc) {
case SideBarLocation::North:
return WidgetResizeHandler::CursorPosition_Bottom;
case SideBarLocation::East:
return WidgetResizeHandler::CursorPosition_Left;
case SideBarLocation::West:
return WidgetResizeHandler::CursorPosition_Right;
case SideBarLocation::South:
return WidgetResizeHandler::CursorPosition_Top;
case SideBarLocation::None:
return WidgetResizeHandler::CursorPosition_Undefined;
}
return WidgetResizeHandler::CursorPosition_Undefined;
}
QRect MainWindowBase::Private::rectForOverlay(Frame *frame, SideBarLocation location) const
{
SideBar *sb = q->sideBar(location);
@@ -323,7 +347,7 @@ SideBarLocation MainWindowBase::Private::preferredSideBar(DockWidgetBase *dw) co
: SideBarLocation::West;
}
void MainWindowBase::Private::updateOverlayGeometry()
void MainWindowBase::Private::updateOverlayGeometry(bool reusePreviousSize)
{
if (!m_overlayedDockWidget)
return;
@@ -334,7 +358,55 @@ void MainWindowBase::Private::updateOverlayGeometry()
return;
}
m_overlayedDockWidget->frame()->QWidgetAdapter::setGeometry(rectForOverlay(m_overlayedDockWidget->frame(), sb->location()));
const QRect defaultGeometry = rectForOverlay(m_overlayedDockWidget->frame(), sb->location());
QRect newGeometry = defaultGeometry;
Frame *frame = m_overlayedDockWidget->frame();
if (reusePreviousSize) {
// Let's try to honour the previous overlay size
switch (sb->location()) {
case SideBarLocation::North: {
const int maxHeight = q->height() - frame->pos().y() - 10; // gap
newGeometry.setHeight(qMin(frame->height(), maxHeight));
break;
}
case SideBarLocation::South: {
const int maxHeight = sb->pos().y() - m_dropArea->pos().y() - 10; // gap
const int bottom = newGeometry.bottom();
newGeometry.setHeight(qMin(frame->height(), maxHeight));
newGeometry.moveBottom(bottom);
break;
}
case SideBarLocation::East: {
const int maxWidth = sb->pos().x() - m_dropArea->pos().x() - 10; // gap
const int right = newGeometry.right();
newGeometry.setWidth(qMin(frame->width(), maxWidth));
newGeometry.moveRight(right);
break;
}
case SideBarLocation::West: {
const int maxWidth = q->width() - frame->pos().x() - 10; // gap
newGeometry.setWidth(qMin(frame->height(), maxWidth));
break;
}
case SideBarLocation::None:
qWarning() << Q_FUNC_INFO << "Unexpected sidebar value";
break;
}
}
m_overlayedDockWidget->frame()->QWidgetAdapter::setGeometry(newGeometry);
}
void MainWindowBase::Private::clearSideBars()
{
for (auto loc : { SideBarLocation::North, SideBarLocation::South,
SideBarLocation::East, SideBarLocation::West }) {
if (SideBar *sb = q->sideBar(loc))
sb->clear();
}
}
void MainWindowBase::moveToSideBar(DockWidgetBase *dw)
@@ -376,7 +448,7 @@ void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
return;
const SideBar *sb = sideBarForDockWidget(dw);
if (sb == nullptr) {
if (!sb) {
qWarning() << Q_FUNC_INFO << "You need to add the dock widget to the sidebar before you can overlay it";
return;
}
@@ -392,7 +464,12 @@ void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
auto frame = Config::self().frameworkWidgetFactory()->createFrame(this, FrameOption_IsOverlayed);
d->m_overlayedDockWidget = dw;
frame->addWidget(dw);
d->updateOverlayGeometry();
d->updateOverlayGeometry(/*reusePreviousSize=*/ false);
// Uncomment once I'm happy with the resizing
auto resizeHandler = new WidgetResizeHandler(true, frame);
resizeHandler->setAllowedResizeSides(d->allowedResizeSides(sb->location()));
frame->QWidgetAdapter::show();
Q_EMIT dw->isOverlayedChanged(true);
@@ -401,7 +478,7 @@ void MainWindowBase::overlayOnSideBar(DockWidgetBase *dw)
void MainWindowBase::toggleOverlayOnSideBar(DockWidgetBase *dw)
{
const bool wasOverlayed = d->m_overlayedDockWidget == dw;
clearSideBarOverlay();
clearSideBarOverlay(); // Because only 1 dock widget can be overlayed each time
if (!wasOverlayed) {
overlayOnSideBar(dw);
}
@@ -425,7 +502,7 @@ SideBar *MainWindowBase::sideBarForDockWidget(const DockWidgetBase *dw) const
SideBarLocation::East, SideBarLocation::West }) {
if (SideBar *sb = sideBar(loc)) {
if (sb->contains(const_cast<DockWidgetBase *>(dw)))
if (sb->containsDockWidget(const_cast<DockWidgetBase *>(dw)))
return sb;
}
}
@@ -440,8 +517,20 @@ DockWidgetBase *MainWindowBase::overlayedDockWidget() const
bool MainWindowBase::sideBarIsVisible(SideBarLocation loc) const
{
if (SideBar *sb = sideBar(loc))
return sb->isVisible();
if (SideBar *sb = sideBar(loc)) {
return !sb->isEmpty(); // isVisible() is always true, but its height is 0 when empty.
}
return false;
}
bool MainWindowBase::anySideBarIsVisible() const
{
for (auto loc : { SideBarLocation::North, SideBarLocation::South,
SideBarLocation::East, SideBarLocation::West }) {
if (sideBarIsVisible(loc))
return true;
}
return false;
}
@@ -463,7 +552,7 @@ void MainWindowBase::setUniqueName(const QString &uniqueName)
void MainWindowBase::onResized(QResizeEvent *)
{
if (d->m_overlayedDockWidget)
d->updateOverlayGeometry();
d->updateOverlayGeometry(/*reusePreviousSize=*/ true);
}
bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
@@ -481,7 +570,34 @@ bool MainWindowBase::deserialize(const LayoutSaver::MainWindow &mw)
d->affinities = mw.affinities;
}
return dropArea()->deserialize(mw.multiSplitterLayout);
const bool success = dropArea()->deserialize(mw.multiSplitterLayout);
// Restore the SideBars
d->clearSideBars();
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
SideBar *sb = sideBar(loc);
if (!sb)
continue;
const QStringList dockWidgets = mw.dockWidgetsPerSideBar.value(loc);
for (const QString &uniqueName : dockWidgets) {
DockWidgetBase *dw = DockRegistry::self()->dockByName(uniqueName);
if (!dw) {
qWarning() << Q_FUNC_INFO << "Could not find dock widget" << uniqueName
<< ". Won't restore it to sidebar";
continue;
}
sb->addDockWidget(dw);
}
}
// Commented-out for now, we dont' want to restore the popup/overlay. popups are perishable
//if (!mw.overlayedDockWidget.isEmpty())
// overlayOnSideBar(DockRegistry::self()->dockByName(mw.overlayedDockWidget));
return success;
}
LayoutSaver::MainWindow MainWindowBase::serialize() const
@@ -497,5 +613,13 @@ LayoutSaver::MainWindow MainWindowBase::serialize() const
m.multiSplitterLayout = dropArea()->serialize();
m.affinities = d->affinities;
for (SideBarLocation loc : { SideBarLocation::North, SideBarLocation::East, SideBarLocation::West, SideBarLocation::South }) {
if (SideBar *sb = sideBar(loc)) {
const QStringList dockwidgets = sb->serialize();
if (!dockwidgets.isEmpty())
m.dockWidgetsPerSideBar.insert(loc, dockwidgets);
}
}
return m;
}

View File

@@ -28,6 +28,8 @@
#include <QVector>
#include <QMargins>
class TestDocks;
namespace KDDockWidgets {
class DockWidgetBase;
@@ -166,6 +168,9 @@ public:
/// @brief Returns whether the specified sidebar is visible
bool sideBarIsVisible(SideBarLocation) const;
/// @brief Returns whether any side bar is visible
bool anySideBarIsVisible() const;
protected:
void setUniqueName(const QString &uniqueName);
void onResized(QResizeEvent *); // Because QtQuick doesn't have resizeEvent()
@@ -180,6 +185,7 @@ private:
class Private;
Private *const d;
friend class ::TestDocks;
friend class LayoutSaver;
bool deserialize(const LayoutSaver::MainWindow &);
LayoutSaver::MainWindow serialize() const;

View File

@@ -13,20 +13,42 @@
#define KDDOCKWIDGETS_QT5QT6_COMPAT_P_H
#include <QMouseEvent>
#include <QDropEvent>
namespace KDDockWidgets {
namespace Qt5Qt6Compat {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#define QQUICKITEMgeometryChanged geometryChange
using QEnterEvent = QEnterEvent;
using qintptr = ::qintptr;
using qhashtype = size_t;
inline QPoint eventPos(QDropEvent *ev)
{
return ev->position().toPoint();
}
inline QPoint eventGlobalPos(QMouseEvent *ev)
{
return ev->globalPosition().toPoint();
}
#else
#else // Qt 5:
#define QQUICKITEMgeometryChanged geometryChanged
using QEnterEvent = QEvent;
using qintptr = long;
using qhashtype = uint;
inline QPoint eventPos(QDropEvent *ev)
{
return ev->pos();
}
// Qt 5:
inline QPoint eventGlobalPos(QMouseEvent *ev)
{
return ev->globalPos();

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../DockWidgetBase.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../DockWidgetQuick.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../FocusScope.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../MainWindowBase.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../QWidgetAdapter.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../Qt5Qt6Compat_p.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../docks_export.h"

View File

@@ -0,0 +1,12 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "../../../private/DockRegistry_p.h"

View File

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

View File

@@ -4,10 +4,14 @@
<file>private/quick/qml/DropArea.qml</file>
<file>private/quick/qml/FloatingWindow.qml</file>
<file>private/quick/qml/Frame.qml</file>
<file>private/quick/qml/RubberBand.qml</file>
<file>private/quick/qml/TitleBarBase.qml</file>
<file>private/quick/qml/TitleBar.qml</file>
<file>private/quick/qml/TitleBarButton.qml</file>
<file>private/quick/qml/ClassicIndicatorsOverlay.qml</file>
<file>private/quick/qml/ClassicIndicator.qml</file>
</qresource>
<qresource prefix="/kddockwidgets/multisplitter/">
<file>private/multisplitter/qml/Separator.qml</file>
</qresource>
</RCC>

View File

@@ -18,10 +18,11 @@
#include "Config.h"
#include "SideBar_p.h"
#include "WindowBeingDragged_p.h"
#include "FloatingWindow_p.h"
#include <QPointer>
#include <QDebug>
#include <QApplication>
#include <QGuiApplication>
#include <QWindow>
#ifdef KDDOCKWIDGETS_QTWIDGETS
@@ -34,8 +35,11 @@ using namespace KDDockWidgets;
static void initKDDockWidgetResources()
{
#ifdef KDDOCKWIDGETS_STATICLIB
Q_INIT_RESOURCE(resources);
#if defined(KDDOCKWIDGETS_STATICLIB) || defined(QT_STATIC)
Q_INIT_RESOURCE(kddockwidgets_resources);
# if defined(KDDOCKWIDGETS_QTQUICK)
Q_INIT_RESOURCE(kddockwidgets_qtquick);
# endif
#endif
}
@@ -54,6 +58,7 @@ DockRegistry::DockRegistry(QObject *parent)
#else
KDDockWidgets::registerQmlTypes();
QQuickWindow::setDefaultAlphaBuffer(true);
#endif
connect(qApp, &QGuiApplication::focusObjectChanged,
@@ -74,28 +79,27 @@ void DockRegistry::maybeDelete()
void DockRegistry::onFocusObjectChanged(QObject *obj)
{
DockWidgetBase *const unfocusedDW = m_focusedDockWidget.data();
DockWidgetBase *newFocusedDockWidget = nullptr;
// In this function we reset the focused dock widget.
// Check if it's inside a dock widget:
auto p = qobject_cast<WidgetType*>(obj);
while (p) {
if (auto dw = qobject_cast<DockWidgetBase*>(p)) {
newFocusedDockWidget = dw;
break;
}
if (qobject_cast<DockWidgetBase*>(p) || qobject_cast<Frame*>(p))
return;
p = KDDockWidgets::Private::parentWidget(p);
}
// Nothing changed
if (m_focusedDockWidget.data() == newFocusedDockWidget)
setFocusedDockWidget(nullptr);
}
void DockRegistry::setFocusedDockWidget(DockWidgetBase *dw)
{
if (m_focusedDockWidget.data() == dw)
return;
m_focusedDockWidget = newFocusedDockWidget;
if (m_focusedDockWidget)
Q_EMIT m_focusedDockWidget->isFocusedChanged(false);
if (unfocusedDW)
Q_EMIT unfocusedDW->isFocusedChanged(false);
m_focusedDockWidget = dw;
if (m_focusedDockWidget)
Q_EMIT m_focusedDockWidget->isFocusedChanged(true);
@@ -328,6 +332,11 @@ void DockRegistry::unregisterFrame(Frame *frame)
m_frames.removeOne(frame);
}
bool DockRegistry::containsDockWidget(const QString &uniqueName) const
{
return dockByName(uniqueName) != nullptr;
}
DockWidgetBase *DockRegistry::dockByName(const QString &name) const
{
for (auto dock : qAsConst(m_dockWidgets)) {
@@ -504,6 +513,37 @@ FloatingWindow *DockRegistry::floatingWindowForHandle(QWindow *windowHandle) con
return nullptr;
}
FloatingWindow *DockRegistry::floatingWindowForHandle(WId hwnd) const
{
for (FloatingWindow *fw : m_floatingWindows) {
if (fw->windowHandle() && fw->windowHandle()->winId() == hwnd)
return fw;
}
return nullptr;
}
MainWindowBase *DockRegistry::mainWindowForHandle(QWindow *windowHandle) const
{
for (MainWindowBase *mw : m_mainWindows) {
if (mw->windowHandle() == windowHandle)
return mw;
}
return nullptr;
}
QWidgetOrQuick *DockRegistry::topLevelForHandle(QWindow *windowHandle) const
{
if (auto fw = floatingWindowForHandle(windowHandle))
return fw;
if (auto mw = mainWindowForHandle(windowHandle))
return mw;
return nullptr;
}
QVector<QWindow *> DockRegistry::topLevels(bool excludeFloatingDocks) const
{
QVector<QWindow *> windows;
@@ -555,7 +595,7 @@ void DockRegistry::clear(const DockWidgetBase::List &dockWidgets,
for (auto mw : qAsConst(mainWindows)) {
if (affinities.isEmpty() || affinitiesMatch(affinities, mw->affinities())) {
mw->multiSplitter()->rootItem()->clear();
mw->multiSplitter()->clearLayout();
}
}
}
@@ -587,12 +627,15 @@ bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
if (!(Config::self().flags() & Config::Flag_AutoHideSupport))
return false;
if (qobject_cast<Frame*>(watched)) {
// break recursion
return false;
}
auto p = watched;
while (p) {
if (auto dw = qobject_cast<DockWidgetBase*>(p)) {
onDockWidgetPressed(dw);
return false;
}
if (auto dw = qobject_cast<DockWidgetBase*>(p))
return onDockWidgetPressed(dw, static_cast<QMouseEvent*>(event));
p = p->parent();
}
@@ -601,17 +644,29 @@ bool DockRegistry::eventFilter(QObject *watched, QEvent *event)
return false;
}
void DockRegistry::onDockWidgetPressed(DockWidgetBase *dw)
bool DockRegistry::onDockWidgetPressed(DockWidgetBase *dw, QMouseEvent *ev)
{
// Here we implement "auto-hide". If there's a overlayed dock widget, we hide it if some other
// dock widget is clicked.
MainWindowBase *mainWindow = dw->mainWindow();
if (!mainWindow) // Only docked widgets are interesting
return;
return false;
DockWidgetBase *overlayedDockWidget = mainWindow->overlayedDockWidget();
if (overlayedDockWidget && dw != overlayedDockWidget) {
mainWindow->clearSideBarOverlay();
if (DockWidgetBase *overlayedDockWidget = mainWindow->overlayedDockWidget()) {
ev->ignore();
qApp->sendEvent(overlayedDockWidget->frame(), ev);
if (ev->isAccepted()) {
// The Frame accepted it. It means the user is resizing it. We allow for 4px outside for better resize.
return true; // don't propagate the event further
}
if (dw != overlayedDockWidget) {
// User clicked outside if the overlay, then we close the overlay.
mainWindow->clearSideBarOverlay();
return false;
}
}
return false;
}

View File

@@ -12,9 +12,8 @@
#ifndef KD_DOCKREGISTRY_P_H
#define KD_DOCKREGISTRY_P_H
#include "DockWidgetBase.h"
#include "MainWindowBase.h"
#include "FloatingWindow_p.h"
#include "../DockWidgetBase.h"
#include "../MainWindowBase.h"
#include <QVector>
#include <QObject>
@@ -28,7 +27,10 @@
namespace KDDockWidgets
{
class FloatingWindow;
class Frame;
class SideBar;
struct WindowBeingDragged;
class DOCKS_EXPORT DockRegistry : public QObject
{
@@ -53,6 +55,7 @@ public:
DockWidgetBase *focusedDockWidget() const;
bool containsDockWidget(const QString &uniqueName) const;
DockWidgetBase *dockByName(const QString &) const;
MainWindowBase *mainWindowByName(const QString &) const;
@@ -80,7 +83,7 @@ public:
const QVector<MultiSplitter*> layouts() const;
///@brief returns a list of all Frame instances
const Frame::List frames() const;
const QList<Frame*> frames() const;
///@brief returns all FloatingWindow instances. Not necessarily all floating dock widgets,
/// As there might be DockWidgets which weren't morphed yet.
@@ -95,6 +98,17 @@ public:
///@brief returns the FloatingWindow with handle @p windowHandle
FloatingWindow *floatingWindowForHandle(QWindow *windowHandle) const;
///@brief returns the FloatingWindow with handle @p hwnd
FloatingWindow *floatingWindowForHandle(WId hwnd) const;
///@brief returns the MainWindow with handle @p windowHandle
MainWindowBase *mainWindowForHandle(QWindow *windowHandle) const;
///@brief returns the top level widget associated with the specified QWindow.
///For QtWidgets, it returns a QWidget which is either a KDDockWidgets::MainWindow or a FloatingWindow.
///For QtQuick ir returns the same, but the type is a QWidgetAdapter (a QQuickItem), not QWidget obviously.
QWidgetOrQuick *topLevelForHandle(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
@@ -193,14 +207,17 @@ public:
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
friend class FocusScope;
explicit DockRegistry(QObject *parent = nullptr);
void onDockWidgetPressed(DockWidgetBase *dw);
bool onDockWidgetPressed(DockWidgetBase *dw, QMouseEvent *);
void onFocusObjectChanged(QObject *obj);
void maybeDelete();
void onFocusObjectChanged(QObject *);
void setFocusedDockWidget(DockWidgetBase *);
bool m_isProcessingAppQuitEvent = false;
DockWidgetBase::List m_dockWidgets;
MainWindowBase::List m_mainWindows;
Frame::List m_frames;
QList<Frame*> m_frames;
QVector<FloatingWindow*> m_floatingWindows;
QVector<MultiSplitter*> m_layouts;
QPointer<DockWidgetBase> m_focusedDockWidget;

View File

@@ -20,9 +20,11 @@
#include "Qt5Qt6Compat_p.h"
#include <QMouseEvent>
#include <QApplication>
#include <QGuiApplication>
#include <QCursor>
#include <QWindow>
#include <QDrag>
#include <QScopedValueRollback>
#if defined(Q_OS_WIN)
# include <QWindow>
@@ -32,7 +34,7 @@
using namespace KDDockWidgets;
namespace KDDockWidgets {
///@brief Custom mouse grabber, as platforms like wayland don't support grabbing the mouse
///@brief Custom mouse grabber, for platforms that don't support grabbing the mouse
class FallbackMouseGrabber : public QObject /// clazy:exclude=missing-qobject-macro
{
public:
@@ -51,6 +53,20 @@ public:
void releaseMouse()
{
#ifdef KDDOCKWIDGETS_QTQUICK
// Ungrab harder if QtQuick.
// QtQuick has the habit og grabbing the MouseArea internally, then doesn't ungrab it since
// we're consuming the events. So explicitly ungrab if any QQuickWindow::mouseGrabberItem()
// is still set.
QQuickView *view = m_target ? m_target->quickView()
: nullptr;
QQuickItem *grabber = view ? view->mouseGrabberItem()
: nullptr;
if (grabber)
grabber->ungrabMouse();
#endif
m_target = nullptr;
qApp->removeEventFilter(this);
}
@@ -60,14 +76,9 @@ public:
if (m_reentrancyGuard || !m_target)
return false;
if (ev->type() == QEvent::MouseButtonPress ||
ev->type() == QEvent::MouseButtonRelease ||
ev->type() == QEvent::MouseMove ||
ev->type() == QEvent::NonClientAreaMouseButtonPress ||
ev->type() == QEvent::NonClientAreaMouseButtonRelease ||
ev->type() == QEvent::NonClientAreaMouseMove) {
if (QMouseEvent *me = mouseEvent(ev)) {
m_reentrancyGuard = true;
qApp->sendEvent(m_target, ev);
qApp->sendEvent(m_target, me);
m_reentrancyGuard = false;
return true;
}
@@ -83,8 +94,51 @@ FallbackMouseGrabber::~FallbackMouseGrabber() {}
}
State::State(MinimalStateMachine *parent)
: QObject(parent)
, m_machine(parent)
{
}
State::~State() = default;
bool State::isCurrentState() const
{
return m_machine->currentState() == this;
}
MinimalStateMachine::MinimalStateMachine(QObject *parent)
: QObject(parent)
{
}
template<typename Obj, typename Signal>
void State::addTransition(Obj *obj, Signal signal, State *dest)
{
connect(obj, signal, this, [this, dest] {
if (isCurrentState()) {
m_machine->setCurrentState(dest);
}
});
}
State *MinimalStateMachine::currentState() const
{
return m_currentState;
}
void MinimalStateMachine::setCurrentState(State *state)
{
if (state != m_currentState) {
m_currentState = state;
if (state)
state->onEntry();
}
}
StateBase::StateBase(DragController *parent)
: QState(parent)
: State(parent)
, q(parent)
{
}
@@ -101,7 +155,7 @@ StateNone::StateNone(DragController *parent)
{
}
void StateNone::onEntry(QEvent *)
void StateNone::onEntry()
{
qCDebug(state) << "StateNone entered";
q->m_pressPos = QPoint();
@@ -142,7 +196,7 @@ StatePreDrag::StatePreDrag(DragController *parent)
StatePreDrag::~StatePreDrag() = default;
void StatePreDrag::onEntry(QEvent *)
void StatePreDrag::onEntry()
{
qCDebug(state) << "StatePreDrag entered";
WidgetResizeHandler::s_disableAllHandlers = true; // Disable the resize handler during dragging
@@ -163,6 +217,15 @@ bool StatePreDrag::handleMouseButtonRelease(QPoint)
return false;
}
bool StatePreDrag::handleMouseDoubleClick()
{
// This is only needed for QtQuick.
// With QtQuick, when double clicking, we get: Press, Release, Press, Double-click. and never
// receive the last Release event.
Q_EMIT q->dragCanceled();
return false;
}
StateDragging::StateDragging(DragController *parent)
: StateBase(parent)
{
@@ -170,7 +233,7 @@ StateDragging::StateDragging(DragController *parent)
StateDragging::~StateDragging() = default;
void StateDragging::onEntry(QEvent *)
void StateDragging::onEntry()
{
if (DockWidgetBase *dw = q->m_draggable->singleDockWidget()) {
// When we start to drag a floating window which has a single dock widget, we save the position
@@ -294,6 +357,14 @@ bool StateDragging::handleMouseMove(QPoint globalPos)
return true;
}
bool StateDragging::handleMouseDoubleClick()
{
// See comment in StatePreDrag::handleMouseDoubleClick().
// Very unlikely that we're in this state though, due to manhattan length
Q_EMIT q->dragCanceled();
return false;
}
StateDraggingWayland::StateDraggingWayland(DragController *parent)
: StateDragging(parent)
{
@@ -303,23 +374,92 @@ StateDraggingWayland::~StateDraggingWayland()
{
}
void StateDraggingWayland::onEntry(QEvent *)
void StateDraggingWayland::onEntry()
{
// Create a QDrag here
qCDebug(state) << "StateDragging entered";
if (m_inQDrag) {
// Maybe we can exit the state due to the nested event loop of QDrag::Exec();
qWarning() << Q_FUNC_INFO << "Impossible!";
return;
}
QScopedValueRollback<bool> guard(m_inQDrag, true);
q->m_windowBeingDragged = std::unique_ptr<WindowBeingDragged>(new WindowBeingDraggedWayland(q->m_draggable));
auto mimeData = new WaylandMimeData();
QDrag drag(this);
drag.setMimeData(mimeData);
drag.setPixmap(q->m_windowBeingDragged->pixmap());
qApp->installEventFilter(q);
const Qt::DropAction result = drag.exec();
qApp->removeEventFilter(q);
if (result == Qt::IgnoreAction)
Q_EMIT q->dragCanceled();
}
bool StateDraggingWayland::handleMouseButtonRelease(QPoint /*globalPos*/)
{
qCDebug(state) << Q_FUNC_INFO;
Q_EMIT q->dragCanceled();
return true;
}
bool StateDraggingWayland::handleMouseMove(QPoint /*globalPos*/)
bool StateDraggingWayland::handleDragEnter(QDragEnterEvent *ev, DropArea *dropArea)
{
auto mimeData = qobject_cast<const WaylandMimeData*>(ev->mimeData());
if (!mimeData || !q->m_windowBeingDragged)
return false; // Not for us, some other user drag.
if (q->m_windowBeingDragged->contains(dropArea)) {
ev->ignore();
return true;
}
dropArea->hover(q->m_windowBeingDragged.get(), dropArea->mapToGlobal(Qt5Qt6Compat::eventPos(ev)));
ev->accept();
return true;
}
DragController::DragController(QObject *)
bool StateDraggingWayland::handleDragLeave(DropArea *dropArea)
{
dropArea->removeHover();
return true;
}
bool StateDraggingWayland::handleDrop(QDropEvent *ev, DropArea *dropArea)
{
auto mimeData = qobject_cast<const WaylandMimeData*>(ev->mimeData());
if (!mimeData || !q->m_windowBeingDragged)
return false; // Not for us, some other user drag.
if (dropArea->drop(q->m_windowBeingDragged.get(), dropArea->mapToGlobal(Qt5Qt6Compat::eventPos(ev)))) {
ev->setDropAction(Qt::MoveAction);
ev->accept();
Q_EMIT q->dropped();
} else {
Q_EMIT q->dragCanceled();
}
dropArea->removeHover();
return true;
}
bool StateDraggingWayland::handleDragMove(QDragMoveEvent *ev, DropArea *dropArea)
{
auto mimeData = qobject_cast<const WaylandMimeData*>(ev->mimeData());
if (!mimeData || !q->m_windowBeingDragged)
return false; // Not for us, some other user drag.
dropArea->hover(q->m_windowBeingDragged.get(), dropArea->mapToGlobal(Qt5Qt6Compat::eventPos(ev)));
return true;
}
DragController::DragController(QObject *parent)
: MinimalStateMachine(parent)
{
qCDebug(creation) << "DragController()";
@@ -334,8 +474,10 @@ DragController::DragController(QObject *)
stateDragging->addTransition(this, &DragController::dragCanceled, stateNone);
stateDragging->addTransition(this, &DragController::dropped, stateNone);
setInitialState(stateNone);
start();
if (usesFallbackMouseGrabber())
enableFallbackMouseGrabber();
setCurrentState(stateNone);
}
DragController *DragController::instance()
@@ -373,6 +515,9 @@ bool DragController::isInClientDrag() const
void DragController::grabMouseFor(QWidgetOrQuick *target)
{
if (isWayland())
return; // No grabbing supported on wayland
if (m_fallbackMouseGrabber) {
m_fallbackMouseGrabber->grabMouse(target);
} else {
@@ -382,6 +527,9 @@ void DragController::grabMouseFor(QWidgetOrQuick *target)
void DragController::releaseMouse(QWidgetOrQuick *target)
{
if (isWayland())
return; // No grabbing supported on wayland
if (m_fallbackMouseGrabber) {
m_fallbackMouseGrabber->releaseMouse();
} else {
@@ -406,39 +554,46 @@ WindowBeingDragged *DragController::windowBeingDragged() const
return m_windowBeingDragged.get();
}
static QMouseEvent *mouseEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
case QEvent::NonClientAreaMouseButtonPress:
case QEvent::NonClientAreaMouseButtonRelease:
case QEvent::NonClientAreaMouseMove:
return static_cast<QMouseEvent *>(e);
default:
break;
}
return nullptr;
}
bool DragController::eventFilter(QObject *o, QEvent *e)
{
if (m_nonClientDrag && e->type() == QEvent::Move) {
// On Windows, non-client mouse moves are only sent at the end, so we must fake it:
qCDebug(mouseevents) << "DragController::eventFilter e=" << e->type() << "; o=" << o;
activeState()->handleMouseMove(QCursor::pos());
return QStateMachine::eventFilter(o, e);
return false;
}
if (isWayland()) {
// Wayland is very different. It uses QDrag for the dragging of a window.
if (auto dropArea = qobject_cast<DropArea*>(o)) {
switch (int(e->type())) {
case QEvent::DragEnter:
if (activeState()->handleDragEnter(static_cast<QDragEnterEvent*>(e), dropArea))
return true;
break;
case QEvent::DragLeave:
if (activeState()->handleDragLeave(dropArea))
return true;
break;
case QEvent::DragMove:
if (activeState()->handleDragMove(static_cast<QDragMoveEvent*>(e), dropArea))
return true;
break;
case QEvent::Drop:
if (activeState()->handleDrop(static_cast<QDropEvent*>(e), dropArea))
return true;
break;
}
}
}
QMouseEvent *me = mouseEvent(e);
if (!me)
return QStateMachine::eventFilter(o, e);
return false;
auto w = qobject_cast<QWidgetOrQuick*>(o);
if (!w)
return QStateMachine::eventFilter(o, e);
return false;
qCDebug(mouseevents) << "DragController::eventFilter e=" << e->type() << "; o=" << o
<< "; m_nonClientDrag=" << m_nonClientDrag;
@@ -451,13 +606,15 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
return activeState()->handleMouseButtonPress(draggableForQObject(o), Qt5Qt6Compat::eventGlobalPos(me), me->pos());
}
}
return QStateMachine::eventFilter(o, e);
return false;
}
case QEvent::MouseButtonPress:
// For top-level windows that support native dragging all goes through the NonClient* events.
// This also forbids dragging a FloatingWindow simply by pressing outside of the title area, in the background
if (!KDDockWidgets::usesNativeDraggingAndResizing() || !w->isWindow())
if (!KDDockWidgets::usesNativeDraggingAndResizing() || !w->isWindow()) {
Q_ASSERT(activeState());
return activeState()->handleMouseButtonPress(draggableForQObject(o), Qt5Qt6Compat::eventGlobalPos(me), me->pos());
}
else break;
case QEvent::MouseButtonRelease:
case QEvent::NonClientAreaMouseButtonRelease:
@@ -465,28 +622,28 @@ bool DragController::eventFilter(QObject *o, QEvent *e)
case QEvent::NonClientAreaMouseMove:
case QEvent::MouseMove:
return activeState()->handleMouseMove(Qt5Qt6Compat::eventGlobalPos(me));
case QEvent::MouseButtonDblClick:
return activeState()->handleMouseDoubleClick();
default:
break;
}
return QStateMachine::eventFilter(o, e);
return false;
}
StateBase *DragController::activeState() const
{
auto set = configuration();
if (set.isEmpty())
return nullptr;
return static_cast<StateBase *>(*(set.begin()));
return static_cast<StateBase *>(currentState());
}
#if defined(Q_OS_WIN)
static QWidget *qtTopLevelForHWND(HWND hwnd)
static QWidgetOrQuick *qtTopLevelForHWND(HWND hwnd)
{
auto topLevels = qApp->topLevelWidgets();
for (auto topLevel : topLevels) {
if (hwnd == (HWND)topLevel->winId())
return topLevel;
const QList<QWindow*> windows = qApp->topLevelWindows();
for (QWindow *window : windows) {
if (hwnd == (HWND)window->winId()) {
return DockRegistry::self()->topLevelForHandle(window);
}
}
qCDebug(toplevels) << Q_FUNC_INFO << "Couldn't find hwnd for top-level" << hwnd;
@@ -496,7 +653,7 @@ static QWidget *qtTopLevelForHWND(HWND hwnd)
template <typename T>
static WidgetType* qtTopLevelUnderCursor_impl(QPoint globalPos, const QVector<QWindow*> &windows, T windowBeingDragged)
{
for (int i = windows.size() -1; i >= 0; --i) {
for (auto i = windows.size() -1; i >= 0; --i) {
QWindow *window = windows.at(i);
auto tl = KDDockWidgets::Private::widgetForWindow(window);
@@ -520,8 +677,7 @@ WidgetType *DragController::qtTopLevelUnderCursor() const
QPoint globalPos = QCursor::pos();
if (qApp->platformName() == QLatin1String("windows")) { // So -platform offscreen on Windows doesn't use this
# if defined(Q_OS_WIN)
auto topLevels = qApp->topLevelWidgets();
#if defined(Q_OS_WIN)
POINT globalNativePos;
if (!GetCursorPos(&globalNativePos))
return nullptr;
@@ -544,7 +700,8 @@ WidgetType *DragController::qtTopLevelUnderCursor() const
return tl;
}
} else {
// Maybe it's embedded in a QWinWidget:
# ifdef KDDOCKWIDGETS_QTWIDGETS // Maybe it's embedded in a QWinWidget:
auto topLevels = qApp->topLevelWidgets();
for (auto topLevel : topLevels) {
if (QLatin1String(topLevel->metaObject()->className()) == QLatin1String("QWinWidget")) {
if (hwnd == GetParent(HWND(topLevel->windowHandle()->winId()))) {
@@ -555,13 +712,13 @@ WidgetType *DragController::qtTopLevelUnderCursor() const
}
}
}
# endif // QtWidgets
// A window belonging to another app is below the cursor
qCDebug(toplevels) << Q_FUNC_INFO << "Window from another app is under cursor" << hwnd;
return nullptr;
}
}
# endif
#endif // Q_OS_WIN
} else {
// !Windows: Linux, macOS, offscreen (offscreen on Windows too), etc.

View File

@@ -17,8 +17,9 @@
#include "TitleBar_p.h"
#include "WindowBeingDragged_p.h"
#include <QStateMachine>
#include <QPoint>
#include <QMimeData>
#include <memory>
namespace KDDockWidgets {
@@ -27,8 +28,37 @@ class StateBase;
class DropArea;
class Draggable;
class FallbackMouseGrabber;
class MinimalStateMachine;
class DOCKS_EXPORT DragController : public QStateMachine
class State : public QObject
{
public:
explicit State(MinimalStateMachine *parent);
~State() override;
template <typename Obj, typename Signal>
void addTransition(Obj *, Signal, State *dest);
bool isCurrentState() const;
virtual void onEntry() = 0;
private:
MinimalStateMachine *const m_machine;
};
class MinimalStateMachine : public QObject
{
Q_OBJECT
public:
explicit MinimalStateMachine(QObject *parent = nullptr);
State *currentState() const;
void setCurrentState(State *);
private:
State *m_currentState = nullptr;
};
class DOCKS_EXPORT DragController : public MinimalStateMachine
{
Q_OBJECT
public:
@@ -75,6 +105,7 @@ private:
friend class StatePreDrag;
friend class StateDragging;
friend class StateDropped;
friend class StateDraggingWayland;
DragController(QObject * = nullptr);
StateBase *activeState() const;
@@ -92,7 +123,7 @@ private:
FallbackMouseGrabber *m_fallbackMouseGrabber = nullptr;
};
class StateBase : public QState
class StateBase : public State
{
Q_OBJECT
public:
@@ -103,6 +134,13 @@ public:
virtual bool handleMouseButtonPress(Draggable * /*receiver*/, QPoint /*globalPos*/, QPoint /*pos*/) { return false; }
virtual bool handleMouseMove(QPoint /*globalPos*/) { return false; }
virtual bool handleMouseButtonRelease(QPoint /*globalPos*/) { return false; }
virtual bool handleMouseDoubleClick() { return false; }
// Only interesting for Wayland
virtual bool handleDragEnter(QDragEnterEvent *, DropArea *) { return false; }
virtual bool handleDragLeave(DropArea *) { return false; }
virtual bool handleDragMove(QDragMoveEvent *, DropArea *) { return false; }
virtual bool handleDrop(QDropEvent *, DropArea *) { return false; }
// Returns whether this is the current state
bool isActiveState() const;
@@ -116,7 +154,7 @@ class StateNone : public StateBase
public:
explicit StateNone(DragController *parent);
~StateNone() override;
void onEntry(QEvent *) override;
void onEntry() override;
bool handleMouseButtonPress(Draggable *draggable, QPoint globalPos, QPoint pos) override;
};
@@ -126,9 +164,10 @@ class StatePreDrag : public StateBase
public:
explicit StatePreDrag(DragController *parent);
~StatePreDrag() override;
void onEntry(QEvent *) override;
void onEntry() override;
bool handleMouseMove(QPoint globalPos) override;
bool handleMouseButtonRelease(QPoint) override;
bool handleMouseDoubleClick() override;
};
// Used on all platforms except Wayland. @see StateDraggingWayland
@@ -138,9 +177,10 @@ class StateDragging : public StateBase
public:
explicit StateDragging(DragController *parent);
~StateDragging() override;
void onEntry(QEvent *) override;
void onEntry() override;
bool handleMouseButtonRelease(QPoint globalPos) override;
bool handleMouseMove(QPoint globalPos) override;
bool handleMouseDoubleClick() override;
};
// Used on wayland only to use QDrag instead of setting geometry on mouse-move.
@@ -150,9 +190,21 @@ class StateDraggingWayland : public StateDragging
public:
explicit StateDraggingWayland(DragController *parent);
~StateDraggingWayland() override;
void onEntry(QEvent *) override;
void onEntry() override;
bool handleMouseButtonRelease(QPoint globalPos) override;
bool handleMouseMove(QPoint globalPos) override;
bool handleDragEnter(QDragEnterEvent *, DropArea *) override;
bool handleDragMove(QDragMoveEvent *, DropArea *) override;
bool handleDragLeave(DropArea *) override;
bool handleDrop(QDropEvent *, DropArea *) override;
bool m_inQDrag = false;
};
// A sub-class just so we don't use QMimeData directly. We'll only accept drops if its mime data
// Can be qobject_casted to this class. For safety.
class WaylandMimeData : public QMimeData
{
Q_OBJECT
public:
};
}

View File

@@ -13,8 +13,8 @@
#include "DragController_p.h"
#include "FloatingWindow_p.h"
#include "WidgetResizeHandler_p.h"
#include "Utils_p.h"
#include <QApplication>
using namespace KDDockWidgets;
@@ -54,12 +54,7 @@ QWidgetOrQuick *Draggable::asWidget() const
bool Draggable::dragCanStart(QPoint pressPos, QPoint globalPos) const
{
return (globalPos - pressPos).manhattanLength() > QApplication::startDragDistance();
}
WidgetResizeHandler *Draggable::widgetResizeHandler() const
{
return d->widgetResizeHandler;
return (globalPos - pressPos).manhattanLength() > KDDockWidgets::startDragDistance();
}
void Draggable::setWidgetResizeHandler(WidgetResizeHandler *w)

View File

@@ -12,7 +12,7 @@
#ifndef KD_DRAGGABLE_P_H
#define KD_DRAGGABLE_P_H
#include "QWidgetAdapter.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include <QVector>
@@ -62,10 +62,11 @@ public:
*/
virtual bool dragCanStart(QPoint pressPos, QPoint globalPos) const;
WidgetResizeHandler *widgetResizeHandler() const;
/**
* @brief Sets a widget resize handler
*/
void setWidgetResizeHandler(WidgetResizeHandler *w);
/**
* @brief If this draggable contains a single dock widget, then it's returned.
* nullptr otherwise.

View File

@@ -18,9 +18,9 @@
#include "DropIndicatorOverlayInterface_p.h"
#include "FrameworkWidgetFactory.h"
#include "MainWindowBase.h"
#include "multisplitter/Item_p.h"
#include "DockRegistry_p.h"
#include "Frame_p.h"
#include "Utils_p.h"
// #include "indicators/AnimatedIndicators_p.h"
#include "WindowBeingDragged_p.h"
@@ -38,6 +38,13 @@ DropArea::DropArea(QWidgetOrQuick *parent)
, m_dropIndicatorOverlay(Config::self().frameworkWidgetFactory()->createDropIndicatorOverlay(this))
{
qCDebug(creation) << "DropArea";
if (isWayland()) {
#ifdef KDDOCKWIDGETS_QTWIDGETS
setAcceptDrops(true);
#else
qWarning() << "Dropping not implement for QtQuick on Wayland yet!";
#endif
}
}
DropArea::~DropArea()
@@ -113,10 +120,10 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
const bool hadSingleFloatingFrame = hasSingleFloatingFrame();
// Check if the dock widget already exists in the layout
if (contains(dw)) {
if (containsDockWidget(dw)) {
Frame *oldFrame = dw->frame();
if (oldFrame->hasSingleDockWidget()) {
Q_ASSERT(oldFrame->contains(dw));
Q_ASSERT(oldFrame->containsDockWidget(dw));
// The frame only has this dock widget, and the frame is already in the layout. So move the frame instead
frame = oldFrame;
} else {
@@ -141,9 +148,9 @@ void DropArea::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBa
}
}
bool DropArea::contains(DockWidgetBase *dw) const
bool DropArea::containsDockWidget(DockWidgetBase *dw) const
{
return dw->frame() && MultiSplitter::contains(dw->frame());
return dw->frame() && MultiSplitter::containsFrame(dw->frame());
}
bool DropArea::hasSingleFloatingFrame() const
@@ -174,20 +181,20 @@ void DropArea::layoutParentContainerEqually(DockWidgetBase *dw)
layoutEqually(item->parentContainer());
}
void DropArea::hover(WindowBeingDragged *floatingWindow, QPoint globalPos)
DropIndicatorOverlayInterface::DropLocation DropArea::hover(WindowBeingDragged *draggedWindow, QPoint globalPos)
{
if (!validateAffinity(floatingWindow))
return;
if (!validateAffinity(draggedWindow))
return DropIndicatorOverlayInterface::DropLocation_None;
if (!m_dropIndicatorOverlay) {
qWarning() << Q_FUNC_INFO << "The frontend is missing a drop indicator overlay";
return;
return DropIndicatorOverlayInterface::DropLocation_None;
}
Frame *frame = frameContainingPos(globalPos); // Frame is nullptr if MainWindowOption_HasCentralFrame isn't set
m_dropIndicatorOverlay->setWindowBeingDragged(floatingWindow != nullptr);
m_dropIndicatorOverlay->setWindowBeingDragged(true);
m_dropIndicatorOverlay->setHoveredFrame(frame);
m_dropIndicatorOverlay->hover(globalPos);
return m_dropIndicatorOverlay->hover(globalPos);
}
static bool isOutterLocation(DropIndicatorOverlayInterface::DropLocation location)
@@ -205,8 +212,7 @@ static bool isOutterLocation(DropIndicatorOverlayInterface::DropLocation locatio
bool DropArea::drop(WindowBeingDragged *droppedWindow, QPoint globalPos)
{
FloatingWindow *floatingWindow = droppedWindow ? droppedWindow->floatingWindow()
: nullptr;
FloatingWindow *floatingWindow = droppedWindow->floatingWindow();
if (floatingWindow == window()) {
qWarning() << "Refusing to drop onto itself"; // Doesn't happen
@@ -223,17 +229,35 @@ bool DropArea::drop(WindowBeingDragged *droppedWindow, QPoint globalPos)
hover(droppedWindow, globalPos);
auto droploc = m_dropIndicatorOverlay->currentDropLocation();
Frame *acceptingFrame = m_dropIndicatorOverlay->hoveredFrame();
if (!(acceptingFrame || isOutterLocation(m_dropIndicatorOverlay->currentDropLocation()))) {
qWarning() << "DropArea::drop: asserted with frame=" << acceptingFrame << "; Location=" << m_dropIndicatorOverlay->currentDropLocation();
if (!(acceptingFrame || isOutterLocation(droploc))) {
qWarning() << "DropArea::drop: asserted with frame=" << acceptingFrame
<< "; Location=" << droploc;
return false;
}
return drop(floatingWindow, acceptingFrame, droploc);
return drop(droppedWindow, acceptingFrame, droploc);
}
bool DropArea::drop(FloatingWindow *droppedWindow, Frame *acceptingFrame,
bool DropArea::drop(WindowBeingDragged *draggedWindow, Frame *acceptingFrame,
DropIndicatorOverlayInterface::DropLocation droploc)
{
FloatingWindow *droppedWindow = draggedWindow ? draggedWindow->floatingWindow()
: nullptr;
if (isWayland() && !droppedWindow) {
// This is the Wayland special case.
// With other platforms, when detaching a tab or dock widget we create the FloatingWindow immediately.
// With Wayland we delay the floating window until we drop it.
// Ofc, we could just dock the dockwidget without the temporary FloatingWindow, but this way we reuse
// 99% of the rest of the code, without adding more wayland special cases
droppedWindow = draggedWindow->draggable()->makeWindow()->floatingWindow();
if (!droppedWindow) {
// Doesn't happen
qWarning() << Q_FUNC_INFO << "Wayland: Expected window" << draggedWindow;
return false;
}
}
bool result = true;
const bool needToFocusNewlyDroppedWidgets = Config::self().flags() & Config::Flag_TitleBarIsFocusable;
const DockWidgetBase::List droppedDockWidgets = needToFocusNewlyDroppedWidgets ? droppedWindow->multiSplitter()->dockWidgets()
@@ -271,7 +295,7 @@ bool DropArea::drop(FloatingWindow *droppedWindow, Frame *acceptingFrame,
if (needToFocusNewlyDroppedWidgets) {
// Let's also focus the newly dropped dock widget
if (droppedDockWidgets.size() > 0) {
if (!droppedDockWidgets.isEmpty()) {
// If more than 1 was dropped, we only focus the first one
Frame *frame = droppedDockWidgets.first()->frame();
frame->FocusScope::focus(Qt::MouseFocusReason);

View File

@@ -19,13 +19,13 @@
#ifndef KD_DROP_AREA_P_H
#define KD_DROP_AREA_P_H
#include "docks_export.h"
#include "kddockwidgets/docks_export.h"
#include "Frame_p.h"
#include "KDDockWidgets.h"
#include "MultiSplitter_p.h"
#include "DropIndicatorOverlayInterface_p.h"
class TestCommon;
class TestDocks;
namespace KDDockWidgets {
@@ -44,7 +44,7 @@ public:
~DropArea();
void removeHover();
void hover(WindowBeingDragged *floatingWindow, QPoint globalPos);
DropIndicatorOverlayInterface::DropLocation hover(WindowBeingDragged *draggedWindow, QPoint globalPos);
///@brief Called when a user drops a widget via DND
bool drop(WindowBeingDragged *droppedWindow, QPoint globalPos);
int numFrames() const;
@@ -54,7 +54,7 @@ public:
DropIndicatorOverlayInterface *dropIndicatorOverlay() const { return m_dropIndicatorOverlay; }
void addDockWidget(DockWidgetBase *, KDDockWidgets::Location location, DockWidgetBase *relativeTo, AddingOption option = {});
bool contains(DockWidgetBase *) const;
bool containsDockWidget(DockWidgetBase *) const;
/// Returns whether this layout has a single dock widget which is floating
/// Implies it's in a FloatingWindow and that it has only one dock widget
@@ -65,15 +65,14 @@ public:
private:
Q_DISABLE_COPY(DropArea)
friend class Frame;
friend class TestDocks;
friend class ::TestCommon;
friend class ::TestDocks;
friend class DropIndicatorOverlayInterface;
friend class AnimatedIndicators;
friend class FloatingWindow;
template <typename T>
bool validateAffinity(T *, Frame *acceptingFrame = nullptr) const;
bool drop(FloatingWindow *droppedWindow, Frame *acceptingFrame, DropIndicatorOverlayInterface::DropLocation);
bool drop(WindowBeingDragged *draggedWindow, Frame *acceptingFrame, DropIndicatorOverlayInterface::DropLocation);
bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo);
Frame *frameContainingPos(QPoint globalPos) const;
void updateFloatingActions();

View File

@@ -118,9 +118,9 @@ void DropIndicatorOverlayInterface::setCurrentDropLocation(DropIndicatorOverlayI
}
}
void DropIndicatorOverlayInterface::hover(QPoint globalPos)
DropIndicatorOverlayInterface::DropLocation DropIndicatorOverlayInterface::hover(QPoint globalPos)
{
hover_impl(globalPos);
return hover_impl(globalPos);
}
void DropIndicatorOverlayInterface::setHoveredFrameRect(QRect rect)

View File

@@ -53,9 +53,11 @@ public:
Frame *hoveredFrame() const { return m_hoveredFrame; }
void setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation location);
void hover(QPoint globalPos);
KDDockWidgets::DropIndicatorOverlayInterface::DropLocation hover(QPoint globalPos);
virtual QPoint posForIndicator(DropLocation) const { return {}; }; // Used by unit-tests only
/// @brief returns the position of the specified drop location
/// The return is in global coordinates
virtual QPoint posForIndicator(DropLocation) const = 0;
static KDDockWidgets::Location multisplitterLocationFor(DropLocation);
@@ -71,7 +73,7 @@ private:
DropLocation m_currentDropLocation = DropLocation_None;
protected:
virtual void hover_impl(QPoint globalPos) = 0;
virtual DropIndicatorOverlayInterface::DropLocation hover_impl(QPoint globalPos) = 0;
virtual void onHoveredFrameChanged(Frame *);
virtual void updateVisibility() {};
@@ -79,6 +81,7 @@ protected:
DropArea *const m_dropArea;
bool m_draggedWindowIsHovering = false;
};
}
#endif

View File

@@ -13,7 +13,6 @@
#include "MainWindowBase.h"
#include "Logging_p.h"
#include "Frame_p.h"
#include "DropArea_p.h"
#include "TitleBar_p.h"
#include "WindowBeingDragged_p.h"
#include "Utils_p.h"
@@ -34,10 +33,10 @@
using namespace KDDockWidgets;
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
#if defined(Q_OS_WIN)
# ifdef KDDOCKWIDGETS_QTWIDGETS
namespace KDDockWidgets {
/**
* @brief Helper to rediriect WM_NCHITTEST from child widgets to the top-level widget
*
@@ -45,12 +44,14 @@ namespace KDDockWidgets {
* in FloatingWindow::nativeEvent(). But if the child widgets have a native handle, then
* the WM_NCHITTEST will go to them. They have to respond HTTRANSPARENT so the event
* is redirected.
*
* This only affects QtWidgets, since QQuickItems never have native WId.
*/
class NCHITTESTEventFilter : public QAbstractNativeEventFilter
{
public:
explicit NCHITTESTEventFilter(FloatingWindow *fw) : m_floatingWindow(fw) {}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
bool nativeEventFilter(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result) override
{
if (eventType != "windows_generic_MSG" || !m_floatingWindow)
return false;
@@ -58,11 +59,14 @@ public:
auto msg = static_cast<MSG *>(message);
if (msg->message != WM_NCHITTEST)
return false;
QWidget *child = QWidget::find(WId(msg->hwnd));
const WId wid = WId(msg->hwnd);
QWidget *child = QWidget::find(wid);
if (!child || child->window() != m_floatingWindow)
return false;
const bool isThisWindow = child == m_floatingWindow;
if (child != m_floatingWindow) {
if (!isThisWindow) {
*result = HTTRANSPARENT;
return true;
}
@@ -73,7 +77,9 @@ public:
QPointer<FloatingWindow> m_floatingWindow;
};
}
#endif
# endif
#endif // Q_OS_WIN
static Qt::WindowFlags windowFlagsToUse()
{
@@ -133,32 +139,21 @@ FloatingWindow::FloatingWindow(MainWindowBase *parent)
, m_dropArea(new DropArea(this))
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
{
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
// On Windows with Qt 5.9 (and maybe earlier), the WM_NCALCSIZE isn't being processed unless we explicitly create the window.
// So create it now, otherwise floating dock widgets will show a native title bar until resized.
create();
if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
m_nchittestFilter = new NCHITTESTEventFilter(this);
qApp->installNativeEventFilter(m_nchittestFilter);
connect(windowHandle(), &QWindow::screenChanged, this, [this] {
// Qt honors our frame hijacking usually... but when screen changes we must give it a nudge.
// Otherwise what Qt thinks is the client area is not what Windows knows it is.
// SetWindowPos() will trigger an NCCALCSIZE message, which Qt will intercept and take note of the margins we're using.
SetWindowPos(HWND(winId()), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
});
// Show drop-shadow:
MARGINS margins = {0, 0, 0, 1}; // arbitrary, just needs to be > 0 it seems
DwmExtendFrameIntoClientArea(HWND(winId()), &margins);
if (kddwUsesQtWidgets()) {
// For QtQuick we do it a bit later, once we have the QQuickWindow
setupWindow();
}
#endif
DockRegistry::self()->registerFloatingWindow(this);
qCDebug(creation) << "FloatingWindow()" << this;
maybeCreateResizeHandler();
if (Config::self().flags() & Config::Flag_KeepAboveIfNotUtilityWindow)
setWindowFlag(Qt::WindowStaysOnTopHint, true);
if (kddwUsesQtWidgets()) {
// QtQuick will do it a bit later, once it has a QWindow
maybeCreateResizeHandler();
}
updateTitleBarVisibility();
connect(m_dropArea, &MultiSplitter::visibleWidgetCountChanged, this, &FloatingWindow::onFrameCountChanged);
@@ -188,8 +183,37 @@ FloatingWindow::~FloatingWindow()
qCDebug(creation) << "~FloatingWindow";
}
void FloatingWindow::setupWindow()
{
// Does some minor setup on our QWindow.
// Like adding the drop shadow on Windows and two other workarounds.
#if defined(Q_OS_WIN)
// On Windows with Qt 5.9 (and maybe earlier), the WM_NCALCSIZE isn't being processed unless we explicitly create the window.
// So create it now, otherwise floating dock widgets will show a native title bar until resized.
create();
if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
# ifdef KDDOCKWIDGETS_QTWIDGETS
m_nchittestFilter = new NCHITTESTEventFilter(this);
qApp->installNativeEventFilter(m_nchittestFilter);
#endif
connect(windowHandle(), &QWindow::screenChanged, this, [this] {
// Qt honors our frame hijacking usually... but when screen changes we must give it a nudge.
// Otherwise what Qt thinks is the client area is not what Windows knows it is.
// SetWindowPos() will trigger an NCCALCSIZE message, which Qt will intercept and take note of the margins we're using.
SetWindowPos(HWND(winId()), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
});
// Show drop-shadow:
MARGINS margins = {0, 0, 0, 1}; // arbitrary, just needs to be > 0 it seems
DwmExtendFrameIntoClientArea(HWND(winId()), &margins);
}
#endif // Q_OS_WIN
}
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result)
{
if (!m_inDtor && !m_deleteScheduled && KDDockWidgets::usesAeroSnapWithCustomDecos()) {
// To enable aero snap we need to tell Windows where's our custom title bar
@@ -205,9 +229,7 @@ void FloatingWindow::maybeCreateResizeHandler()
{
if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
setFlag(Qt::FramelessWindowHint, true);
#ifdef KDDOCKWIDGETS_QTWIDGETS
setWidgetResizeHandler(new WidgetResizeHandler(this));
#endif
setWidgetResizeHandler(new WidgetResizeHandler(false, this));
}
}
@@ -228,6 +250,11 @@ DockWidgetBase *FloatingWindow::singleDockWidget() const
return nullptr;
}
const DockWidgetBase::List FloatingWindow::dockWidgets() const
{
return m_dropArea->dockWidgets();
}
const Frame::List FloatingWindow::frames() const
{
Q_ASSERT(m_dropArea);
@@ -270,6 +297,14 @@ MultiSplitter *FloatingWindow::multiSplitter() const
bool FloatingWindow::isInDragArea(QPoint globalPoint) const
{
#ifdef Q_OS_WIN
// A click near the border will still send a Qt::NonClientMousePressEvent. We shouldn't
// interpret that as a drag, as it's for a native resize.
// Keep track of how we handled the WM_NCHITTEST
if (m_lastHitTest != 0 && m_lastHitTest != HTCAPTION)
return false;
#endif
return dragRect().contains(globalPoint);
}
@@ -308,7 +343,7 @@ bool FloatingWindow::hasSingleDockWidget() const
bool FloatingWindow::beingDeleted() const
{
if (m_deleteScheduled)
if (m_deleteScheduled || m_inDtor)
return true;
// TODO: Confusing logic
@@ -387,10 +422,10 @@ void FloatingWindow::updateTitleAndIcon()
m_titleBar->setTitle(title);
m_titleBar->setIcon(icon);
if (KDDockWidgets::usesNativeTitleBar()) {
setWindowTitle(title);
setWindowIcon(icon);
}
// Even without a native title bar it's nice to set the window title/icon, so it shows
// in the taskbar (when minimization is supported), or Alt-Tab (in supporting Window Managers)
setWindowTitle(title);
setWindowIcon(icon);
}
void FloatingWindow::onCloseEvent(QCloseEvent *e)
@@ -438,6 +473,7 @@ LayoutSaver::FloatingWindow FloatingWindow::serialize() const
auto mainWindow = qobject_cast<MainWindowBase*>(parentWidget());
fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
: -1;
return fw;
}
@@ -465,4 +501,3 @@ bool FloatingWindow::event(QEvent *ev)
return QWidgetAdapter::event(ev);
}

View File

@@ -12,11 +12,13 @@
#ifndef KD_FLOATING_WINDOW_P_H
#define KD_FLOATING_WINDOW_P_H
#include "docks_export.h"
#include "../docks_export.h"
#include "../QWidgetAdapter.h"
#include "../LayoutSaver_p.h"
#include "Frame_p.h"
#include "Draggable_p.h"
#include "QWidgetAdapter.h"
#include "LayoutSaver_p.h"
#include "DropArea_p.h"
#include "Qt5Qt6Compat_p.h"
QT_BEGIN_NAMESPACE
class QAbstractNativeEventFilter;
@@ -49,9 +51,15 @@ public:
std::unique_ptr<WindowBeingDragged> makeWindow() override;
DockWidgetBase *singleDockWidget() const override;
const QVector<DockWidgetBase*> dockWidgets() const;
const Frame::List frames() const;
DropArea *dropArea() const { return m_dropArea; }
#ifdef Q_OS_WIN
void setLastHitTest(int hitTest) {
m_lastHitTest = hitTest;
}
#endif
/**
* @brief Returns the title bar.
*
@@ -128,18 +136,20 @@ Q_SIGNALS:
void numFramesChanged();
void windowStateChanged(QWindowStateChangeEvent *);
protected:
void setupWindow();
void maybeCreateResizeHandler();
#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
bool nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result) override;
#endif
bool event(QEvent *ev) override;
void onCloseEvent(QCloseEvent *) override;
DropArea *const m_dropArea;
QPointer<DropArea> m_dropArea;
TitleBar *const m_titleBar;
private:
Q_DISABLE_COPY(FloatingWindow)
void maybeCreateResizeHandler();
void onFrameCountChanged(int count);
void onVisibleFrameCountChanged(int count);
bool m_disableSetVisible = false;
@@ -148,6 +158,9 @@ private:
bool m_updatingTitleBarVisibility = false;
QMetaObject::Connection m_layoutDestroyedConnection;
QAbstractNativeEventFilter *m_nchittestFilter = nullptr;
#ifdef Q_OS_WIN
int m_lastHitTest = 0;
#endif
};
}

View File

@@ -25,6 +25,7 @@
#include "DockRegistry_p.h"
#include "Config.h"
#include "TitleBar_p.h"
#include "TabWidget_p.h"
#include "FrameworkWidgetFactory.h"
#include <QCloseEvent>
@@ -49,6 +50,7 @@ static FrameOptions actualOptions(FrameOptions options)
Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
: LayoutGuestWidget(parent)
, FocusScope(this)
, m_tabWidget(Config::self().frameworkWidgetFactory()->createTabWidget(this))
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
, m_options(actualOptions(options))
{
@@ -57,6 +59,10 @@ Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
qCDebug(creation) << "Frame" << ((void*)this) << s_dbg_numFrames;
connect(this, &Frame::currentDockWidgetChanged, this, &Frame::updateTitleAndIcon);
connect(m_tabWidget->asWidget(), SIGNAL(currentTabChanged(int)),
this, SLOT(onCurrentTabChanged(int)));
setDropArea(qobject_cast<DropArea *>(QWidgetAdapter::parentWidget()));
m_inCtor = false;
}
@@ -135,7 +141,7 @@ void Frame::insertWidget(DockWidgetBase *dockWidget, int index, AddingOption add
<< "; addingOption=" << addingOption;
Q_ASSERT(dockWidget);
if (contains(dockWidget)) {
if (containsDockWidget(dockWidget)) {
qWarning() << "Frame::addWidget dockWidget already exists. this=" << this << "; dockWidget=" << dockWidget;
return;
}
@@ -171,11 +177,25 @@ void Frame::removeWidget(DockWidgetBase *dw)
removeWidget_impl(dw);
}
void Frame::detachTab(DockWidgetBase *dw)
FloatingWindow* Frame::detachTab(DockWidgetBase *dockWidget)
{
if (m_inCtor || m_inDtor) return;
if (m_inCtor || m_inDtor) return nullptr;
detachTab_impl(dw);
QRect r = dockWidget->geometry();
removeWidget(dockWidget);
auto newFrame = Config::self().frameworkWidgetFactory()->createFrame();
const QPoint globalPoint = mapToGlobal(QPoint(0, 0));
newFrame->addWidget(dockWidget);
// We're potentially already dead at this point, as frames with 0 tabs auto-destruct. Don't access members from this point.
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(newFrame);
r.moveTopLeft(globalPoint);
floatingWindow->setSuggestedGeometry(r);
floatingWindow->show();
return floatingWindow;
}
int Frame::indexOfDockWidget(DockWidgetBase *dw)
@@ -231,7 +251,7 @@ int Frame::dockWidgetCount() const
{
if (m_inCtor || m_inDtor) return 0;
return dockWidgetCount_impl();
return m_tabWidget->numDockWidgets();
}
void Frame::onDockWidgetCountChanged()
@@ -265,6 +285,16 @@ void Frame::onCurrentTabChanged(int index)
}
}
void Frame::isFocusedChangedCallback()
{
Q_EMIT isFocusedChanged();
}
void Frame::focusedWidgetChangedCallback()
{
Q_EMIT focusedWidgetChanged();
}
void Frame::updateTitleBarVisibility()
{
if (m_updatingTitleBar || m_beingDeleted) {
@@ -347,7 +377,7 @@ const DockWidgetBase::List Frame::dockWidgets() const
return dockWidgets;
}
bool Frame::contains(DockWidgetBase *dockWidget) const
bool Frame::containsDockWidget(DockWidgetBase *dockWidget) const
{
const int count = dockWidgetCount();
for (int i = 0, e = count; i != e; ++i) {
@@ -442,7 +472,7 @@ bool Frame::anyNonDockable() const
void Frame::onDockWidgetShown(DockWidgetBase *w)
{
if (hasSingleDockWidget() && contains(w)) { // We have to call contains because it might be being in process of being reparented
if (hasSingleDockWidget() && containsDockWidget(w)) { // We have to call contains because it might be being in process of being reparented
if (!QWidgetAdapter::isVisible()) {
qCDebug(hiding) << "Widget" << w << " was shown, we're=" << "; visible="
<< QWidgetAdapter::isVisible();
@@ -453,7 +483,7 @@ void Frame::onDockWidgetShown(DockWidgetBase *w)
void Frame::onDockWidgetHidden(DockWidgetBase *w)
{
if (hasSingleDockWidget() && contains(w)) { // We have to call contains because it might be being in process of being reparented
if (hasSingleDockWidget() && containsDockWidget(w)) { // We have to call contains because it might be being in process of being reparented
if (QWidgetAdapter::isVisible()) {
qCDebug(hiding) << "Widget" << w << " was hidden, we're="
<< "; visible=" << QWidgetAdapter::isVisible()
@@ -573,7 +603,6 @@ bool Frame::isInMainWindow() const
bool Frame::event(QEvent *e)
{
if (e->type() == QEvent::ParentChange) {
qCDebug(docking) << "Frame: parent changed to =" << QWidgetAdapter::parentWidget();
if (auto dropArea = qobject_cast<DropArea *>(QWidgetAdapter::parentWidget())) {
setDropArea(dropArea);
} else {
@@ -637,7 +666,7 @@ QSize Frame::dockWidgetsMinSize() const
{
QSize size = Layouting::Item::hardcodedMinimumSize;
for (DockWidgetBase *dw : dockWidgets())
size = size.expandedTo(Layouting::Widget_qwidget::widgetMinSize(dw));
size = size.expandedTo(Layouting::Widget::widgetMinSize(dw));
return size;
}
@@ -687,3 +716,8 @@ MainWindowBase *Frame::mainWindow() const
return m_dropArea ? m_dropArea->mainWindow()
: nullptr;
}
TabWidget *Frame::tabWidget() const
{
return m_tabWidget;
}

View File

@@ -19,21 +19,22 @@
#ifndef KD_FRAME_P_H
#define KD_FRAME_P_H
#include "docks_export.h"
#include "QWidgetAdapter.h"
#include "LayoutSaver_p.h"
#include "multisplitter/Widget_qwidget.h"
#include "multisplitter/Item_p.h"
#include "FocusScope.h"
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/FocusScope.h"
#include "../LayoutSaver_p.h"
#include "multisplitter/Widget.h"
#include <QWidget>
#include <QVector>
#include <QDebug>
#include <QPointer>
class TestDocks;
namespace KDDockWidgets {
class TitleBar;
class TabWidget;
class DropArea;
class DockWidgetBase;
class FloatingWindow;
@@ -55,6 +56,7 @@ class DOCKS_EXPORT Frame
{
Q_OBJECT
Q_PROPERTY(KDDockWidgets::TitleBar* titleBar READ titleBar CONSTANT)
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentDockWidgetChanged)
public:
typedef QList<Frame *> List;
@@ -78,7 +80,7 @@ public:
void removeWidget(DockWidgetBase *);
///@brief detaches this dock widget
void detachTab(DockWidgetBase *);
FloatingWindow *detachTab(DockWidgetBase *);
///@brief returns the index of the specified dock widget
int indexOfDockWidget(DockWidgetBase *);
@@ -104,6 +106,9 @@ public:
/// @brief returns the number of dock widgets inside the frame
int dockWidgetCount() const;
/// @brief returns the tab widget
TabWidget *tabWidget() const;
void updateTitleAndIcon();
void onDockWidgetTitleChanged();
void updateTitleBarVisibility();
@@ -165,7 +170,7 @@ public:
bool alwaysShowsTabs() const { return m_options & FrameOption_AlwaysShowsTabs; }
/// @brief returns whether the dockwidget @p w is inside this frame
bool contains(DockWidgetBase *w) const;
bool containsDockWidget(DockWidgetBase *w) const;
///@brief returns the FloatingWindow this frame is in, if any
FloatingWindow *floatingWindow() const;
@@ -240,9 +245,16 @@ Q_SIGNALS:
void hasTabsVisibleChanged();
void layoutInvalidated();
void isInMainWindowChanged();
void isFocusedChanged() override; // override from non-QObject
void focusedWidgetChanged() override;
void isFocusedChanged();
void focusedWidgetChanged();
protected Q_SLOTS:
void onDockWidgetCountChanged();
void onCurrentTabChanged(int index);
protected:
void isFocusedChangedCallback() final;
void focusedWidgetChangedCallback() final;
virtual void renameTab(int index, const QString &) = 0;
@@ -265,10 +277,8 @@ protected:
* Any widget having 16777215x16777215 is ignored (represents not having a max-size, QWIDGETSIZE_MAX)
*/
QSize biggestDockWidgetMaxSize() const;
void onDockWidgetCountChanged();
virtual void removeWidget_impl(DockWidgetBase *) = 0;
virtual void detachTab_impl(DockWidgetBase *) = 0;
virtual int indexOfDockWidget_impl(DockWidgetBase *) = 0;
virtual int currentIndex_impl() const = 0;
virtual void setCurrentTabIndex_impl(int index) = 0;
@@ -276,17 +286,21 @@ protected:
virtual void insertDockWidget_impl(DockWidgetBase *, int index) = 0;
virtual DockWidgetBase *dockWidgetAt_impl(int index) const = 0;
virtual DockWidgetBase *currentDockWidget_impl() const = 0;
virtual int dockWidgetCount_impl() const = 0;
virtual int nonContentsHeight() const = 0;
bool m_inDtor = false;
TabWidget *const m_tabWidget;
TitleBar *const m_titleBar;
private:
Q_DISABLE_COPY(Frame)
friend class TestDocks;
friend class ::TestDocks;
friend class TabWidget;
void onCurrentTabChanged(int index);
void scheduleDeleteLater();
bool event(QEvent *) override;
bool m_inCtor = true;
TitleBar *const m_titleBar;
DropArea *m_dropArea = nullptr;
const FrameOptions m_options;
QPointer<Layouting::Item> m_layoutItem;

View File

@@ -17,17 +17,9 @@ Q_LOGGING_CATEGORY(hovering, "kdab.docks.hovering", QtWarningMsg)
Q_LOGGING_CATEGORY(mouseevents, "kdab.docks.mouseevents", QtWarningMsg)
Q_LOGGING_CATEGORY(state, "kdab.docks.state", QtWarningMsg)
Q_LOGGING_CATEGORY(docking, "kdab.docks.docking", QtWarningMsg)
Q_LOGGING_CATEGORY(globalevents, "kdab.docks.globalevents", QtWarningMsg)
Q_LOGGING_CATEGORY(hiding, "kdab.docks.hiding", QtWarningMsg)
Q_LOGGING_CATEGORY(closing, "kdab.docks.closing", QtWarningMsg)
Q_LOGGING_CATEGORY(overlay, "kdab.docks.overlay", QtWarningMsg)
Q_LOGGING_CATEGORY(dropping, "kdab.docks.dropping", QtWarningMsg)
Q_LOGGING_CATEGORY(restoring, "kdab.docks.restoring", QtWarningMsg)
Q_LOGGING_CATEGORY(title, "kdab.docks.title", QtWarningMsg)
Q_LOGGING_CATEGORY(closebutton, "kdab.docks.closebutton", QtWarningMsg)
Q_LOGGING_CATEGORY(sizing, "kdab.multisplitter.sizing", QtWarningMsg)
Q_LOGGING_CATEGORY(multisplittercreation, "kdab.multisplitter.multisplittercreation", QtWarningMsg)
Q_LOGGING_CATEGORY(addwidget, "kdab.multisplitter.addwidget", QtWarningMsg)
Q_LOGGING_CATEGORY(anchors, "kdab.multisplitter.anchors", QtWarningMsg)
Q_LOGGING_CATEGORY(item, "kdab.multisplitter.item", QtWarningMsg)
Q_LOGGING_CATEGORY(placeholder, "kdab.multisplitter.placeholder", QtWarningMsg)

View File

@@ -29,7 +29,7 @@
#include "DockRegistry_p.h"
#include "Config.h"
#include "FrameworkWidgetFactory.h"
#include "multisplitter/Widget_qwidget.h"
#include "multisplitter/Widget.h"
#include "DropArea_p.h"
#include "WindowBeingDragged_p.h"
@@ -46,7 +46,7 @@ MultiSplitter::MultiSplitter(QWidgetOrQuick *parent)
setLayoutSize(parent->size());
qCDebug(multisplittercreation()) << "MultiSplitter";
qCDebug(creation) << "MultiSplitter";
// Initialize min size
updateSizeConstraints();
@@ -56,7 +56,7 @@ MultiSplitter::MultiSplitter(QWidgetOrQuick *parent)
MultiSplitter::~MultiSplitter()
{
qCDebug(multisplittercreation) << "~MultiSplitter" << this;
qCDebug(creation) << "~MultiSplitter" << this;
if (m_rootItem->hostWidget()->asQObject() == this)
delete m_rootItem;
DockRegistry::self()->unregisterLayout(this);
@@ -72,7 +72,7 @@ bool MultiSplitter::onResize(QSize newSize)
qCDebug(sizing) << Q_FUNC_INFO << "; new=" << newSize
<< "; window=" << window();
QScopedValueRollback<bool>(m_inResizeEvent, true); // to avoid re-entrancy
QScopedValueRollback<bool> resizeGuard(m_inResizeEvent, true); // to avoid re-entrancy
if (!LayoutSaver::restoreInProgress()) {
// don't resize anything while we're restoring the layout
@@ -135,7 +135,7 @@ bool MultiSplitter::validateInputs(QWidgetOrQuick *widget,
Layouting::Item *item = itemForFrame(qobject_cast<Frame*>(widget));
if (contains(item)) {
if (containsItem(item)) {
qWarning() << "MultiSplitter::addWidget: Already contains" << widget;
return false;
}
@@ -148,7 +148,7 @@ bool MultiSplitter::validateInputs(QWidgetOrQuick *widget,
const bool relativeToThis = relativeToFrame == nullptr;
Layouting::Item *relativeToItem = itemForFrame(relativeToFrame);
if (!relativeToThis && !contains(relativeToItem)) {
if (!relativeToThis && !containsItem(relativeToItem)) {
qWarning() << "MultiSplitter::addWidget: Doesn't contain relativeTo:"
<< "; relativeToFrame=" << relativeToFrame
<< "; relativeToItem=" << relativeToItem
@@ -202,7 +202,7 @@ void MultiSplitter::addWidget(QWidgetOrQuick *w, Location location,
newItem->setGuestWidget(frame);
frame->addWidget(dw, option);
} else if (auto ms = qobject_cast<MultiSplitter*>(w)) {
newItem = ms->rootItem();
newItem = ms->m_rootItem;
newItem->setHostWidget(this);
if (FloatingWindow *fw = ms->floatingWindow()) {
@@ -242,12 +242,12 @@ void MultiSplitter::removeItem(Layouting::Item *item)
item->parentContainer()->removeItem(item);
}
bool MultiSplitter::contains(const Layouting::Item *item) const
bool MultiSplitter::containsItem(const Layouting::Item *item) const
{
return m_rootItem->contains_recursive(item);
}
bool MultiSplitter::contains(const Frame *frame) const
bool MultiSplitter::containsFrame(const Frame *frame) const
{
return itemForFrame(frame) != nullptr;
}
@@ -267,7 +267,7 @@ int MultiSplitter::placeholderCount() const
return count() - visibleCount();
}
Layouting::Separator::List MultiSplitter::separators() const
QVector<Layouting::Separator*> MultiSplitter::separators() const
{
return m_rootItem->separators_recursive();
}
@@ -371,6 +371,11 @@ void MultiSplitter::layoutEqually(Layouting::ItemContainer *container)
}
}
void MultiSplitter::clearLayout()
{
m_rootItem->clear();
}
bool MultiSplitter::checkSanity() const
{
return m_rootItem->checkSanity();
@@ -404,6 +409,11 @@ QSize MultiSplitter::layoutMinimumSize() const
return m_rootItem->minSize();
}
QSize MultiSplitter::layoutMaximumSizeHint() const
{
return m_rootItem->maxSizeHint();
}
QSize MultiSplitter::size() const { return m_rootItem->size(); }
void MultiSplitter::setLayoutMinimumSize(QSize sz)

View File

@@ -23,7 +23,6 @@
#define KDDOCKWIDGETS_MULTISPLITTER_P_H
#include "docks_export.h"
#include "multisplitter/Separator_p.h"
#include "QWidgetAdapter.h"
#include "KDDockWidgets.h"
#include "LayoutSaver_p.h"
@@ -35,12 +34,14 @@ class Separator;
class Widget_qwidget;
}
class TestDocks;
namespace KDDockWidgets {
class MainWindowBase;
class FloatingWindow;
class Frame;
class WindowBeingDragged;
struct WindowBeingDragged;
/**
* MultiSplitter is simply a wrapper around Layouting::Item in which the hosted widgets are
@@ -87,12 +88,12 @@ public:
/**
* @brief Returns true if this layout contains the specified item.
*/
bool contains(const Layouting::Item *) const;
bool containsItem(const Layouting::Item *) const;
/**
* @brief Returns true if this layout contains the specified frame.
*/
bool contains(const Frame *) const;
bool containsFrame(const Frame *) const;
/**
* @brief Returns the number of Item objects in this layout.
@@ -120,10 +121,6 @@ public:
*/
const QVector<Layouting::Item*> items() const;
/**
* @brief Returns the root container item
*/
Layouting::ItemContainer *rootItem() const;
/**
* Called by the indicators, so they draw the drop rubber band at the correct place.
@@ -171,6 +168,11 @@ public:
*/
QSize layoutMinimumSize() const;
/**
* @brief returns the layout's maximum size hint
*/
QSize layoutMaximumSizeHint() const;
/**
* @brief getter for the size
*/
@@ -204,6 +206,9 @@ public:
/// @brief overload that just resizes widgets within a sub-tree
void layoutEqually(Layouting::ItemContainer *);
/// @brief clears the layout
void clearLayout();
Q_SIGNALS:
void visibleWidgetCountChanged(int count);
@@ -213,7 +218,7 @@ protected:
private:
bool m_inResizeEvent = false;
friend class TestDocks;
friend class ::TestDocks;
/**
* @brief returns the frames contained in @p frameOrMultiSplitter
@@ -222,6 +227,7 @@ private:
*/
QList<Frame*> framesFrom(QWidgetOrQuick *frameOrMultiSplitter) const;
Layouting::ItemContainer *rootItem() const;
// For debug/hardening
bool validateInputs(QWidgetOrQuick *widget, KDDockWidgets::Location location,

View File

@@ -18,6 +18,7 @@
#include "Position_p.h"
#include "DockRegistry_p.h"
#include "MultiSplitter_p.h"
#include "FloatingWindow_p.h"
#include <algorithm>
@@ -82,7 +83,7 @@ bool Position::containsPlaceholder(Layouting::Item *item) const
void Position::removePlaceholders()
{
QScopedValueRollback<bool>(m_clearing, true);
QScopedValueRollback<bool> clearGuard(m_clearing, true);
m_placeholders.clear();
}
@@ -162,7 +163,7 @@ LayoutSaver::Position Position::serialize() const
Layouting::Item *item = itemRef->item;
MultiSplitter *layout = DockRegistry::self()->layoutForItem(item);
const int itemIndex = layout->items().indexOf(item);
const auto itemIndex = layout->items().indexOf(item);
auto fw = layout->floatingWindow();
auto mainWindow = layout->mainWindow();

View File

@@ -21,7 +21,6 @@
#include "docks_export.h"
#include "Logging_p.h"
#include "LayoutSaver_p.h"
#include "multisplitter/Item_p.h"
#include "QWidgetAdapter.h"
#include <QScopedValueRollback>

View File

@@ -58,7 +58,7 @@ void SideBar::removeDockWidget(DockWidgetBase *dw)
updateSize();
}
bool SideBar::contains(DockWidgetBase *dw) const
bool SideBar::containsDockWidget(DockWidgetBase *dw) const
{
return m_dockWidgets.contains(dw);
}
@@ -107,3 +107,19 @@ void SideBar::toggleOverlay(DockWidgetBase *dw)
{
m_mainWindow->toggleOverlayOnSideBar(dw);
}
QStringList SideBar::serialize() const
{
QStringList ids;
ids.reserve(m_dockWidgets.size());
for (DockWidgetBase *dw : m_dockWidgets)
ids << dw->uniqueName();
return ids;
}
void SideBar::clear()
{
for (DockWidgetBase *dw : qAsConst(m_dockWidgets))
removeDockWidget(dw);
}

View File

@@ -29,7 +29,7 @@ public:
void addDockWidget(DockWidgetBase *dw);
void removeDockWidget(DockWidgetBase *dw);
bool contains(DockWidgetBase *) const;
bool containsDockWidget(DockWidgetBase *) const;
/// @brief Returns this side bar's orientation
Qt::Orientation orientation() const;
@@ -49,6 +49,13 @@ public:
/// @brief Toggles the dock widget overlay. Equivalent to the user clicking on the button.
void toggleOverlay(DockWidgetBase *);
/// @brief returns a serialization of this sidebar's state
/// Currently it's just a list of dock widget ids
QStringList serialize() const;
/// @brief clears the sidebar (removes all dock widgets from it)
void clear();
protected:
virtual void addDockWidget_Impl(DockWidgetBase *dock) = 0;
virtual void removeDockWidget_Impl(DockWidgetBase *dock) = 0;

View File

@@ -84,42 +84,36 @@ std::unique_ptr<WindowBeingDragged> TabBar::makeWindow()
if (!dock)
return {};
FloatingWindow *floatingWindow = detachTab(dock);
FloatingWindow *floatingWindow = frame()->detachTab(dock);
if (!floatingWindow)
return {};
auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable*>(floatingWindow)
: static_cast<Draggable*>(this);
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, draggable));
}
FloatingWindow * TabBar::detachTab(DockWidgetBase *dockWidget)
{
QRect r = dockWidget->geometry();
m_tabWidget->removeDockWidget(dockWidget);
auto newFrame = Config::self().frameworkWidgetFactory()->createFrame();
const QPoint globalPoint = m_thisWidget->mapToGlobal(QPoint(0, 0));
newFrame->addWidget(dockWidget);
// We're potentially already dead at this point, as frames with 0 tabs auto-destruct. Don't access members from this point.
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(newFrame);
r.moveTopLeft(globalPoint);
floatingWindow->setSuggestedGeometry(r);
floatingWindow->show();
return floatingWindow;
}
void TabBar::onMousePress(QPoint localPos)
{
m_lastPressedDockWidget = dockWidgetAt(localPos);
}
void TabBar::onMouseDoubleClick(QPoint localPos)
{
if (DockWidgetBase *dw = dockWidgetAt(localPos))
dw->setFloating(true);
}
bool TabBar::hasSingleDockWidget() const
{
return numDockWidgets() == 1;
}
int TabBar::numDockWidgets() const
{
return m_tabWidget->numDockWidgets();
}
QWidgetOrQuick *TabBar::asWidget() const
{
return m_thisWidget;
@@ -140,15 +134,6 @@ TabWidget::TabWidget(QWidgetOrQuick *thisWidget, Frame *frame)
, m_frame(frame)
, m_thisWidget(thisWidget)
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
// Little ifdefery, as this is not so easy to abstract
QObject::connect(static_cast<QTabWidget*>(thisWidget), &QTabWidget::currentChanged,
frame, &Frame::onCurrentTabChanged);
#else
qWarning() << Q_FUNC_INFO << "Implement me";
#endif
}
void TabWidget::setCurrentDockWidget(DockWidgetBase *dw)
@@ -156,12 +141,17 @@ void TabWidget::setCurrentDockWidget(DockWidgetBase *dw)
setCurrentDockWidget(indexOfDockWidget(dw));
}
DockWidgetBase *TabWidget::currentDockWidget() const
{
return dockwidgetAt(currentIndex());
}
void TabWidget::addDockWidget(DockWidgetBase *dock)
{
insertDockWidget(dock, numDockWidgets());
}
void TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
bool TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
{
Q_ASSERT(dock);
qCDebug(addwidget) << Q_FUNC_INFO << dock << "; count before=" << numDockWidgets();
@@ -173,7 +163,7 @@ void TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
if (contains(dock)) {
qWarning() << Q_FUNC_INFO << "Refusing to add already existing widget";
return;
return false;
}
QPointer<Frame> oldFrame = dock->frame();
@@ -191,6 +181,8 @@ void TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
delete oldFrame;
}
return true;
}
bool TabWidget::contains(DockWidgetBase *dw) const
@@ -213,7 +205,6 @@ std::unique_ptr<WindowBeingDragged> TabWidget::makeWindow()
// This is called when using Flag_HideTitleBarWhenTabsVisible
// For detaching individual tabs, TabBar::makeWindow() is called.
if (auto floatingWindow = qobject_cast<FloatingWindow*>(asWidget()->window())) {
if (floatingWindow->hasSingleFrame()) {
// We're already in a floating window, and it only has 1 dock widget.
// So there's no detachment to be made, we just move the window.
@@ -221,7 +212,7 @@ std::unique_ptr<WindowBeingDragged> TabWidget::makeWindow()
}
}
QRect r = m_frame->QWidget::geometry();
QRect r = m_frame->QWidgetAdapter::geometry();
const QPoint globalPoint = m_thisWidget->mapToGlobal(QPoint(0, 0));
@@ -255,3 +246,26 @@ void TabWidget::onCurrentTabChanged(int index)
{
Q_UNUSED(index);
}
bool TabWidget::onMouseDoubleClick(QPoint localPos)
{
// User clicked the empty space of the tab widget and we don't have title bar
// We float the entire frame.
if (!(Config::self().flags() & Config::Flag_HideTitleBarWhenTabsVisible) || tabBar()->dockWidgetAt(localPos))
return false;
Frame *frame = this->frame();
if (FloatingWindow *fw = frame->floatingWindow()) {
if (!fw->hasSingleFrame()) {
makeWindow();
return true;
}
} else if (frame->isInMainWindow()) {
makeWindow();
return true;
}
return false;
}

View File

@@ -20,12 +20,12 @@
#ifndef KD_TAB_WIDGET_P_H
#define KD_TAB_WIDGET_P_H
#include "docks_export.h"
#include "../Draggable_p.h"
#include "../Frame_p.h"
#include "DockWidgetBase.h"
#include "kddockwidgets/docks_export.h"
#include "kddockwidgets/DockWidgetBase.h"
#include "Draggable_p.h"
#include "Frame_p.h"
#include <QTabBar>
#include <QVector>
#include <memory>
@@ -58,23 +58,15 @@ public:
// Draggable
std::unique_ptr<WindowBeingDragged> makeWindow() override;
/**
* @brief detaches a dock widget and shows it as a floating dock widget
* The dock widget is morphed into a FloatingWindow for convenience.
* @param dockWidget the dock widget to detach
* @returns the created FloatingWindow
*/
FloatingWindow *detachTab(DockWidgetBase *dockWidget);
void onMousePress(QPoint localPos);
void onMouseDoubleClick(QPoint localPos);
///@brief returns whether there's only 1 tab
bool hasSingleDockWidget() const;
virtual int numDockWidgets() const = 0;
int numDockWidgets() const;
virtual int tabAt(QPoint localPos) const = 0;
/**
* @brief Returns this class as a QWidget (if using QtWidgets) or QQuickItem
*/
@@ -119,7 +111,10 @@ public:
virtual void setCurrentDockWidget(int index) = 0;
void setCurrentDockWidget(DockWidgetBase *);
virtual void insertDockWidget(int index, DockWidgetBase *, const QIcon&, const QString &title) = 0;
/// @brief Returns the current dock widget
DockWidgetBase *currentDockWidget() const;
virtual bool insertDockWidget(int index, DockWidgetBase *, const QIcon&, const QString &title) = 0;
virtual void setTabBarAutoHide(bool) = 0;
@@ -139,18 +134,12 @@ public:
*/
virtual DockWidgetBase *dockwidgetAt(int index) const = 0;
/**
* @brief detaches a dock widget and shows it as a floating dock widget
* @param dockWidget the dock widget to detach
*/
virtual void detachTab(DockWidgetBase *dockWidget) = 0;
/**
* @brief inserts @p dockwidget into the TabWidget, at @p index
* @param dockwidget the dockwidget to insert
* @param index The index to where to put it
*/
void insertDockWidget(DockWidgetBase *dockwidget, int index);
bool insertDockWidget(DockWidgetBase *dockwidget, int index);
/**
* @brief Returns whether dockwidget @p dw is contained in this tab widget
@@ -175,10 +164,16 @@ public:
std::unique_ptr<WindowBeingDragged> makeWindow() override;
DockWidgetBase *singleDockWidget() const override;
//Q_SIGNALS: // Not a OQbject
virtual void currentTabChanged(int index) = 0;
virtual void currentDockWidgetChanged(KDDockWidgets::DockWidgetBase *) = 0;
virtual void countChanged() {};
protected:
void onTabInserted();
void onTabRemoved();
void onCurrentTabChanged(int index);
bool onMouseDoubleClick(QPoint localPos);
private:
Frame *const m_frame;

View File

@@ -69,6 +69,8 @@ void TitleBar::init()
// repaint
update();
});
updateCloseButton();
updateFloatButton();
}
TitleBar::~TitleBar()
@@ -89,6 +91,16 @@ bool TitleBar::onDoubleClicked()
return false;
}
void TitleBar::updateCloseButton()
{
const bool anyNonClosable = frame() ? frame()->anyNonClosable()
: (floatingWindow() ? floatingWindow()->anyNonClosable()
: false);
setCloseButtonEnabled(!anyNonClosable);
}
void TitleBar::toggleMaximized()
{
if (!m_floatingWindow)
@@ -116,15 +128,34 @@ bool TitleBar::isOverlayed() const
return m_frame && m_frame->isOverlayed();
}
void TitleBar::setCloseButtonEnabled(bool enabled)
{
if (enabled != m_closeButtonEnabled) {
m_closeButtonEnabled = enabled;
Q_EMIT closeButtonEnabledChanged(enabled);
}
}
void TitleBar::setFloatButtonVisible(bool visible)
{
if (visible != m_floatButtonVisible) {
m_floatButtonVisible = visible;
Q_EMIT floatButtonVisibleChanged(visible);
}
}
void TitleBar::setFloatButtonToolTip(const QString &tip)
{
if (tip != m_floatButtonToolTip) {
m_floatButtonToolTip = tip;
Q_EMIT floatButtonToolTipChanged(tip);
}
}
void TitleBar::setTitle(const QString &title)
{
if (title != m_title) {
m_title = title;
qCDebug(::title) << Q_FUNC_INFO << "\n title=" << title
<< "\n this=" << this
<< "\n parentWidget=" << parentWidget()
<< "\n isVisible=" << isVisible()
<< "\nwindow=" << window();
update();
Q_EMIT titleChanged();
}
@@ -140,7 +171,7 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
{
if (!isVisible() && window()->isVisible()) {
qWarning() << "TitleBar::makeWindow shouldn't be called on invisible title bar"
<< this << window()->isVisible() << parentWidget();
<< this << window()->isVisible();
if (m_floatingWindow) {
qWarning() << "Has floating window with titlebar=" << m_floatingWindow->titleBar()
@@ -198,6 +229,12 @@ bool TitleBar::supportsFloatingButton() const
return false;
}
if (DockWidgetBase *dw = singleDockWidget()) {
// Don't show the dock/undock button if the window is not dockable
if (dw->options() & DockWidgetBase::Option_NotDockable)
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();
@@ -361,3 +398,24 @@ void TitleBar::onAutoHideClicked()
}
}
}
bool TitleBar::closeButtonEnabled() const
{
return m_closeButtonEnabled;
}
bool TitleBar::floatButtonVisible() const
{
return m_floatButtonVisible;
}
QString TitleBar::floatButtonToolTip() const
{
return m_floatButtonToolTip;
}
void TitleBar::updateFloatButton()
{
setFloatButtonToolTip(floatingWindow() ? tr("Dock window") : tr("Undock window"));
setFloatButtonVisible(supportsFloatingButton());
}

View File

@@ -12,12 +12,12 @@
#ifndef KD_TITLEBAR_P_H
#define KD_TITLEBAR_P_H
#include "docks_export.h"
#include "kddockwidgets/docks_export.h"
#include "QWidgetAdapter.h"
#include "kddockwidgets/QWidgetAdapter.h"
#include "kddockwidgets/DockWidgetBase.h"
#include "Draggable_p.h"
#include "Frame_p.h"
#include "DockWidgetBase.h"
#include <QVector>
#include <QIcon>
@@ -27,7 +27,7 @@ class QHBoxLayout;
class QLabel;
QT_END_NAMESPACE
class TestCommon;
class TestDocks;
namespace KDDockWidgets {
@@ -41,6 +41,9 @@ class DOCKS_EXPORT TitleBar : public QWidgetAdapter
Q_OBJECT
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(bool hasIcon READ hasIcon NOTIFY iconChanged)
Q_PROPERTY(bool closeButtonEnabled READ closeButtonEnabled WRITE setCloseButtonEnabled NOTIFY closeButtonEnabledChanged)
Q_PROPERTY(bool floatButtonVisible READ floatButtonVisible WRITE setFloatButtonVisible NOTIFY floatButtonVisibleChanged)
Q_PROPERTY(QString floatButtonToolTip READ floatButtonToolTip NOTIFY floatButtonToolTipChanged)
public:
typedef QVector<TitleBar *> List;
@@ -87,20 +90,24 @@ public:
QIcon icon() const;
///@brief toggle floating
bool onDoubleClicked();
Q_INVOKABLE bool onDoubleClicked();
///@brief getter for m_frame
const Frame *frame() const { return m_frame; }
Frame *frame() const { return m_frame; }
///@brief getter for m_floatingWindow
const FloatingWindow *floatingWindow() const { return m_floatingWindow; }
FloatingWindow *floatingWindow() const { return m_floatingWindow; }
virtual void updateCloseButton() {}
/// @brief updates the close button enabled state
void updateCloseButton();
Q_SIGNALS:
void titleChanged();
void iconChanged();
void isFocusedChanged();
void closeButtonEnabledChanged(bool);
void floatButtonVisibleChanged(bool);
void floatButtonToolTipChanged(const QString &);
protected:
@@ -111,25 +118,31 @@ protected:
Q_INVOKABLE void toggleMaximized();
Q_INVOKABLE void onAutoHideClicked();
virtual void updateFloatButton() {}
bool closeButtonEnabled() const;
bool floatButtonVisible() const;
QString floatButtonToolTip() const;
virtual void updateMaximizeButton() {}
virtual void updateMinimizeButton() {}
virtual void updateAutoHideButton() {}
#ifdef DOCKS_DEVELOPER_MODE
// The following are needed for the unit-tests
virtual bool isCloseButtonVisible() const { return true; }
virtual bool isCloseButtonEnabled() const { return true; }
virtual bool isFloatButtonVisible() const { return true; }
virtual bool isFloatButtonEnabled() const { return true; }
virtual bool isCloseButtonVisible() const = 0;
virtual bool isCloseButtonEnabled() const = 0;
virtual bool isFloatButtonVisible() const = 0;
virtual bool isFloatButtonEnabled() const = 0;
#endif
void focusInEvent(QFocusEvent *event) override;
bool isOverlayed() const;
private:
friend class TestDocks;
friend class ::TestCommon;
friend class ::TestDocks;
void updateFloatButton();
void setCloseButtonEnabled(bool);
void setFloatButtonVisible(bool);
void setFloatButtonToolTip(const QString &);
void init();
@@ -140,6 +153,9 @@ private:
Frame *const m_frame;
FloatingWindow *const m_floatingWindow;
const bool m_supportsAutoHide;
bool m_closeButtonEnabled = true;
bool m_floatButtonVisible = true;
QString m_floatButtonToolTip;
};

View File

@@ -13,28 +13,58 @@
#define KD_UTILS_P_H
#include "Config.h"
#include "QWidgetAdapter.h"
#include <QApplication>
#include <QScreen>
#include <QWidget>
#include <QWindow>
#include <QMouseEvent>
#ifdef KDDOCKWIDGETS_QTQUICK
# include "private/quick/TitleBarQuick_p.h"
# include <QQuickItem>
# include <QQuickWindow>
# include <QQuickView>
#else
# include <QApplication>
# include <QAbstractButton>
# include <QLineEdit>
#endif
#ifdef QT_X11EXTRAS_LIB
# include <QtX11Extras/QX11Info>
#endif
QT_BEGIN_NAMESPACE
class QWidget;
class QWindow;
QT_END_NAMESPACE
namespace KDDockWidgets {
#ifdef KDDOCKWIDGETS_QTQUICK
inline QQuickItem* mouseAreaForPos(QQuickItem *item, QPointF globalPos);
#endif
inline bool isWayland()
{
return qApp->platformName() == QLatin1String("wayland");
}
inline bool isOffscreen()
{
return qApp->platformName() == QLatin1String("offscreen");
}
inline bool kddwUsesQtWidgets()
{
// Returns whether KDDW is built for QtWidgets or QtQuick
#ifdef KDDOCKWIDGETS_QTWIDGETS
return true;
#else
return false;
#endif
}
inline bool isLeftButtonPressed()
{
return qApp->mouseButtons() & Qt::LeftButton;
@@ -69,6 +99,24 @@ inline bool usesNativeDraggingAndResizing()
return usesNativeTitleBar() || usesAeroSnapWithCustomDecos();
}
inline bool usesFallbackMouseGrabber()
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
// Will use QWidget::grabMouse()
return false;
#else
// For QtQuick we use the global event filter as mouse delivery is flaky
// For example, the same QQuickItem that receives the press isn't receiving the mouse moves
// when the top-level window moves.
return true;
#endif
}
inline void activateWindow(QWindow *window)
{
window->requestActivate();
}
inline bool windowManagerHasTranslucency()
{
#ifdef QT_X11EXTRAS_LIB
@@ -102,6 +150,84 @@ inline int screenNumberForWindow(const QWindow *window)
return -1;
}
inline QMouseEvent *mouseEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
case QEvent::NonClientAreaMouseButtonPress:
case QEvent::NonClientAreaMouseButtonRelease:
case QEvent::NonClientAreaMouseMove:
return static_cast<QMouseEvent *>(e);
default:
break;
}
return nullptr;
}
inline bool isNonClientMouseEvent(const QEvent *e)
{
switch (e->type()) {
case QEvent::NonClientAreaMouseButtonPress:
case QEvent::NonClientAreaMouseButtonRelease:
case QEvent::NonClientAreaMouseMove:
return true;
default:
break;
}
return false;
}
inline bool isWindow(const QWindow *w)
{
return w != nullptr;
}
inline int startDragDistance()
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
return QApplication::startDragDistance();
#else
return 4;
#endif
}
/// @brief Returns the QWidget or QtQuickItem at the specified position
/// Basically QApplication::widgetAt() but with support for QtQuick
inline WidgetType* mouseReceiverAt(QPoint globalPos)
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
return qApp->widgetAt(globalPos);
#else
auto window = qobject_cast<QQuickWindow*>(qApp->topLevelAt(globalPos));
if (!window)
return nullptr;
return mouseAreaForPos(window->contentItem(), globalPos);
#endif
}
/// Not the entire TitleBar is draggable. For example, the close button won't allow to start a drag from there.
/// Returns true if we're over such controls where we shouldn't drag.
inline bool inDisallowDragWidget(QPoint globalPos)
{
WidgetType *widget = mouseReceiverAt(globalPos);
if (!widget)
return false;
#ifdef KDDOCKWIDGETS_QTWIDGETS
// User might have a line edit on the toolbar. TODO: Not so elegant fix, we should make the user's tabbar implement some virtual method...
return qobject_cast<QAbstractButton*>(widget) ||
qobject_cast<QLineEdit*>(widget);
#else
return widget->objectName() != QLatin1String("draggableMouseArea");
#endif
}
#ifdef KDDOCKWIDGETS_QTWIDGETS
inline int screenNumberForWidget(const QWidget *widget)
{
@@ -113,6 +239,21 @@ inline QSize screenSizeForWidget(const QWidget *widget)
return screenSizeForWindow(widget->window()->windowHandle());
}
inline QPoint mapToGlobal(QWidget *w, QPoint p)
{
return w->mapToGlobal(p);
}
inline void activateWindow(QWidget *widget)
{
widget->activateWindow();
}
inline bool isWindow(const QWidget *w)
{
return w && w->isWindow();
}
#else
inline int screenNumberForWidget(const QQuickItem *w)
@@ -125,8 +266,46 @@ inline QSize screenSizeForWidget(const QQuickItem *w)
return screenSizeForWindow(w->window());
}
inline QPoint mapToGlobal(QQuickItem *item, QPoint p)
{
return item->mapToGlobal(p).toPoint();
}
inline QQuickItem* mouseAreaForPos(QQuickItem *item, QPointF globalPos)
{
QRectF rect = item->boundingRect();
rect.moveTopLeft(item->mapToGlobal(QPointF(0, 0)));
// Assumes children are inside its parent. That's fine for KDDW's purposes.
if (!rect.contains(globalPos)) {
return nullptr;
}
const QList<QQuickItem*> children = item->childItems();
for (auto it = children.rbegin(), end = children.rend(); it != end; ++it) {
if (QQuickItem *receiver = mouseAreaForPos(*it, globalPos))
return receiver;
}
if (QLatin1String(item->metaObject()->className()) == QLatin1String("QQuickMouseArea"))
return item;
return nullptr;
}
#endif
/// @brief Returns the widget's geometry, but always in global space.
inline QRect globalGeometry(QWidgetOrQuick *w)
{
QRect geo = w->geometry();
if (!w->isTopLevel())
geo.moveTopLeft(w->mapToGlobal(QPoint(0, 0)));
return geo;
}
};
#endif

View File

@@ -15,16 +15,14 @@
#include "DragController_p.h"
#include "Config.h"
#include "Qt5Qt6Compat_p.h"
#include "Utils_p.h"
#include <QEvent>
#include <QMouseEvent>
#include <QWidget>
#include <QDebug>
#include <QApplication>
#include <QGuiApplication>
#include <QScreen>
#include <QWindow>
#include <QAbstractButton>
#include <QLineEdit>
#if defined(Q_OS_WIN)
# include <QtGui/private/qhighdpiscaling_p.h>
@@ -35,15 +33,12 @@
# endif
#endif
namespace {
int widgetResizeHandlerMargin = 4; //4 pixel
}
using namespace KDDockWidgets;
bool WidgetResizeHandler::s_disableAllHandlers = false;
WidgetResizeHandler::WidgetResizeHandler(QWidget *target)
WidgetResizeHandler::WidgetResizeHandler(bool filterIsGlobal, QWidgetOrQuick *target)
: QObject(target)
, mFilterIsGlobal(filterIsGlobal)
{
setTarget(target);
}
@@ -52,15 +47,32 @@ WidgetResizeHandler::~WidgetResizeHandler()
{
}
void WidgetResizeHandler::setAllowedResizeSides(CursorPositions sides)
{
mAllowedResizeSides = sides;
}
void WidgetResizeHandler::setResizeGap(int gap)
{
m_resizeGap = gap;
}
int WidgetResizeHandler::widgetResizeHandlerMargin()
{
return 4; // pixels
}
bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *e)
{
if (s_disableAllHandlers || o != mTarget)
if (s_disableAllHandlers)
return false;
auto widget = qobject_cast<QWidget*>(o);
if (!widget || !widget->isTopLevel()) {
auto widget = qobject_cast<QWidgetOrQuick*>(o);
if (!widget)
return false;
if (!mFilterIsGlobal && (!widget->isTopLevel() || o != mTarget))
return false;
}
switch (e->type()) {
case QEvent::MouseButtonPress: {
@@ -68,31 +80,32 @@ bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *e)
break;
auto mouseEvent = static_cast<QMouseEvent *>(e);
auto cursorPos = cursorPosition(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
if (cursorPos == CursorPosition::Undefined)
if (cursorPos == CursorPosition_Undefined)
return false;
const QRect widgetRect = mTarget->rect().marginsAdded(QMargins(widgetResizeHandlerMargin, widgetResizeHandlerMargin, widgetResizeHandlerMargin, widgetResizeHandlerMargin));
const int m = widgetResizeHandlerMargin();
const QRect widgetRect = mTarget->rect().marginsAdded(QMargins(m, m, m, m));
const QPoint cursorPoint = mTarget->mapFromGlobal(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
if (!widgetRect.contains(cursorPoint))
if (!widgetRect.contains(cursorPoint) || mouseEvent->button() != Qt::LeftButton)
return false;
if (mouseEvent->button() == Qt::LeftButton) {
mResizeWidget = true;
}
mResizeWidget = true;
mNewPosition = Qt5Qt6Compat::eventGlobalPos(mouseEvent);
mCursorPos = cursorPos;
return true;
}
case QEvent::MouseButtonRelease: {
if (mTarget->isMaximized())
break;
mResizeWidget = false;
auto mouseEvent = static_cast<QMouseEvent *>(e);
if (mouseEvent->button() == Qt::LeftButton) {
mResizeWidget = false;
mTarget->releaseMouse();
mTarget->releaseKeyboard();
return true;
}
if (mTarget->isMaximized() || !mResizeWidget || mouseEvent->button() != Qt::LeftButton)
break;
mTarget->releaseMouse();
mTarget->releaseKeyboard();
return true;
break;
}
case QEvent::MouseMove: {
@@ -101,10 +114,11 @@ bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *e)
auto mouseEvent = static_cast<QMouseEvent *>(e);
mResizeWidget = mResizeWidget && (mouseEvent->buttons() & Qt::LeftButton);
const bool state = mResizeWidget;
mResizeWidget = ((o == mTarget) && mResizeWidget);
mouseMoveEvent(mouseEvent);
if (!mFilterIsGlobal)
mResizeWidget = ((o == mTarget) && mResizeWidget);
const bool consumed = mouseMoveEvent(mouseEvent);
mResizeWidget = state;
return true;
return consumed;
}
default:
break;
@@ -112,26 +126,34 @@ bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *e)
return false;
}
void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
bool WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
{
const QPoint globalPos = Qt5Qt6Compat::eventGlobalPos(e);
if (!mResizeWidget) {
updateCursor(cursorPosition(globalPos));
return;
const CursorPosition pos = cursorPosition(globalPos);
updateCursor(pos);
return pos != CursorPosition_Undefined;
}
const QRect oldGeometry = mTarget->geometry();
const QRect oldGeometry = KDDockWidgets::globalGeometry(mTarget);
QRect newGeometry = oldGeometry;
QRect parentGeometry;
if (!mTarget->isTopLevel())
parentGeometry = KDDockWidgets::Private::parentGeometry(mTarget);
{
int deltaWidth = 0;
int newWidth = 0;
const int minWidth = mTarget->minimumWidth();
const int maxWidth = mTarget->maximumWidth();
const int maxWidth = Layouting::Widget::widgetMaxSize(mTarget).width();
const int minWidth = Layouting::Widget::widgetMinSize(mTarget).width();
switch (mCursorPos) {
case CursorPosition::TopLeft:
case CursorPosition::Left:
case CursorPosition::BottomLeft: {
case CursorPosition_TopLeft:
case CursorPosition_Left:
case CursorPosition_BottomLeft: {
parentGeometry = parentGeometry.adjusted(0, m_resizeGap, 0, 0);
deltaWidth = oldGeometry.left() - globalPos.x();
newWidth = qBound(minWidth, mTarget->width() + deltaWidth, maxWidth);
deltaWidth = newWidth - mTarget->width();
@@ -142,9 +164,10 @@ void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
break;
}
case CursorPosition::TopRight:
case CursorPosition::Right:
case CursorPosition::BottomRight: {
case CursorPosition_TopRight:
case CursorPosition_Right:
case CursorPosition_BottomRight: {
parentGeometry = parentGeometry.adjusted(0, 0, -m_resizeGap, 0);
deltaWidth = globalPos.x() - newGeometry.right();
newWidth = qBound(minWidth, mTarget->width() + deltaWidth, maxWidth);
deltaWidth = newWidth - mTarget->width();
@@ -159,14 +182,15 @@ void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
}
{
const int maxHeight = mTarget->maximumHeight();
const int minHeight = mTarget->minimumHeight();
const int maxHeight = Layouting::Widget::widgetMaxSize(mTarget).height();
const int minHeight = Layouting::Widget::widgetMinSize(mTarget).height();
int deltaHeight = 0;
int newHeight = 0;
switch (mCursorPos) {
case CursorPosition::TopLeft:
case CursorPosition::Top:
case CursorPosition::TopRight: {
case CursorPosition_TopLeft:
case CursorPosition_Top:
case CursorPosition_TopRight: {
parentGeometry = parentGeometry.adjusted(0, m_resizeGap, 0, 0);
deltaHeight = oldGeometry.top() - globalPos.y();
newHeight = qBound(minHeight, mTarget->height() + deltaHeight, maxHeight);
deltaHeight = newHeight - mTarget->height();
@@ -177,9 +201,10 @@ void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
break;
}
case CursorPosition::BottomLeft:
case CursorPosition::Bottom:
case CursorPosition::BottomRight: {
case CursorPosition_BottomLeft:
case CursorPosition_Bottom:
case CursorPosition_BottomRight: {
parentGeometry = parentGeometry.adjusted(0, 0, 0, -m_resizeGap);
deltaHeight = globalPos.y() - newGeometry.bottom();
newHeight = qBound(minHeight, mTarget->height() + deltaHeight, maxHeight);
deltaHeight = newHeight - mTarget->height();
@@ -193,15 +218,26 @@ void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
}
}
if (newGeometry != mTarget->geometry())
mTarget->setGeometry(newGeometry);
}
if (newGeometry != mTarget->geometry()) {
if (!mTarget->isTopLevel()) {
// Clip to parent's geometry.
newGeometry = newGeometry.intersected(parentGeometry);
// Back to local.
newGeometry.moveTopLeft(mTarget->mapFromGlobal(newGeometry.topLeft()) + mTarget->pos());
}
mTarget->setGeometry(newGeometry);
}
return true;
}
#ifdef Q_OS_WIN
/// Handler to enable Aero-snap
bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByteArray &eventType, void *message, long *result)
bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result)
{
if (eventType != "windows_generic_MSG")
return false;
@@ -250,18 +286,15 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
*result = HTRIGHT;
} else {
const QPoint globalPosQt = QHighDpi::fromNativePixels(QPoint(xPos, yPos), w->windowHandle());
const QRect htCaptionRect = w->dragRect(); // The rect on which we allow for Windows to do Ba native drag
const QRect htCaptionRect = w->dragRect(); // The rect on which we allow for Windows to do a native drag
if (globalPosQt.y() >= htCaptionRect.top() && globalPosQt.y() <= htCaptionRect.bottom() && globalPosQt.x() >= htCaptionRect.left() && globalPosQt.x() <= htCaptionRect.right()) {
QWidget *hoveredWidget = qApp->widgetAt(globalPosQt);
if (!qobject_cast<QAbstractButton*>(hoveredWidget) &&
!qobject_cast<QLineEdit*>(hoveredWidget)) { // User might have a line edit on the toolbar. TODO: Not so elegant fix, we should make the user's tabbar implement some virtual method...
// User clicked on the title bar, let's allow it, so we get Aero-Snap.
*result = HTCAPTION;
if (!KDDockWidgets::inDisallowDragWidget(globalPosQt)) { // Just makes sure the mouse isn't over the close button, we don't allow drag in that case.
*result = HTCAPTION;
}
}
}
w->setLastHitTest(*result);
return *result != 0;
} else if (msg->message == WM_NCLBUTTONDBLCLK) {
if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes)) {
@@ -287,7 +320,7 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
// and patch the size
// According to microsoft docs it only works for the primary screen, but extrapolates for the others
QScreen *screen = QApplication::primaryScreen();
QScreen *screen = QGuiApplication::primaryScreen();
if (!screen || w->windowHandle()->screen() != screen) {
return false;
}
@@ -317,12 +350,16 @@ bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *w, const QByt
#endif
void WidgetResizeHandler::setTarget(QWidget *w)
void WidgetResizeHandler::setTarget(QWidgetOrQuick *w)
{
if (w) {
mTarget = w;
mTarget->setMouseTracking(true);
mTarget->installEventFilter(this);
if (mFilterIsGlobal) {
qApp->installEventFilter(this);
} else {
mTarget->installEventFilter(this);
}
} else {
qWarning() << "Target widget is null!";
}
@@ -330,62 +367,85 @@ void WidgetResizeHandler::setTarget(QWidget *w)
void WidgetResizeHandler::updateCursor(CursorPosition m)
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
//Need for updating cursor when we change child widget
const QObjectList children = mTarget->children();
for (int i = 0, total = children.size(); i < total; ++i) {
if (auto child = qobject_cast<QWidget*>(children.at(i))) {
if (auto child = qobject_cast<WidgetType*>(children.at(i))) {
if (!child->testAttribute(Qt::WA_SetCursor)) {
child->setCursor(Qt::ArrowCursor);
}
}
}
#endif
switch (m) {
case CursorPosition::TopLeft:
case CursorPosition::BottomRight:
mTarget->setCursor(Qt::SizeFDiagCursor);
case CursorPosition_TopLeft:
case CursorPosition_BottomRight:
setMouseCursor(Qt::SizeFDiagCursor);
break;
case CursorPosition::BottomLeft:
case CursorPosition::TopRight:
mTarget->setCursor(Qt::SizeBDiagCursor);
case CursorPosition_BottomLeft:
case CursorPosition_TopRight:
setMouseCursor(Qt::SizeBDiagCursor);
break;
case CursorPosition::Top:
case CursorPosition::Bottom:
mTarget->setCursor(Qt::SizeVerCursor);
case CursorPosition_Top:
case CursorPosition_Bottom:
setMouseCursor(Qt::SizeVerCursor);
break;
case CursorPosition::Left:
case CursorPosition::Right:
mTarget->setCursor(Qt::SizeHorCursor);
case CursorPosition_Left:
case CursorPosition_Right:
setMouseCursor(Qt::SizeHorCursor);
break;
case CursorPosition::Undefined:
mTarget->setCursor(Qt::ArrowCursor);
case CursorPosition_Undefined:
restoreMouseCursor();
break;
case CursorPosition_All:
// Doesn't happen
break;
}
}
void WidgetResizeHandler::setMouseCursor(Qt::CursorShape cursor)
{
if (mFilterIsGlobal)
qApp->setOverrideCursor(cursor);
else
mTarget->setCursor(cursor);
}
void WidgetResizeHandler::restoreMouseCursor()
{
if (mFilterIsGlobal)
qApp->restoreOverrideCursor();
else
mTarget->setCursor(Qt::ArrowCursor);
}
WidgetResizeHandler::CursorPosition WidgetResizeHandler::cursorPosition(QPoint globalPos) const
{
if (!mTarget)
return CursorPosition::Undefined;
return CursorPosition_Undefined;
QPoint pos = mTarget->mapFromGlobal(globalPos);
if (pos.y() <= widgetResizeHandlerMargin && pos.x() <= widgetResizeHandlerMargin) {
return CursorPosition::TopLeft;
} else if (pos.y() >= mTarget->height() - widgetResizeHandlerMargin && pos.x() >= mTarget->width() - widgetResizeHandlerMargin) {
return CursorPosition::BottomRight;
} else if (pos.y() >= mTarget->height() - widgetResizeHandlerMargin && pos.x() <= widgetResizeHandlerMargin) {
return CursorPosition::BottomLeft;
} else if (pos.y() <= widgetResizeHandlerMargin && pos.x() >= mTarget->width() - widgetResizeHandlerMargin) {
return CursorPosition::TopRight;
} else if (pos.y() <= widgetResizeHandlerMargin) {
return CursorPosition::Top;
} else if (pos.y() >= mTarget->height() - widgetResizeHandlerMargin) {
return CursorPosition::Bottom;
} else if (pos.x() <= widgetResizeHandlerMargin) {
return CursorPosition::Left;
} else if ( pos.x() >= mTarget->width() - widgetResizeHandlerMargin) {
return CursorPosition::Right;
} else {
return CursorPosition::Undefined;
}
const int x = pos.x();
const int y = pos.y();
const int margin = widgetResizeHandlerMargin();
int result = CursorPosition_Undefined;
if (qAbs(x) <= margin)
result |= CursorPosition_Left;
else if (qAbs(x - (mTarget->width() - margin)) <= margin)
result |= CursorPosition_Right;
if (qAbs(y) <= margin)
result |= CursorPosition_Top;
else if (qAbs(y - (mTarget->height() - margin)) <= margin)
result |= CursorPosition_Bottom;
// Filter out sides we don't allow
result = result & mAllowedResizeSides;
return static_cast<CursorPosition>(result);
}

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