From 02617cc498b4549afe5e9826d5b34dcf82364128 Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 16 Feb 2025 18:35:39 +0300 Subject: [PATCH] Introduce BaseItem (#373) --- po/POTFILES | 1 + src/AppSystem/Launcher.vala | 133 ++------------------------------- src/BaseItem.vala | 142 ++++++++++++++++++++++++++++++++++++ src/ItemManager.vala | 58 ++++++++------- src/meson.build | 1 + 5 files changed, 184 insertions(+), 151 deletions(-) create mode 100644 src/BaseItem.vala diff --git a/po/POTFILES b/po/POTFILES index 66922120..10fbdaf0 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -1,4 +1,5 @@ src/Application.vala +src/BaseItem.vala src/ItemManager.vala src/MainWindow.vala src/AppSystem/App.vala diff --git a/src/AppSystem/Launcher.vala b/src/AppSystem/Launcher.vala index 29194c7d..804724e0 100644 --- a/src/AppSystem/Launcher.vala +++ b/src/AppSystem/Launcher.vala @@ -1,25 +1,19 @@ /* * SPDX-License-Identifier: GPL-3.0 - * SPDX-FileCopyrightText: 2022 elementary, Inc. (https://elementary.io) + * SPDX-FileCopyrightText: 2022-2025 elementary, Inc. (https://elementary.io) */ -public class Dock.Launcher : Gtk.Box { +public class Dock.Launcher : BaseItem { private const int DND_TIMEOUT = 1000; - private static Settings settings; private static Settings? notify_settings; static construct { - settings = new Settings ("io.elementary.dock"); - if (SettingsSchemaSource.get_default ().lookup ("io.elementary.notifications", true) != null) { notify_settings = new Settings ("io.elementary.notifications"); } } - public signal void removed (); - public signal void revealed_done (); - // Matches icon size and padding in Launcher.css public const int ICON_SIZE = 48; public const int PADDING = 6; @@ -31,8 +25,6 @@ public class Dock.Launcher : Gtk.Box { public App app { get; construct; } - public double current_pos { get; set; } - private bool _moving = false; public bool moving { get { @@ -57,20 +49,12 @@ public class Dock.Launcher : Gtk.Box { private Gtk.Image image; private Gtk.Revealer progress_revealer; private Gtk.Revealer badge_revealer; - private Gtk.Revealer running_revealer; private Adw.TimedAnimation bounce_up; private Adw.TimedAnimation bounce_down; - private Adw.TimedAnimation timed_animation; - - private Gtk.GestureClick gesture_click; - private Gtk.Overlay overlay; private Gtk.PopoverMenu popover; private Binding current_count_binding; - private Adw.TimedAnimation? fade; - private Adw.TimedAnimation? reveal; - private int drag_offset_x = 0; private int drag_offset_y = 0; @@ -119,32 +103,12 @@ public class Dock.Launcher : Gtk.Box { transition_type = CROSSFADE }; - var running_indicator = new Gtk.Image.from_icon_name ("pager-checked-symbolic"); - running_indicator.add_css_class ("running-indicator"); - - running_revealer = new Gtk.Revealer () { - can_target = false, - child = running_indicator, - overflow = VISIBLE, - transition_type = CROSSFADE, - valign = END - }; - - overlay = new Gtk.Overlay () { - child = image - }; + overlay.child = image; overlay.add_overlay (badge_revealer); overlay.add_overlay (progress_revealer); - // Needed to work around DnD bug where it - // would stop working once the button got clicked - append (overlay); - append (running_revealer); - orientation = VERTICAL; tooltip_text = app.app_info.get_display_name (); - var launcher_manager = ItemManager.get_default (); - insert_action_group (ACTION_GROUP_PREFIX, app.action_group); // We have to destroy the progressbar when it is not needed otherwise it will @@ -194,21 +158,6 @@ public class Dock.Launcher : Gtk.Box { }; bounce_up.done.connect (bounce_down.play); - var animation_target = new Adw.CallbackAnimationTarget ((val) => { - launcher_manager.move (this, val, 0); - current_pos = val; - }); - - timed_animation = new Adw.TimedAnimation ( - this, - 0, - 0, - 200, - animation_target - ) { - easing = EASE_IN_OUT_QUAD - }; - var drag_source = new Gtk.DragSource () { actions = MOVE }; @@ -228,10 +177,7 @@ public class Dock.Launcher : Gtk.Box { add_controller (drop_target); drop_target.enter.connect (on_drop_enter); - gesture_click = new Gtk.GestureClick () { - button = 0 - }; - add_controller (gesture_click); + gesture_click.button = 0; gesture_click.released.connect (on_click_released); var scroll_controller = new Gtk.EventControllerScroll (VERTICAL); @@ -241,7 +187,7 @@ public class Dock.Launcher : Gtk.Box { return Gdk.EVENT_STOP; }); - settings.bind ("icon-size", image, "pixel-size", DEFAULT); + bind_property ("icon-size", image, "pixel-size", SYNC_CREATE); app.notify["count-visible"].connect (update_badge_revealer); update_badge_revealer (); @@ -294,26 +240,6 @@ public class Dock.Launcher : Gtk.Box { add_controller (drop_controller_motion); drop_controller_motion.enter.connect (queue_dnd_cycle); drop_controller_motion.leave.connect (remove_dnd_cycle); - - fade = new Adw.TimedAnimation ( - this, 0, 1, - Granite.TRANSITION_DURATION_OPEN, - new Adw.CallbackAnimationTarget ((val) => { - opacity = val; - }) - ) { - easing = EASE_IN_OUT_QUAD - }; - - reveal = new Adw.TimedAnimation ( - overlay, image.pixel_size, 0, - Granite.TRANSITION_DURATION_OPEN, - new Adw.CallbackAnimationTarget ((val) => { - overlay.allocate (image.pixel_size, image.pixel_size, -1, - new Gsk.Transform ().translate (Graphene.Point () { y = (float) val } - )); - }) - ); } ~Launcher () { @@ -322,14 +248,12 @@ public class Dock.Launcher : Gtk.Box { } /** - * If the launcher isn't needed anymore call this otherwise it won't be freed. + * {@inheritDoc} */ - public void cleanup () { - timed_animation = null; + public override void cleanup () { + base.cleanup (); bounce_down = null; bounce_up = null; - fade = null; - reveal = null; current_count_binding.unbind (); remove_dnd_cycle (); } @@ -367,47 +291,6 @@ public class Dock.Launcher : Gtk.Box { bounce_up.play (); } - /** - * Makes the launcher animate a move to the given position. Make sure to - * always use this instead of manually calling Gtk.Fixed.move on the manager - * when moving a launcher so that its current_pos is always up to date. - */ - public void animate_move (double new_position) { - timed_animation.value_from = current_pos; - timed_animation.value_to = new_position; - - timed_animation.play (); - } - - public void set_revealed (bool revealed) { - fade.skip (); - reveal.skip (); - - // Avoid a stutter at the beginning - opacity = 0; - // clip launcher to dock size until we finish animating - overflow = HIDDEN; - - if (revealed) { - reveal.easing = EASE_OUT_BACK; - } else { - fade.duration = Granite.TRANSITION_DURATION_CLOSE; - fade.reverse = true; - - reveal.duration = Granite.TRANSITION_DURATION_CLOSE; - reveal.easing = EASE_IN_OUT_QUAD; - reveal.reverse = true; - } - - fade.play (); - reveal.play (); - - reveal.done.connect (() => { - overflow = VISIBLE; - revealed_done (); - }); - } - private Gdk.ContentProvider? on_drag_prepare (double x, double y) { drag_offset_x = (int) x; drag_offset_y = (int) y; diff --git a/src/BaseItem.vala b/src/BaseItem.vala new file mode 100644 index 00000000..de07f7ec --- /dev/null +++ b/src/BaseItem.vala @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + */ + + public class Dock.BaseItem : Gtk.Box { + protected static GLib.Settings dock_settings; + + static construct { + dock_settings = new GLib.Settings ("io.elementary.dock"); + } + + public signal void removed (); + public signal void revealed_done (); + + public int icon_size { get; set; } + public double current_pos { get; set; } + + protected Gtk.Overlay overlay; + protected Gtk.Revealer running_revealer; + protected Gtk.GestureClick gesture_click; + + private Adw.TimedAnimation fade; + private Adw.TimedAnimation reveal; + private Adw.TimedAnimation timed_animation; + + private BaseItem () {} + + construct { + orientation = VERTICAL; + + overlay = new Gtk.Overlay (); + + var running_indicator = new Gtk.Image.from_icon_name ("pager-checked-symbolic"); + running_indicator.add_css_class ("running-indicator"); + + running_revealer = new Gtk.Revealer () { + can_target = false, + child = running_indicator, + overflow = VISIBLE, + transition_type = CROSSFADE, + valign = END + }; + + append (overlay); + append (running_revealer); + + icon_size = dock_settings.get_int ("icon-size"); + dock_settings.changed["icon-size"].connect (() => { + icon_size = dock_settings.get_int ("icon-size"); + }); + + fade = new Adw.TimedAnimation ( + this, 0, 1, + Granite.TRANSITION_DURATION_OPEN, + new Adw.CallbackAnimationTarget ((val) => { + opacity = val; + }) + ) { + easing = EASE_IN_OUT_QUAD + }; + + reveal = new Adw.TimedAnimation ( + overlay, icon_size, 0, + Granite.TRANSITION_DURATION_OPEN, + new Adw.CallbackAnimationTarget ((val) => { + overlay.allocate (icon_size, icon_size, -1, + new Gsk.Transform ().translate (Graphene.Point () { y = (float) val } + )); + }) + ); + + unowned var item_manager = ItemManager.get_default (); + var animation_target = new Adw.CallbackAnimationTarget ((val) => { + item_manager.move (this, val, 0); + current_pos = val; + }); + + timed_animation = new Adw.TimedAnimation ( + this, + 0, + 0, + 200, + animation_target + ) { + easing = EASE_IN_OUT_QUAD + }; + + gesture_click = new Gtk.GestureClick (); + add_controller (gesture_click); + } + + public void set_revealed (bool revealed) { + fade.skip (); + reveal.skip (); + + // Avoid a stutter at the beginning + opacity = 0; + // clip launcher to dock size until we finish animating + overflow = HIDDEN; + + if (revealed) { + reveal.easing = EASE_OUT_BACK; + } else { + fade.duration = Granite.TRANSITION_DURATION_CLOSE; + fade.reverse = true; + + reveal.duration = Granite.TRANSITION_DURATION_CLOSE; + reveal.easing = EASE_IN_OUT_QUAD; + reveal.reverse = true; + } + + fade.play (); + reveal.play (); + + reveal.done.connect (() => { + overflow = VISIBLE; + revealed_done (); + }); + } + + /** + * Makes the launcher animate a move to the given position. Make sure to + * always use this instead of manually calling Gtk.Fixed.move on the manager + * when moving a launcher so that its current_pos is always up to date. + */ + public void animate_move (double new_position) { + timed_animation.value_from = current_pos; + timed_animation.value_to = new_position; + + timed_animation.play (); + } + + /** + * If the icon group isn't needed anymore call this otherwise it won't be freed. + */ + public virtual void cleanup () { + fade = null; + reveal = null; + timed_animation = null; + } +} diff --git a/src/ItemManager.vala b/src/ItemManager.vala index 904d0376..230194b6 100644 --- a/src/ItemManager.vala +++ b/src/ItemManager.vala @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: GPL-3.0 - * SPDX-FileCopyrightText: 2023 elementary, Inc. (https://elementary.io) + * SPDX-FileCopyrightText: 2023-2025 elementary, Inc. (https://elementary.io) */ public class Dock.ItemManager : Gtk.Fixed { @@ -36,7 +36,7 @@ settings.changed.connect ((key) => { if (key == "icon-size") { - reposition_launchers (); + reposition_items (); } }); @@ -98,20 +98,22 @@ AppSystem.get_default ().app_added.connect ((app) => { var launcher = new Launcher (app); - int position = -1; if (drop_target_file.get_value () != null && added_launcher == null) { // The launcher is being added via dnd from wingpanel - position = (int) Math.round (drop_x / get_launcher_size ()); + var position = (int) Math.round (drop_x / get_launcher_size ()); added_launcher = launcher; launcher.moving = true; + + add_launcher_via_dnd (launcher, position); + return; } - add_launcher (launcher, position); + add_item (launcher); }); map.connect (AppSystem.get_default ().load); } - private void reposition_launchers () { + private void reposition_items () { var launcher_size = get_launcher_size (); int index = 0; @@ -129,18 +131,20 @@ } } - private void add_launcher (Launcher launcher, int index = -1) { - launcher.removed.connect (remove_launcher); + private void add_launcher_via_dnd (Launcher launcher, int index = -1) { + launcher.removed.connect (remove_item); - if (index >= 0) { - // If the index is > 0 the resize is done by the reposition so we return early - launchers.insert (launcher, index); - reposition_launchers (); - launcher.set_revealed (true); - return; - } + launchers.insert (launcher, index); + reposition_items (); + launcher.set_revealed (true); + } + + private void add_item (BaseItem item, int index = -1) { + item.removed.connect (remove_item); - launchers.append (launcher); + if (item is Launcher) { + launchers.append ((Launcher) item); + } resize_animation.easing = EASE_OUT_BACK; resize_animation.duration = Granite.TRANSITION_DURATION_OPEN; @@ -150,24 +154,26 @@ ulong reveal_cb = 0; reveal_cb = resize_animation.done.connect (() => { - reposition_launchers (); - launcher.set_revealed (true); + reposition_items (); + item.set_revealed (true); resize_animation.disconnect (reveal_cb); }); } - private void remove_launcher (Launcher launcher) { - launchers.remove (launcher); + private void remove_item (BaseItem item) { + if (item is Launcher) { + launchers.remove ((Launcher) item); + } - launcher.set_revealed (false); - launcher.revealed_done.connect (remove_finish); + item.set_revealed (false); + item.revealed_done.connect (remove_finish); } - private void remove_finish (Launcher launcher) { + private void remove_finish (BaseItem item) { width_request = get_width (); //Temporarily set the width request to avoid flicker until the animation calls the callback for the first time - remove (launcher); - reposition_launchers (); + remove (item); + reposition_items (); resize_animation.easing = EASE_IN_OUT_QUAD; resize_animation.duration = Granite.TRANSITION_DURATION_CLOSE; @@ -175,7 +181,7 @@ resize_animation.value_to = launchers.length () * get_launcher_size (); resize_animation.play (); - launcher.cleanup (); + item.cleanup (); } public void move_launcher_after (Launcher source, int target_index) { diff --git a/src/meson.build b/src/meson.build index 25b12389..83240a98 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ sources = [ 'Application.vala', + 'BaseItem.vala', 'ItemManager.vala', 'MainWindow.vala', 'AppSystem' / 'App.vala',