Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions data/Application.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ bottom-margin {
min-height: 9px;
}

horizontal-margin {
min-width: 9px;
}

launcher {
padding: 6px;
padding-bottom: 0px;
Expand Down
30 changes: 30 additions & 0 deletions src/HorizontalMargin.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

public class HorizontalMargin : Gtk.Widget {
private static GLib.List<unowned HorizontalMargin> instances = new GLib.List<unowned HorizontalMargin> ();

class construct {
set_css_name ("horizontal-margin");
}

construct {
instances.append (this);
}

~HorizontalMargin () {
instances.remove (this);
}

public new static int get_size () {
foreach (var instance in instances) {
if (instance.get_realized ()) {
return instance.get_width ();
}
}

return 0;
}
}
55 changes: 2 additions & 53 deletions src/ItemManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
private Adw.TimedAnimation resize_animation;
private GLib.GenericArray<Launcher> launchers; // Only used to keep track of launcher indices
private BackgroundItem background_item;
private GLib.GenericArray<WorkspaceIconGroup> icon_groups; // Only used to keep track of icon group indices
private DynamicWorkspaceIcon dynamic_workspace_item;

#if WORKSPACE_SWITCHER
private Gtk.Separator separator;
#endif

static construct {
settings = new Settings ("io.elementary.dock");
Expand All @@ -33,15 +27,8 @@
background_item = new BackgroundItem ();
background_item.apps_appeared.connect (add_item);

icon_groups = new GLib.GenericArray<WorkspaceIconGroup> ();

#if WORKSPACE_SWITCHER
dynamic_workspace_item = new DynamicWorkspaceIcon ();

separator = new Gtk.Separator (VERTICAL);
settings.bind ("icon-size", separator, "height-request", GET);
put (separator, 0, 0);
#endif
// append (new ItemGroup (launchers));
// append (background_item);

overflow = VISIBLE;

Expand Down Expand Up @@ -160,18 +147,9 @@
add_item (launcher);
});

#if WORKSPACE_SWITCHER
WorkspaceSystem.get_default ().workspace_added.connect ((workspace) => {
add_item (new WorkspaceIconGroup (workspace));
});
#endif

map.connect (() => {
AppSystem.get_default ().load.begin ();
background_item.load ();
#if WORKSPACE_SWITCHER
WorkspaceSystem.get_default ().load.begin ();
#endif
});
}

Expand All @@ -184,19 +162,6 @@
if (background_item.has_apps) {
position_item (background_item, ref index);
}

#if WORKSPACE_SWITCHER
var separator_y = (get_launcher_size () - separator.height_request) / 2;
move (separator, index * get_launcher_size () - 1, separator_y);
#endif

foreach (var icon_group in icon_groups) {
position_item (icon_group, ref index);
}

#if WORKSPACE_SWITCHER
position_item (dynamic_workspace_item, ref index);
#endif
}

private void position_item (BaseItem item, ref int index) {
Expand Down Expand Up @@ -227,8 +192,6 @@
if (item is Launcher) {
launchers.add ((Launcher) item);
sync_pinned ();
} else if (item is WorkspaceIconGroup) {
icon_groups.add ((WorkspaceIconGroup) item);
}

ulong reveal_cb = 0;
Expand All @@ -248,8 +211,6 @@
private void remove_item (BaseItem item) {
if (item is Launcher) {
launchers.remove ((Launcher) item);
} else if (item is WorkspaceIconGroup) {
icon_groups.remove ((WorkspaceIconGroup) item);
}

item.removed.disconnect (remove_item);
Expand Down Expand Up @@ -279,9 +240,6 @@
double offset = 0;
if (source is Launcher) {
list = launchers;
} else if (source is WorkspaceIconGroup) {
list = icon_groups;
offset = (launchers.length + (background_item.has_apps ? 1 : 0)) * get_launcher_size (); // +1 for the background item
} else {
warning ("Tried to move neither launcher nor icon group");
return;
Expand Down Expand Up @@ -317,15 +275,6 @@
}

return 0;
} else if (item is WorkspaceIconGroup) {
uint index;
if (icon_groups.find ((WorkspaceIconGroup) item, out index)) {
return (int) index;
}

return 0;
} else if (item == dynamic_workspace_item) { //treat dynamic workspace icon as last icon group
return (int) icon_groups.length;
}

warning ("Tried to get index of neither launcher nor icon group");
Expand Down
62 changes: 45 additions & 17 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,47 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
resizable = false;
titlebar = new Gtk.Label ("") { visible = false };

var dock_box = new Gtk.Box (VERTICAL, 0);
dock_box.append (new Container ());
dock_box.append (new BottomMargin ());
/* Launcher */

var launcher_container = new Gtk.Box (VERTICAL, 0);
launcher_container.append (new Container ());
launcher_container.append (new BottomMargin ());

// // Don't clip launchers to dock background https://github.com/elementary/dock/issues/275
var launcher_overlay = new Gtk.Overlay () {
child = launcher_container
};
unowned var launcher_manager = ItemManager.get_default ();
launcher_overlay.add_overlay (launcher_manager);

var launcher_size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH);
launcher_size_group.add_widget (launcher_container);
launcher_size_group.add_widget (launcher_manager);

/* Workspace */

var workspace_manager = WorkspaceManager.get_default ();
var workspace_container = new Gtk.Box (VERTICAL, 0);
workspace_container.append (new Container ());
workspace_container.append (new BottomMargin ());

// Don't clip launchers to dock background https://github.com/elementary/dock/issues/275
var overlay = new Gtk.Overlay () {
child = dock_box
var workspace_overlay = new Gtk.Overlay () {
child = workspace_container
};
overlay.add_overlay (launcher_manager);
workspace_overlay.add_overlay (workspace_manager);

var size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH);
size_group.add_widget (dock_box);
size_group.add_widget (launcher_manager);
var workspace_size_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH);
workspace_size_group.add_widget (workspace_container);
workspace_size_group.add_widget (workspace_manager);

child = overlay;
/* Full dock */

var docks_box = new Gtk.Box (HORIZONTAL, 0);
docks_box.append (launcher_overlay);
docks_box.append (new HorizontalMargin ());
docks_box.append (workspace_overlay);

child = docks_box;

remove_css_class ("background");

Expand Down Expand Up @@ -85,7 +109,6 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
remove_css_class ("reduce-transparency");
} else {
add_css_class ("reduce-transparency");

}
}

Expand Down Expand Up @@ -113,7 +136,9 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
// manually set shadow width since the additional margin we add to avoid icons clipping when
// bouncing isn't added by default and instead counts to the frame
var item_manager_width = ItemManager.get_default ().get_width ();
var shadow_size = (surface.width - item_manager_width) / 2;
var hztl_margin_width = HorizontalMargin.get_size ();
var workspace_manager_width = WorkspaceManager.get_default ().get_width ();
var shadow_size = (surface.width - item_manager_width - hztl_margin_width - workspace_manager_width) / 2;
var top_margin = TOP_MARGIN + shadow_size - 1;
size.set_shadow_width (shadow_size, shadow_size, top_margin, shadow_size);
});
Expand All @@ -122,12 +147,14 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
// manually set input region since container's shadow are is the content of the window
// and it still gets window events
var item_manager_width = ItemManager.get_default ().get_width ();
var shadow_size = (width - item_manager_width) / 2;
var hztl_margin_width = HorizontalMargin.get_size ();
var workspace_manager_width = WorkspaceManager.get_default ().get_width ();
var shadow_size = (width - item_manager_width - hztl_margin_width - workspace_manager_width) / 2;
var top_margin = TOP_MARGIN + shadow_size;
surface.set_input_region (new Cairo.Region.rectangle ({
shadow_size,
top_margin,
item_manager_width,
item_manager_width + workspace_manager_width,
height - top_margin
}));

Expand All @@ -148,6 +175,7 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {

if (panel != null) {
panel.add_blur (0, 0, 0, BottomMargin.get_size (), border_radius);

} else {
update_panel_x11 ();
}
Expand All @@ -159,8 +187,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
unowned var wl_display = ((Gdk.Wayland.Display) display).get_wl_display ();
var wl_registry = wl_display.get_registry ();
wl_registry.add_listener (
registry_listener,
this
registry_listener,
this
);

if (wl_display.roundtrip () < 0) {
Expand Down
124 changes: 124 additions & 0 deletions src/WorkspaceManager.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

public class Dock.WorkspaceManager : Gtk.Fixed {
private static Settings settings;

private static GLib.Once<WorkspaceManager> instance;
public static unowned WorkspaceManager get_default () {
return instance.once (() => { return new WorkspaceManager (); });
}

private Adw.TimedAnimation resize_animation;
private GLib.GenericArray<WorkspaceIconGroup> icon_groups; // Only used to keep track of icon group indices
private DynamicWorkspaceIcon dynamic_workspace_item;

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

construct {
icon_groups = new GLib.GenericArray<WorkspaceIconGroup> ();
dynamic_workspace_item = new DynamicWorkspaceIcon ();

overflow = VISIBLE;

resize_animation = new Adw.TimedAnimation (
this, 0, 0, 0,
new Adw.CallbackAnimationTarget ((val) => {
width_request = (int) val;
})
);

resize_animation.done.connect (() => width_request = -1); //Reset otherwise we stay to big when the launcher icon size changes

settings.changed["icon-size"].connect (reposition_items);

WorkspaceSystem.get_default ().workspace_added.connect ((workspace) => {
add_item (new WorkspaceIconGroup (workspace));
});

map.connect (() => {
WorkspaceSystem.get_default ().load.begin ();
});
}

private void reposition_items () {
int index = 0;

foreach (var icon_group in icon_groups) {
position_item (icon_group, ref index);
}

position_item (dynamic_workspace_item, ref index);
}

private void position_item (BaseItem item, ref int index) {
var position = get_group_size () * index;

if (item.parent != this) {
put (item, position, 0);
item.current_pos = position;
} else {
item.animate_move (position);
}

index++;
}

private void add_item (BaseItem item) {
item.removed.connect (remove_item);

if (item is WorkspaceIconGroup) {
icon_groups.add ((WorkspaceIconGroup) item);
} else if (item is DynamicWorkspaceIcon) {
icon_groups.add ((WorkspaceIconGroup) item);
}

ulong reveal_cb = 0;
reveal_cb = resize_animation.done.connect (() => {
resize_animation.disconnect (reveal_cb);
reposition_items ();
item.set_revealed (true);
});

resize_animation.easing = EASE_OUT_BACK;
resize_animation.duration = Granite.TRANSITION_DURATION_OPEN;
resize_animation.value_from = get_width ();
resize_animation.value_to = icon_groups.length * get_group_size ();
resize_animation.play ();
}

private void remove_item (BaseItem item) {
if (item is WorkspaceIconGroup) {
icon_groups.remove ((WorkspaceIconGroup) item);
}

item.removed.disconnect (remove_item);
item.revealed_done.connect (remove_finish);
item.set_revealed (false);
}

private void remove_finish (BaseItem item) {
// Temporarily set the width request to avoid flicker until the animation calls the callback for the first time
width_request = get_width ();

remove (item);
reposition_items ();

resize_animation.easing = EASE_IN_OUT_QUAD;
resize_animation.duration = Granite.TRANSITION_DURATION_CLOSE;
resize_animation.value_from = get_width ();
resize_animation.value_to = icon_groups.length * get_group_size ();
resize_animation.play ();

item.revealed_done.disconnect (remove_finish);
item.cleanup ();
}

public static int get_group_size () {
return settings.get_int ("icon-size") + Launcher.PADDING * 2;
}
}
Loading