diff --git a/src/Launcher.vala b/src/Launcher.vala index 042a8d0d..2c1cfa19 100644 --- a/src/Launcher.vala +++ b/src/Launcher.vala @@ -11,6 +11,8 @@ public class Dock.Launcher : Gtk.Button { private static Gtk.CssProvider css_provider; + private Gtk.PopoverMenu popover; + public Launcher (GLib.DesktopAppInfo app_info) { Object (app_info: app_info); } @@ -28,6 +30,32 @@ public class Dock.Launcher : Gtk.Button { windows = new GLib.List (); get_style_context ().add_provider (css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + var action_section = new Menu (); + foreach (var action in app_info.list_actions ()) { + action_section.append ( + app_info.get_action_name (action), + MainWindow.ACTION_PREFIX + MainWindow.LAUNCHER_ACTION_TEMPLATE.printf (app_info.get_id (), action) + ); + } + + var pinned_section = new Menu (); + pinned_section.append ( + _("Keep in Dock"), + MainWindow.ACTION_PREFIX + MainWindow.LAUNCHER_PINNED_ACTION_TEMPLATE.printf (app_info.get_id ()) + ); + + var model = new Menu (); + if (action_section.get_n_items () > 0) { + model.append_section (null, action_section); + } + model.append_section (null, pinned_section); + + popover = new Gtk.PopoverMenu.from_model (model) { + autohide = true, + position = TOP + }; + popover.set_parent (this); + var image = new Gtk.Image () { gicon = app_info.get_icon () }; @@ -36,33 +64,51 @@ public class Dock.Launcher : Gtk.Button { child = image; tooltip_text = app_info.get_display_name (); - clicked.connect (() => { - try { - add_css_class ("bounce"); + notify["pinned"].connect (() => ((MainWindow) get_root ()).sync_pinned ()); - var context = Gdk.Display.get_default ().get_app_launch_context (); - context.set_timestamp (Gdk.CURRENT_TIME); + var gesture_click = new Gtk.GestureClick () { + button = Gdk.BUTTON_SECONDARY + }; + add_controller (gesture_click); + gesture_click.released.connect (popover.popup); + clicked.connect (() => launch ()); + } + + ~Launcher () { + popover.unparent (); + popover.dispose (); + } + + public void launch (string? action = null) { + try { + add_css_class ("bounce"); + + var context = Gdk.Display.get_default ().get_app_launch_context (); + context.set_timestamp (Gdk.CURRENT_TIME); + + if (action != null) { + app_info.launch_action (action, context); + } else { app_info.launch (null, context); - } catch (Error e) { - critical (e.message); } - Timeout.add (400, () => { - remove_css_class ("bounce"); + } catch (Error e) { + critical (e.message); + } - return Source.REMOVE; - }); + Timeout.add (400, () => { + remove_css_class ("bounce"); + return Source.REMOVE; }); } public void update_windows (owned GLib.List? new_windows) { if (new_windows == null) { windows = new GLib.List (); - return; + } else { + windows = (owned) new_windows; } - - windows = (owned) new_windows; } public AppWindow? find_window (uint64 window_uid) { diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 44200d8d..32100d9d 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -4,6 +4,13 @@ */ public class Dock.MainWindow : Gtk.ApplicationWindow { + // First %s is the app id second %s the action name + public const string LAUNCHER_ACTION_TEMPLATE = "%s.%s"; + // %s is the app id + public const string LAUNCHER_PINNED_ACTION_TEMPLATE = "%s-pinned"; + public const string ACTION_GROUP_PREFIX = "win"; + public const string ACTION_PREFIX = ACTION_GROUP_PREFIX + "."; + private static Gtk.CssProvider css_provider; private Gtk.Box box; @@ -67,6 +74,22 @@ public class Dock.MainWindow : Gtk.ApplicationWindow { unowned var app_id = app_info.get_id (); app_to_launcher.insert (app_id, launcher); box.append (launcher); + + var pinned_action = new SimpleAction.stateful ( + LAUNCHER_PINNED_ACTION_TEMPLATE.printf (app_id), + null, + new Variant.boolean (launcher.pinned) + ); + launcher.notify["pinned"].connect (() => pinned_action.set_state (launcher.pinned)); + pinned_action.change_state.connect ((new_state) => launcher.pinned = (bool) new_state); + add_action (pinned_action); + + foreach (var action in app_info.list_actions ()) { + var simple_action = new SimpleAction (LAUNCHER_ACTION_TEMPLATE.printf (app_id, action), null); + simple_action.activate.connect (() => launcher.launch (action)); + add_action (simple_action); + } + return app_to_launcher[app_id]; } @@ -110,14 +133,45 @@ public class Dock.MainWindow : Gtk.ApplicationWindow { app_to_launcher.foreach_remove ((app_id, launcher) => { var window_list = launcher_window_list.take (launcher); launcher.update_windows ((owned) window_list); - if (launcher.windows.is_empty ()) { - if (!launcher.pinned) { - launcher.unparent (); - return true; - } + if (launcher.windows.is_empty () && !launcher.pinned) { + remove_launcher (launcher, false); + return true; } return false; }); } + + public void remove_launcher (Launcher launcher, bool from_map = true) { + foreach (var action in list_actions ()) { + if (action.has_prefix (launcher.app_info.get_id ())) { + remove_action (action); + } + } + box.remove (launcher); + + if (from_map) { + app_to_launcher.remove (launcher.app_info.get_id ()); + } + } + + public void sync_pinned () { + string[] new_pinned_ids = {}; + + unowned Launcher child = (Launcher) box.get_first_child (); + while (child != null) { + unowned var current_child = child; + child = (Launcher) child.get_next_sibling (); + + if (current_child.pinned) { + new_pinned_ids += current_child.app_info.get_id (); + } else if (!current_child.pinned && current_child.windows.is_empty ()) { + remove_launcher (current_child); + } + } + + + var settings = new Settings ("io.elementary.dock"); + settings.set_strv ("launchers", new_pinned_ids); + } }