Skip to content

Commit 6b79bd2

Browse files
committed
Allow dropping windows from the MultitaskingView on the IconGroups
1 parent a3ea3cf commit 6b79bd2

File tree

6 files changed

+147
-5
lines changed

6 files changed

+147
-5
lines changed

src/DBus/WindowDragManager.vala

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* SPDX-License-Identifier: GPL-3.0
3+
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
4+
*/
5+
6+
public class Dock.WindowDragManager : Object {
7+
[DBus (name = "io.elementary.desktop.wm.WindowDragProvider")]
8+
private interface WindowDragProvider : Object {
9+
public signal void enter (uint64 window_id);
10+
public signal void motion (int x, int y);
11+
public signal void leave ();
12+
public signal void dropped ();
13+
}
14+
15+
public Gtk.Window dock_window { get; construct; }
16+
17+
private Window? current_window = null;
18+
private WorkspaceIconGroup? current_icon_group = null;
19+
20+
private WindowDragProvider provider;
21+
22+
public WindowDragManager (Gtk.Window dock_window) {
23+
Object (dock_window: dock_window);
24+
}
25+
26+
construct {
27+
connect_to_dbus.begin ();
28+
}
29+
30+
private async void connect_to_dbus () {
31+
try {
32+
provider = yield Bus.get_proxy (SESSION, "io.elementary.gala", "/io/elementary/gala");
33+
provider.enter.connect (on_enter);
34+
provider.motion.connect (on_motion);
35+
provider.leave.connect (on_leave);
36+
provider.dropped.connect (on_dropped);
37+
} catch (Error e) {
38+
warning ("Failed to connect to WindowDragProvider DBus interface: %s", e.message);
39+
}
40+
}
41+
42+
private void on_enter (uint64 window_id) {
43+
current_window = WindowSystem.get_default ().find_window (window_id);
44+
}
45+
46+
private void on_motion (int x, int y) {
47+
if (current_window == null) {
48+
return;
49+
}
50+
51+
var icon_group = find_icon_group (x, y);
52+
53+
if (icon_group == current_icon_group) {
54+
return;
55+
}
56+
57+
if (current_icon_group != null) {
58+
current_icon_group.window_left ();
59+
}
60+
61+
current_icon_group = icon_group;
62+
63+
if (current_icon_group != null) {
64+
current_icon_group.window_entered (current_window);
65+
}
66+
}
67+
68+
private WorkspaceIconGroup? find_icon_group (int x, int y) {
69+
double root_x, root_y;
70+
dock_window.get_surface_transform (out root_x, out root_y);
71+
72+
var widget = dock_window.pick (x - root_x, y - root_y, DEFAULT);
73+
74+
while (!(widget is WorkspaceIconGroup) && widget != null) {
75+
widget = widget.get_parent ();
76+
}
77+
78+
if (widget is WorkspaceIconGroup) {
79+
return (WorkspaceIconGroup) widget;
80+
}
81+
82+
return null;
83+
}
84+
85+
private void on_leave () {
86+
if (current_icon_group != null) {
87+
current_icon_group.window_left ();
88+
current_icon_group = null;
89+
}
90+
91+
current_window = null;
92+
}
93+
94+
private void on_dropped () {
95+
if (current_icon_group != null && current_window != null &&
96+
current_icon_group.workspace.index != current_window.workspace_index
97+
) {
98+
WindowSystem.get_default ().move_window_to_workspace.begin (
99+
current_window.uid, current_icon_group.workspace.index
100+
);
101+
}
102+
}
103+
}

src/MainWindow.vala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
2727

2828
private Gtk.Box main_box;
2929

30+
private WindowDragManager window_drag_manager;
31+
3032
class construct {
3133
set_css_name ("dock-window");
3234
}
@@ -75,6 +77,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
7577
transparency_settings.changed["use-transparency"].connect (update_transparency);
7678
update_transparency ();
7779
}
80+
81+
window_drag_manager = new WindowDragManager (this);
7882
}
7983

8084
private void update_transparency () {

src/WindowSystem/DesktopIntegration.vala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public interface Dock.DesktopIntegration : GLib.Object {
2727
public abstract async Window[] get_windows () throws GLib.DBusError, GLib.IOError;
2828
public abstract async void show_windows_for (string app_id) throws GLib.DBusError, GLib.IOError;
2929
public abstract async void focus_window (uint64 uid) throws GLib.DBusError, GLib.IOError;
30+
public abstract async void move_window_to_workspace (uint64 window, int workspace) throws GLib.DBusError, GLib.IOError;
3031
public abstract async void activate_workspace (int index) throws GLib.DBusError, GLib.IOError;
3132
public abstract async int get_n_workspaces () throws GLib.DBusError, GLib.IOError;
3233
public abstract async int get_active_workspace () throws GLib.DBusError, GLib.IOError;

src/WindowSystem/WindowSystem.vala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
}
4242
}
4343

44-
private Window? find_window (uint64 uid) {
44+
public Window? find_window (uint64 uid) {
4545
uint index;
4646
if (windows.find_custom (uid, (win, uid) => {
47-
return win.uid == uid;
47+
return win.uid == (uint64) uid;
4848
}, out index)) {
4949
return windows[index];
5050
}
@@ -83,4 +83,12 @@
8383
critical (e.message);
8484
}
8585
}
86+
87+
public async void move_window_to_workspace (uint64 window, int workspace) requires (desktop_integration != null) {
88+
try {
89+
yield desktop_integration.move_window_to_workspace (window, workspace);
90+
} catch (Error e) {
91+
critical ("Failed to move window to workspace: %s", e.message);
92+
}
93+
}
8694
}

src/WorkspaceSystem/IconGroup.vala

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@
66
public class Dock.WorkspaceIconGroup : BaseIconGroup {
77
public Workspace workspace { get; construct; }
88

9+
public GLib.ListStore additional_icons { private get; construct; }
10+
911
public WorkspaceIconGroup (Workspace workspace) {
12+
var additional_icons = new GLib.ListStore (typeof (GLib.Icon));
13+
14+
var workspace_icons = new Gtk.MapListModel (workspace.windows, (window) => {
15+
return ((Window) window).icon;
16+
});
17+
18+
var icon_sources_list_store = new GLib.ListStore (typeof (GLib.ListModel));
19+
icon_sources_list_store.append (additional_icons);
20+
icon_sources_list_store.append (workspace_icons);
21+
22+
var flatten_model = new Gtk.FlattenListModel (icon_sources_list_store);
23+
1024
Object (
1125
workspace: workspace,
12-
icons: new Gtk.MapListModel (workspace.windows, (window) => {
13-
return ((Window) window).icon;
14-
}),
26+
additional_icons: additional_icons,
27+
icons: flatten_model,
1528
group: Group.WORKSPACE
1629
);
1730
}
@@ -36,4 +49,16 @@ public class Dock.WorkspaceIconGroup : BaseIconGroup {
3649
workspace.reorder (ItemManager.get_default ().get_index_for_launcher (this));
3750
}
3851
}
52+
53+
public void window_entered (Window window) {
54+
if (window.workspace_index == workspace.index) {
55+
return;
56+
}
57+
58+
additional_icons.append (window.icon);
59+
}
60+
61+
public void window_left () {
62+
additional_icons.remove_all ();
63+
}
3964
}

src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ sources = [
1717
'DBus' / 'ShellKeyGrabber.vala',
1818
'DBus' / 'SwitcherooControl.vala',
1919
'DBus' / 'Unity.vala',
20+
'DBus' / 'WindowDragManager.vala',
2021
'WindowSystem' / 'DesktopIntegration.vala',
2122
'WindowSystem' / 'Window.vala',
2223
'WindowSystem' / 'WindowSystem.vala',

0 commit comments

Comments
 (0)