Skip to content

Commit dd5183e

Browse files
committed
Allow dropping windows from the MultitaskingView on the IconGroups
1 parent 82c0845 commit dd5183e

File tree

6 files changed

+150
-7
lines changed

6 files changed

+150
-7
lines changed

src/DBus/WindowDragManager.vala

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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 IconGroup? 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 IconGroup? 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 IconGroup) && widget != null) {
75+
widget = widget.get_parent ();
76+
}
77+
78+
if (widget is IconGroup) {
79+
return (IconGroup) 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+
}
104+

src/MainWindow.vala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
2828
private Gtk.Box main_box;
2929
private int height = 0;
3030

31+
private WindowDragManager window_drag_manager;
32+
3133
class construct {
3234
set_css_name ("dock-window");
3335
}
@@ -76,6 +78,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
7678
transparency_settings.changed["use-transparency"].connect (update_transparency);
7779
update_transparency ();
7880
}
81+
82+
window_drag_manager = new WindowDragManager (this);
7983
}
8084

8185
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: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class Dock.IconGroup : BaseItem {
1313

1414
private Gtk.Grid grid;
1515

16+
private Window? hovering_window = null;
17+
1618
class construct {
1719
set_css_name ("icongroup");
1820
}
@@ -56,6 +58,20 @@ public class Dock.IconGroup : BaseItem {
5658
notify["moving"].connect (on_moving_changed);
5759
}
5860

61+
public void window_entered (Window window) {
62+
if (window.workspace_index == workspace.index) {
63+
return;
64+
}
65+
66+
hovering_window = window;
67+
update_icons ();
68+
}
69+
70+
public void window_left () {
71+
hovering_window = null;
72+
update_icons ();
73+
}
74+
5975
private void update_icons () {
6076
unowned Gtk.Widget? child;
6177
while ((child = grid.get_first_child ()) != null) {
@@ -67,21 +83,30 @@ public class Dock.IconGroup : BaseItem {
6783
grid.column_spacing = grid_spacing;
6884

6985
var new_pixel_size = get_pixel_size ();
70-
int i;
71-
for (i = 0; i < int.min (workspace.windows.length, 4); i++) {
86+
int nth_window = 0;
87+
88+
if (hovering_window != null) {
89+
grid.attach (new Gtk.Image.from_gicon (hovering_window.icon) {
90+
pixel_size = new_pixel_size
91+
}, 0, 0);
92+
nth_window++;
93+
}
94+
95+
for (int i = 0; i < int.min (workspace.windows.length, 4 - nth_window); i++) {
7296
var image = new Gtk.Image.from_gicon (workspace.windows[i].icon) {
7397
pixel_size = new_pixel_size
7498
};
7599

76-
grid.attach (image, i % MAX_IN_ROW, i / MAX_IN_ROW, 1, 1);
100+
grid.attach (image, nth_window % MAX_IN_ROW, nth_window / MAX_IN_ROW, 1, 1);
101+
nth_window++;
77102
}
78103

79104
// We always need to attach at least 3 elements for grid to be square and properly aligned
80-
for (; i < 3; i++) {
105+
for (; nth_window < 3; nth_window++) {
81106
var empty_widget = new EmptyWidget ();
82107
empty_widget.set_size_request (new_pixel_size, new_pixel_size);
83108

84-
grid.attach (empty_widget, i % MAX_IN_ROW, i / MAX_IN_ROW, 1, 1);
109+
grid.attach (empty_widget, nth_window % MAX_IN_ROW, nth_window / MAX_IN_ROW, 1, 1);
85110
}
86111
}
87112

src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ sources = [
1212
'DBus' / 'ShellKeyGrabber.vala',
1313
'DBus' / 'SwitcherooControl.vala',
1414
'DBus' / 'Unity.vala',
15+
'DBus' / 'WindowDragManager.vala',
1516
'WindowSystem' / 'DesktopIntegration.vala',
1617
'WindowSystem' / 'Window.vala',
1718
'WindowSystem' / 'WindowSystem.vala',

0 commit comments

Comments
 (0)