Dragging Docks and Tugging Toolbars on Wayland
Most of our desktop applications have a toolbar, sometimes they
even have multiple toolbars next to or stacked on top of each other.
More complex desktop applications such as Krita, Kdenlive or LabPlot often
consist of multiple sub-windows, docks, tabbed views, etc.
Docks and toolbars can be undocked, moved around and arranged freely and when dragged over a part
of a window snap back into the window. This allows the user to customize their
work environment to their liking and needs.
This worked fine on X because it lets you do anything, this post explores the situation
on Wayland.
The current situation
Lets start with a more familiar example, tabs in applications such as web browsers can often be dragged between different windows of the browser and when dropped somewhere else they become their own window. Dragging a tab out of Chrome creates a new window that follows the cursor and it is seamlessly integrated back into other Chrome windows when hovering over their tab bar. When doing this in a Wayland session it tried to do the same, starting a drag operation and creating a new window, when the cursor goes back to the bar the window is reincorporated. Except that clients can’t position their own windows on Wayland leading to sub-par UX. So it creates the window which the compositor places somewhere according to its policies. This window does not move around with the cursor but vanishes when the cursor enters the tabbar of another browser window. This feels weird and broken.
Qt exhibits similar behavior to Chrome when trying to drag around toolbars in a Wayland session, they detach and appear in the middle of application but the app itself reacts as if the toolbar is being dragged around and you could
reattach it in some part of the window. However since they don’t have any window decorations,
once detached they can only be moved around by compositor specific means like pressing Alt
and dragging when using KWin. Dragging the cursor on the displayed move handle
starts the “app thinks the window moves but it isn’t” state again and it may be possible to drag it back to its window. ‘May’ because this feels even weirder than the initial action since the app
can’t know on Wayland where the toolbar window was in relation to the other window . Detaching a dock results in the same experience, except that once it is
detached it is actually impossible to dock it again. Floating docks receive a system titlebar
so at least can be moved around more easily but contain no way to start the docking process
again. This is also weird and broken.
This problem is one of the last remaining blockers for some of our more complex applications in KDE for switching to Wayland by default. While some workarounds (such as the one outlined above) exist, a proper solution is needed.
The solution
Facing this shortcoming a new extended drag protocol was proposed and implemented by Chrome developers in 2020 but unfortunately the effort stalled. My initial impression was that it provided a good starting point but maybe sometimes a bit complicated. Nevertheless I started to prototype an implementation in KWin and although not even Chrome implemented the more involved parts of the protocol I had working tab dragging not soon after. Encouraged by this I implemented the minimal client parts in Qt and could successfully drag toolbars and dock widgets around. This convinced me that a more focused protocol would be sufficient cover the different use cases.
A bit of trimming, renaming and shuffling things around resulted in the xdg-toplevel-drag
protcotol.
The idea behind it is that the application can attach a window to a drag operation which then the compositor moves around
with the cursor. The advantage of using the existing drag mechanism is that the window under the cursor is
informed of the cursor position while a normal window move does not provide any information to it.
The application can then also use the normal drag and drop negotiation mechanisms to infer the state
and choose an appropriate action. For example the drag being accepted means it is over
an area that will incorporate the window. It’s up to the client to take immediate action or not,
Chrome will move a tab to a window the moment it reaches its tabbar while Qt just shows an indicator
and waits for the drop to perform any action. When a drop happens but the drag is not accepted the cursor
was over some other area and a likely (non-)action of a client would be to keep the window that
was being dragged around alive.
The above shows the mentioned protocol in action inside Qt and KWin. I also went ahead and added support for the newly proposed protocol to Chrome since I iterated on their original work. But it also shows that the same protocol also works for its slightly different use case of attaching and detaching tabs during the ongoing drag. Below you can see Chromium using this new protocol inside KWin.
The way forward
This work addresses one of the main blockers for our Wayland session that we want to solve in time for the initial Plasma 6 release.
Due to timeframes we know that Plasma 6 is likely to
require Qt 6.6. However the feature freeze for Qt 6.6 was already in June and the protocol was not accepted at that time. To ensure the functionality is available for our deadlines the Qt implementation that is shown in this blog post was merged under a different name (changing thexdg
prefix toqt
).
The intention here is to change it back once as soon as possible when
the upstream procotol is accepted. So the applications relying on it work perfectly not only on Plasma but also on all others desktops.
Discuss this post on KDE Discuss.