Skip to content

Commit 6dfcc67

Browse files
lenemterdanirabbitalainm23
authored
Workspace switcher (#361)
Co-authored-by: Danielle Foré <[email protected]> Co-authored-by: Alain <[email protected]>
1 parent 50f5379 commit 6dfcc67

File tree

11 files changed

+425
-12
lines changed

11 files changed

+425
-12
lines changed

data/Application.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,38 @@ launcher progressbar progress {
6060
min-width: 0;
6161
}
6262

63+
icongroup {
64+
padding: 6px;
65+
padding-bottom: 0;
66+
}
67+
68+
icongroup box {
69+
background: alpha(@base_color, 0.4);
70+
box-shadow:
71+
inset 0 -1px 0 0 alpha(@highlight_color, 0.2),
72+
inset 0 1px 0 0 alpha(@highlight_color, 0.3),
73+
inset 1px 0 0 0 alpha(@highlight_color, 0.07),
74+
inset -1px 0 0 0 alpha(@highlight_color, 0.07),
75+
0 0 0 1px alpha(@borders, 0.3),
76+
0 1px 1px alpha(@borders, 0.2),
77+
0 1px 4px alpha(@borders, 0.4);
78+
border-radius: 6px;
79+
border-spacing: 3px;
80+
}
81+
82+
.reduce-transparency icongroup box {
83+
background: @base_color;
84+
}
85+
86+
icongroup .add-image {
87+
color: alpha(@selected_fg_color, 0.75);
88+
-gtk-icon-shadow: 0 1px 0 alpha(@highlight_color, 0.2);
89+
}
90+
91+
.reduce-transparency .add-image {
92+
color: @selected_fg_color;
93+
}
94+
6395
.running-indicator {
6496
color: @selected_fg_color;
6597
-gtk-icon-size: 9px;

po/POTFILES

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ src/DBus/Unity.vala
1313
src/WindowSystem/DesktopIntegration.vala
1414
src/WindowSystem/Window.vala
1515
src/WindowSystem/WindowSystem.vala
16+
src/WorkspaceSystem/DynamicWorkspaceItem.vala
17+
src/WorkspaceSystem/IconGroup.vala
18+
src/WorkspaceSystem/Workspace.vala
19+
src/WorkspaceSystem/WorkspaceSystem.vala

src/ItemManager.vala

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,22 @@
1515

1616
private Adw.TimedAnimation resize_animation;
1717
private List<Launcher> launchers; // Only used to keep track of launcher indices
18+
private List<IconGroup> icon_groups; // Only used to keep track of icon group indices
19+
private DynamicWorkspaceIcon dynamic_workspace_item;
1820

1921
static construct {
2022
settings = new Settings ("io.elementary.dock");
2123
}
2224

2325
construct {
2426
launchers = new List<Launcher> ();
27+
icon_groups = new List<IconGroup> ();
28+
29+
// Idle is used here to because DynamicWorkspaceIcon depends on ItemManager
30+
Idle.add_once (() => {
31+
dynamic_workspace_item = new DynamicWorkspaceIcon ();
32+
add_item (dynamic_workspace_item);
33+
});
2534

2635
overflow = VISIBLE;
2736

@@ -110,25 +119,40 @@
110119
add_item (launcher);
111120
});
112121

113-
map.connect (AppSystem.get_default ().load);
122+
WorkspaceSystem.get_default ().workspace_added.connect ((workspace) => {
123+
add_item (new IconGroup (workspace));
124+
});
125+
126+
map.connect (() => {
127+
AppSystem.get_default ().load.begin ();
128+
WorkspaceSystem.get_default ().load.begin ();
129+
});
114130
}
115131

116132
private void reposition_items () {
117-
var launcher_size = get_launcher_size ();
118-
119133
int index = 0;
120134
foreach (var launcher in launchers) {
121-
var position = index * launcher_size;
135+
position_item (launcher, ref index);
136+
}
122137

123-
if (launcher.parent != this) {
124-
put (launcher, position, 0);
125-
launcher.current_pos = position;
126-
} else {
127-
launcher.animate_move (position);
128-
}
138+
foreach (var icon_group in icon_groups) {
139+
position_item (icon_group, ref index);
140+
}
141+
142+
position_item (dynamic_workspace_item, ref index);
143+
}
144+
145+
private void position_item (BaseItem item, ref int index) {
146+
var position = get_launcher_size () * index;
129147

130-
index++;
148+
if (item.parent != this) {
149+
put (item, position, 0);
150+
item.current_pos = position;
151+
} else {
152+
item.animate_move (position);
131153
}
154+
155+
index++;
132156
}
133157

134158
private void add_launcher_via_dnd (Launcher launcher, int index) {
@@ -144,6 +168,8 @@
144168

145169
if (item is Launcher) {
146170
launchers.append ((Launcher) item);
171+
} else if (item is IconGroup) {
172+
icon_groups.append ((IconGroup) item);
147173
}
148174

149175
resize_animation.easing = EASE_OUT_BACK;
@@ -163,6 +189,8 @@
163189
private void remove_item (BaseItem item) {
164190
if (item is Launcher) {
165191
launchers.remove ((Launcher) item);
192+
} else if (item is IconGroup) {
193+
icon_groups.remove ((IconGroup) item);
166194
}
167195

168196
item.set_revealed (false);

src/WindowSystem/DesktopIntegration.vala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ public interface Dock.DesktopIntegration : GLib.Object {
2020

2121
public signal void running_applications_changed ();
2222
public signal void windows_changed ();
23+
public signal void active_workspace_changed ();
24+
public signal void workspace_removed (int index);
2325

2426
public abstract async RunningApplication[] get_running_applications () throws GLib.DBusError, GLib.IOError;
2527
public abstract async Window[] get_windows () throws GLib.DBusError, GLib.IOError;
2628
public abstract async void show_windows_for (string app_id) throws GLib.DBusError, GLib.IOError;
2729
public abstract async void focus_window (uint64 uid) throws GLib.DBusError, GLib.IOError;
30+
public abstract async void activate_workspace (int index) throws GLib.DBusError, GLib.IOError;
31+
public abstract async int get_n_workspaces () throws GLib.DBusError, GLib.IOError;
32+
public abstract async int get_active_workspace () throws GLib.DBusError, GLib.IOError;
2833
}

src/WindowSystem/Window.vala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ public class Dock.Window : GLib.Object {
99

1010
public string app_id { get; private set; default = ""; }
1111
public bool has_focus { get; private set; default = false; }
12+
public int workspace_index { get; private set; default = 0; }
1213
public bool on_active_workspace { get; private set; default = false; }
1314

15+
public GLib.Icon icon { get; private set; default = new GLib.ThemedIcon ("application-default-icon"); }
16+
1417
public Window (uint64 uid) {
1518
Object (uid: uid);
1619
}
@@ -24,8 +27,20 @@ public class Dock.Window : GLib.Object {
2427
has_focus = (bool) properties["has-focus"];
2528
}
2629

30+
if ("workspace-index" in properties) {
31+
workspace_index = (int) properties["workspace-index"];
32+
}
33+
2734
if ("on-active-workspace" in properties) {
2835
on_active_workspace = (bool) properties["on-active-workspace"];
2936
}
37+
38+
var app_info = new GLib.DesktopAppInfo (app_id);
39+
if (app_info != null) {
40+
var icon = app_info.get_icon ();
41+
if (icon != null && Gtk.IconTheme.get_for_display (Gdk.Display.get_default ()).has_gicon (icon)) {
42+
this.icon = icon;
43+
}
44+
}
3045
}
3146
}

src/WindowSystem/WindowSystem.vala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
return instance.once (() => { return new WindowSystem (); });
1010
}
1111

12+
public signal void workspace_removed (int index);
13+
1214
public DesktopIntegration? desktop_integration { get; private set; }
1315
public Gee.List<Window> windows { get; private owned set; }
16+
public int active_workspace { get; private set; default = 0; }
1417

1518
private WindowSystem () {}
1619

@@ -28,8 +31,11 @@
2831
);
2932

3033
yield sync_windows ();
34+
yield sync_active_workspace ();
3135

3236
desktop_integration.windows_changed.connect (sync_windows);
37+
desktop_integration.active_workspace_changed.connect (sync_active_workspace);
38+
desktop_integration.workspace_removed.connect ((index) => workspace_removed (index));
3339
} catch (Error e) {
3440
critical ("Failed to get desktop integration: %s", e.message);
3541
}
@@ -64,4 +70,12 @@
6470

6571
windows = new_windows;
6672
}
73+
74+
private async void sync_active_workspace () requires (desktop_integration != null) {
75+
try {
76+
active_workspace = yield desktop_integration.get_active_workspace ();
77+
} catch (Error e) {
78+
critical (e.message);
79+
}
80+
}
6781
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* SPDX-License-Identifier: GPL-3.0
3+
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
4+
*/
5+
6+
public class Dock.DynamicWorkspaceIcon : BaseItem {
7+
class construct {
8+
set_css_name ("icongroup");
9+
}
10+
11+
public DynamicWorkspaceIcon () {
12+
Object ();
13+
}
14+
15+
construct {
16+
var add_image = new Gtk.Image.from_icon_name ("list-add-symbolic") {
17+
hexpand = true,
18+
vexpand = true
19+
};
20+
add_image.add_css_class ("add-image");
21+
22+
// Gtk.Box is used here to keep css nodes consistent with IconGroup
23+
var box = new Gtk.Box (VERTICAL, 0);
24+
box.append (add_image);
25+
26+
overlay.child = box;
27+
28+
WorkspaceSystem.get_default ().workspace_added.connect (update_running_indicator_visibility);
29+
WorkspaceSystem.get_default ().workspace_removed.connect (update_running_indicator_visibility);
30+
WindowSystem.get_default ().notify["active-workspace"].connect (update_running_indicator_visibility);
31+
32+
dock_settings.bind ("icon-size", box, "width-request", DEFAULT);
33+
dock_settings.bind ("icon-size", box, "height-request", DEFAULT);
34+
35+
dock_settings.bind_with_mapping (
36+
"icon-size", add_image, "pixel_size", DEFAULT | GET,
37+
(value, variant, user_data) => {
38+
var icon_size = variant.get_int32 ();
39+
value.set_int (icon_size / 2);
40+
return true;
41+
},
42+
(value, expected_type, user_data) => {
43+
return new Variant.maybe (null, null);
44+
},
45+
null, null
46+
);
47+
48+
gesture_click.button = Gdk.BUTTON_PRIMARY;
49+
gesture_click.released.connect (switch_to_new_workspace);
50+
}
51+
52+
private void update_running_indicator_visibility () {
53+
unowned var workspace_system = WorkspaceSystem.get_default ();
54+
unowned var window_system = WindowSystem.get_default ();
55+
running_revealer.reveal_child = workspace_system.workspaces.size == window_system.active_workspace;
56+
}
57+
58+
private async void switch_to_new_workspace () {
59+
var n_workspaces = WorkspaceSystem.get_default ().workspaces.size;
60+
61+
try {
62+
yield WindowSystem.get_default ().desktop_integration.activate_workspace (n_workspaces);
63+
} catch (Error e) {
64+
warning ("Couldn't switch to new workspace: %s", e.message);
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)