Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce an AppSystem #336

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
23 changes: 16 additions & 7 deletions src/App.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class Dock.App : Object {
}
}

public signal void removed ();

public bool pinned { get; construct set; }
public GLib.DesktopAppInfo app_info { get; construct; }

Expand Down Expand Up @@ -97,8 +99,6 @@ public class Dock.App : Object {
}
menu_model.append_section (null, pinned_section);

var launcher_manager = LauncherManager.get_default ();

pinned_action = new SimpleAction.stateful (PINNED_ACTION, null, new Variant.boolean (pinned));
pinned_action.change_state.connect ((new_state) => pinned = (bool) new_state);
action_group.add_action (pinned_action);
Expand All @@ -118,6 +118,7 @@ public class Dock.App : Object {

notify["pinned"].connect (() => {
pinned_action.set_state (pinned);
check_remove ();
LauncherManager.get_default ().sync_pinned ();
});
}
Expand All @@ -137,9 +138,9 @@ public class Dock.App : Object {
} else if (windows.size == 0) {
app_info.launch (null, context);
} else if (windows.size == 1) {
LauncherManager.get_default ().desktop_integration.focus_window.begin (windows.first ().uid);
} else if (LauncherManager.get_default ().desktop_integration != null) {
LauncherManager.get_default ().desktop_integration.show_windows_for.begin (app_info.get_id ());
AppSystem.get_default ().desktop_integration.focus_window.begin (windows.first ().uid);
} else if (AppSystem.get_default ().desktop_integration != null) {
AppSystem.get_default ().desktop_integration.show_windows_for.begin (app_info.get_id ());
}
} catch (Error e) {
critical (e.message);
Expand Down Expand Up @@ -177,6 +178,12 @@ public class Dock.App : Object {
return false;
}

private void check_remove () {
if (!pinned && !running) {
removed ();
}
}

public void update_windows (Gee.List<AppWindow>? new_windows) {
if (new_windows == null) {
windows = new Gee.LinkedList<AppWindow> ();
Expand All @@ -190,6 +197,8 @@ public class Dock.App : Object {
if (launching && running) {
launching = false;
}

check_remove ();
}

public AppWindow? find_window (uint64 window_uid) {
Expand Down Expand Up @@ -255,7 +264,7 @@ public class Dock.App : Object {
return;
}

LauncherManager.get_default ().desktop_integration.focus_window.begin (current_windows[current_index].uid);
AppSystem.get_default ().desktop_integration.focus_window.begin (current_windows[current_index].uid);

// Throttle the scroll for performance and better visibility of the windows
Timeout.add (250, () => {
Expand All @@ -270,7 +279,7 @@ public class Dock.App : Object {
if (timer_id != 0) {
Source.remove (timer_id);
} else {
yield LauncherManager.get_default ().sync_windows (); // Get the current stacking order
yield AppSystem.get_default ().sync_windows (); // Get the current stacking order
current_index = windows.size > 1 && windows.first ().has_focus ? 1 : 0;
current_windows = {};
foreach (AppWindow window in windows) {
Expand Down
165 changes: 165 additions & 0 deletions src/AppSystem.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
*/

public class Dock.AppSystem : Object, UnityClient {
private static Settings settings;
private static GLib.Once<AppSystem> instance;

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

public static unowned AppSystem get_default () {
return instance.once (() => { return new AppSystem (); });
}

public signal void app_added (App app);

public DesktopIntegration? desktop_integration { get; private set; }

private GLib.HashTable<unowned string, App> id_to_app;

private AppSystem () { }

construct {
id_to_app = new HashTable<unowned string, App> (str_hash, str_equal);
}

public App? get_app (string id) {
return id_to_app[id];
}

public async void load () {
foreach (string app_id in settings.get_strv ("launchers")) {
var app_info = new GLib.DesktopAppInfo (app_id);
add_app (app_info, true);
}

try {
desktop_integration = yield GLib.Bus.get_proxy<Dock.DesktopIntegration> (
SESSION,
"org.pantheon.gala",
"/org/pantheon/gala/DesktopInterface"
);

yield sync_windows ();

desktop_integration.windows_changed.connect (sync_windows);
} catch (Error e) {
critical ("Failed to get desktop integration: %s", e.message);
}
}

private App add_app (DesktopAppInfo app_info, bool pinned) {
var app = new App (app_info, pinned);
id_to_app[app_info.get_id ()] = app;
app.removed.connect ((_app) => id_to_app.remove (_app.app_info.get_id ()));
app_added (app);
return app;
}

public async void sync_windows () requires (desktop_integration != null) {
DesktopIntegration.Window[] windows;
try {
windows = yield desktop_integration.get_windows ();
} catch (Error e) {
critical (e.message);
return;
}

var app_window_list = new Gee.HashMap<App, Gee.List<AppWindow>> ();
foreach (unowned var window in windows) {
unowned var app_id = window.properties["app-id"].get_string ();
App? app = id_to_app[app_id];
if (app == null) {
var app_info = new GLib.DesktopAppInfo (app_id);
if (app_info == null) {
continue;
}

app = add_app (app_info, false);
}

AppWindow? app_window = app.find_window (window.uid);
if (app_window == null) {
app_window = new AppWindow (window.uid);
}

app_window.update_properties (window.properties);

var window_list = app_window_list.get (app);
if (window_list == null) {
var new_window_list = new Gee.LinkedList<AppWindow> ();
new_window_list.add (app_window);
app_window_list.set (app, new_window_list);
} else {
window_list.add (app_window);
}
}

foreach (var app in id_to_app.get_values ()) {
Gee.List<AppWindow>? window_list = null;
app_window_list.unset (app, out window_list);
app.update_windows (window_list);
}
}

public void add_app_for_id (string app_id) {
if (app_id in id_to_app) {
id_to_app[app_id].pinned = true;
return;
}

var app_info = new DesktopAppInfo (app_id);

if (app_info == null) {
warning ("App not found: %s", app_id);
return;
}

add_app (app_info, true);
}

public void remove_app_by_id (string app_id) {
if (app_id in id_to_app) {
id_to_app[app_id].pinned = false;
}
}

public string[] list_launchers () {
return settings.get_strv ("launchers");
}

private void update_launcher_entry (string sender_name, GLib.Variant parameters, bool is_retry = false) {
if (!is_retry) {
// Wait to let further update requests come in to catch the case where one application
// sends out multiple LauncherEntry-updates with different application-uris, e.g. Nautilus
Idle.add (() => {
update_launcher_entry (sender_name, parameters, true);
return false;
});

return;
}

string app_uri;
VariantIter prop_iter;
parameters.get ("(sa{sv})", out app_uri, out prop_iter);

var app_id = app_uri.replace ("application://", "");
if (id_to_app[app_id] != null) {
id_to_app[app_id].perform_unity_update (prop_iter);
} else {
critical ("unable to update missing launcher: %s", app_id);
}
}

private void remove_launcher_entry (string sender_name) {
var app_id = sender_name + ".desktop";
if (id_to_app[app_id] != null) {
id_to_app[app_id].remove_launcher_entry ();
}
}
}
2 changes: 1 addition & 1 deletion src/Application.vala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class Dock.Application : Gtk.Application {
add_window (main_window);

unowned var unity_client = Unity.get_default ();
unity_client.add_client (LauncherManager.get_default ());
unity_client.add_client (AppSystem.get_default ());
}

active_window.present ();
Expand Down
6 changes: 3 additions & 3 deletions src/DBus/ItemInterface.vala
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
[DBus (name = "io.elementary.dock.items")]
public class Dock.ItemInterface : Object {
public void add_launcher (string app_id) throws DBusError, IOError {
LauncherManager.get_default ().add_launcher_for_id (app_id);
AppSystem.get_default ().add_app_for_id (app_id);
}

public void remove_launcher (string app_id) throws DBusError, IOError {
LauncherManager.get_default ().remove_launcher_by_id (app_id);
AppSystem.get_default ().remove_app_by_id (app_id);
}

public string[] list_launchers () throws DBusError, IOError {
return LauncherManager.get_default ().list_launchers ();
return AppSystem.get_default ().list_launchers ();
}
}
13 changes: 11 additions & 2 deletions src/Launcher.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class Dock.Launcher : Gtk.Box {
}
}

public signal void removed ();
public signal void revealed_done ();

// Matches icon size and padding in Launcher.css
Expand Down Expand Up @@ -71,6 +72,8 @@ public class Dock.Launcher : Gtk.Box {
private int drag_offset_x = 0;
private int drag_offset_y = 0;

private bool flagged_for_removal = false;

public Launcher (App app) {
Object (app: app);
}
Expand Down Expand Up @@ -149,6 +152,7 @@ public class Dock.Launcher : Gtk.Box {
});

app.launched.connect (animate_launch);
app.removed.connect (() => removed ());

var bounce_animation_target = new Adw.CallbackAnimationTarget ((val) => {
var height = overlay.get_height ();
Expand Down Expand Up @@ -208,7 +212,11 @@ public class Dock.Launcher : Gtk.Box {
drag_source.prepare.connect (on_drag_prepare);
drag_source.drag_begin.connect (on_drag_begin);
drag_source.drag_cancel.connect (on_drag_cancel);
drag_source.drag_end.connect (() => moving = false);
drag_source.drag_end.connect (() => {
if (!flagged_for_removal) {
moving = false;
}
});

var drop_target = new Gtk.DropTarget (typeof (Launcher), MOVE) {
preload = true
Expand Down Expand Up @@ -435,7 +443,8 @@ public class Dock.Launcher : Gtk.Box {
popover.popup ();
popover.start_animation ();

LauncherManager.get_default ().remove_launcher (this, false);
app.pinned = false;
flagged_for_removal = true;

return true;
} else {
Expand Down
Loading
Loading