From e780b209e5b4d182742b86d6b9dc83e7c97e78f3 Mon Sep 17 00:00:00 2001 From: flodavid Date: Mon, 9 Mar 2026 15:04:00 +0100 Subject: [PATCH] Separate launcher and worskpace docks --- data/Application.css | 4 ++ src/HorizontalMargin.vala | 30 +++++++++ src/ItemManager.vala | 55 +---------------- src/MainWindow.vala | 62 +++++++++++++------ src/WorkspaceManager.vala | 124 ++++++++++++++++++++++++++++++++++++++ src/meson.build | 2 + 6 files changed, 207 insertions(+), 70 deletions(-) create mode 100644 src/HorizontalMargin.vala create mode 100644 src/WorkspaceManager.vala diff --git a/data/Application.css b/data/Application.css index bfd5c8a1..3b6fba5c 100644 --- a/data/Application.css +++ b/data/Application.css @@ -40,6 +40,10 @@ bottom-margin { min-height: 9px; } +horizontal-margin { + min-width: 9px; +} + launcher { padding: 6px; padding-bottom: 0px; diff --git a/src/HorizontalMargin.vala b/src/HorizontalMargin.vala new file mode 100644 index 00000000..c94e2358 --- /dev/null +++ b/src/HorizontalMargin.vala @@ -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 instances = new GLib.List (); + + 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; + } +} diff --git a/src/ItemManager.vala b/src/ItemManager.vala index ee5a25a6..47896966 100644 --- a/src/ItemManager.vala +++ b/src/ItemManager.vala @@ -16,12 +16,6 @@ private Adw.TimedAnimation resize_animation; private GLib.GenericArray launchers; // Only used to keep track of launcher indices private BackgroundItem background_item; - private GLib.GenericArray 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"); @@ -33,15 +27,8 @@ background_item = new BackgroundItem (); background_item.apps_appeared.connect (add_item); - icon_groups = new GLib.GenericArray (); - -#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; @@ -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 }); } @@ -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) { @@ -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; @@ -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); @@ -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; @@ -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"); diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 2d05ae08..0c7b9c8f 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -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"); @@ -85,7 +109,6 @@ public class Dock.MainWindow : Gtk.ApplicationWindow { remove_css_class ("reduce-transparency"); } else { add_css_class ("reduce-transparency"); - } } @@ -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); }); @@ -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 })); @@ -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 (); } @@ -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) { diff --git a/src/WorkspaceManager.vala b/src/WorkspaceManager.vala new file mode 100644 index 00000000..df293553 --- /dev/null +++ b/src/WorkspaceManager.vala @@ -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 instance; + public static unowned WorkspaceManager get_default () { + return instance.once (() => { return new WorkspaceManager (); }); + } + + private Adw.TimedAnimation resize_animation; + private GLib.GenericArray 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 (); + 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; + } +} diff --git a/src/meson.build b/src/meson.build index b6a8f052..bd5a32c7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,8 +3,10 @@ sources = [ 'BaseIconGroup.vala', 'BaseItem.vala', 'BottomMargin.vala', + 'HorizontalMargin.vala', 'ContainerItem.vala', 'ItemManager.vala', + 'WorkspaceManager.vala', 'MainWindow.vala', 'RenderNodeWalker.vala', 'AppSystem' / 'App.vala',