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
53 changes: 13 additions & 40 deletions daemon-gtk3/DBus.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* Copyright 2024-2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*/

Expand All @@ -17,60 +17,33 @@ public struct Gala.Daemon.MonitorLabelInfo {
public int y;
}

public struct Gala.Daemon.DaemonWindowMenuItem {
WindowMenuItemType type;
bool sensitive;
bool toggle_state;
string display_name;
string keybinding;
}

[DBus (name = "org.pantheon.gala.daemon")]
public class Gala.Daemon.DBus : GLib.Object {
private const string DBUS_NAME = "org.pantheon.gala";
private const string DBUS_OBJECT_PATH = "/org/pantheon/gala";

private const string DAEMON_DBUS_NAME = "org.pantheon.gala.daemon";
private const string DAEMON_DBUS_OBJECT_PATH = "/org/pantheon/gala/daemon";

private WMDBus? wm_proxy = null;
public signal void window_menu_action_invoked (int action);

private WindowMenu? window_menu;
private BackgroundMenu? background_menu;

private List<MonitorLabel> monitor_labels = new List<MonitorLabel> ();

construct {
Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala);
}

private void on_gala_get (GLib.Object? obj, GLib.AsyncResult? res) {
try {
wm_proxy = Bus.get_proxy.end (res);
} catch (Error e) {
warning ("Failed to get Gala proxy: %s", e.message);
}
}

private void lost_gala () {
wm_proxy = null;
}

private void gala_appeared () {
if (wm_proxy == null) {
Bus.get_proxy.begin<WMDBus> (BusType.SESSION, DBUS_NAME, DBUS_OBJECT_PATH, 0, null, on_gala_get);
}
}

private void perform_action (Gala.ActionType type) {
if (wm_proxy != null) {
try {
wm_proxy.perform_action (type);
} catch (Error e) {
warning ("Failed to perform Gala action over DBus: %s", e.message);
}
}
}

public void show_window_menu (Gala.WindowFlags flags, int display_width, int display_height, int x, int y) throws DBusError, IOError {
public void show_window_menu (int display_width, int display_height, int x, int y, DaemonWindowMenuItem[] items) throws Error {
if (window_menu == null) {
window_menu = new WindowMenu ();
window_menu.perform_action.connect (perform_action);
window_menu.action_invoked.connect ((action) => window_menu_action_invoked (action));
}

window_menu.update (flags);
window_menu.update (items);

show_menu (window_menu, display_width, display_height, x, y, true);
}
Expand Down
205 changes: 28 additions & 177 deletions daemon-gtk3/WindowMenu.vala
Original file line number Diff line number Diff line change
@@ -1,194 +1,45 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* Copyright 2024-2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*/

public class Gala.Daemon.WindowMenu : Gtk.Menu {
private static GLib.Settings gala_keybind_settings = new GLib.Settings ("io.elementary.desktop.wm.keybindings");
private static GLib.Settings keybind_settings = new GLib.Settings ("org.gnome.desktop.wm.keybindings");
public signal void action_invoked (int action);

public signal void perform_action (Gala.ActionType type);

private Granite.AccelLabel always_on_top_accellabel;
private Granite.AccelLabel close_accellabel;
private Granite.AccelLabel minimize_accellabel;
private Granite.AccelLabel move_accellabel;
private Granite.AccelLabel move_left_accellabel;
private Granite.AccelLabel move_right_accellabel;
private Granite.AccelLabel on_visible_workspace_accellabel;
private Granite.AccelLabel resize_accellabel;
private Granite.AccelLabel screenshot_accellabel;
private Gtk.MenuItem minimize;
private Gtk.MenuItem maximize;
private Gtk.MenuItem move;
private Gtk.MenuItem resize;
private Gtk.CheckMenuItem always_on_top;
private Gtk.CheckMenuItem on_visible_workspace;
private Gtk.MenuItem move_left;
private Gtk.MenuItem move_right;
private Gtk.MenuItem close;
private Gtk.MenuItem screenshot;

private ulong always_on_top_sid = 0U;
private ulong on_visible_workspace_sid = 0U;

construct {
minimize_accellabel = new Granite.AccelLabel (_("Hide"));

minimize = new Gtk.MenuItem ();
minimize.add (minimize_accellabel);
minimize.activate.connect (() => {
perform_action (Gala.ActionType.HIDE_CURRENT);
});

maximize = new Gtk.MenuItem ();
maximize.activate.connect (() => {
perform_action (Gala.ActionType.MAXIMIZE_CURRENT);
});

move_accellabel = new Granite.AccelLabel (_("Move"));

move = new Gtk.MenuItem ();
move.add (move_accellabel);
move.activate.connect (() => {
perform_action (Gala.ActionType.START_MOVE_CURRENT);
});

resize_accellabel = new Granite.AccelLabel (_("Resize"));

resize = new Gtk.MenuItem ();
resize.add (resize_accellabel);
resize.activate.connect (() => {
perform_action (Gala.ActionType.START_RESIZE_CURRENT);
});

always_on_top_accellabel = new Granite.AccelLabel (_("Always on Top"));

always_on_top = new Gtk.CheckMenuItem ();
always_on_top.add (always_on_top_accellabel);
always_on_top_sid = always_on_top.activate.connect (() => {
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_TOP_CURRENT);
});

on_visible_workspace_accellabel = new Granite.AccelLabel (_("Always on Visible Workspace"));

on_visible_workspace = new Gtk.CheckMenuItem ();
on_visible_workspace.add (on_visible_workspace_accellabel);
on_visible_workspace_sid = on_visible_workspace.activate.connect (() => {
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_VISIBLE_WORKSPACE_CURRENT);
});

move_left_accellabel = new Granite.AccelLabel (_("Move to Workspace Left"));

move_left = new Gtk.MenuItem ();
move_left.add (move_left_accellabel);
move_left.activate.connect (() => {
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_LEFT);
});

move_right_accellabel = new Granite.AccelLabel (_("Move to Workspace Right"));

move_right = new Gtk.MenuItem ();
move_right.add (move_right_accellabel);
move_right.activate.connect (() => {
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_RIGHT);
});

screenshot_accellabel = new Granite.AccelLabel (_("Take Screenshot"));
public void update (DaemonWindowMenuItem[] items) {
foreach (unowned var child in get_children ()) {
remove (child);
}

screenshot = new Gtk.MenuItem ();
screenshot.add (screenshot_accellabel);
screenshot.activate.connect (() => {
perform_action (Gala.ActionType.SCREENSHOT_CURRENT);
});
for (var i = 0; i < items.length; i++) {
var item = items[i];

var accel_label = new Granite.AccelLabel (item.display_name, item.keybinding);

close_accellabel = new Granite.AccelLabel (_("Close"));
if (item.type == BUTTON) {
var button = new Gtk.MenuItem () {
child = accel_label,
sensitive = item.sensitive
};

close = new Gtk.MenuItem ();
close.add (close_accellabel);
close.activate.connect (() => {
perform_action (Gala.ActionType.CLOSE_CURRENT);
});
var i_copy = i;
button.activate.connect (() => action_invoked (i_copy));

append (screenshot);
append (new Gtk.SeparatorMenuItem ());
append (always_on_top);
append (on_visible_workspace);
append (move_left);
append (move_right);
append (new Gtk.SeparatorMenuItem ());
append (move);
append (resize);
append (maximize);
append (new Gtk.SeparatorMenuItem ());
append (minimize);
append (close);
}
append (button);
} else if (item.type == TOGGLE) {
var button = new Gtk.CheckMenuItem () {
child = accel_label,
sensitive = item.sensitive,
active = item.toggle_state
};

public void update (Gala.WindowFlags flags) {
minimize.visible = Gala.WindowFlags.CAN_HIDE in flags;
if (minimize.visible) {
minimize_accellabel.accel_string = keybind_settings.get_strv ("minimize")[0];
}
var i_copy = i;
button.activate.connect (() => action_invoked (i_copy));

maximize.visible = Gala.WindowFlags.CAN_MAXIMIZE in flags;
if (maximize.visible) {
unowned string maximize_label;
if (Gala.WindowFlags.IS_MAXIMIZED in flags) {
maximize_label = (Gala.WindowFlags.IS_TILED in flags) ? _("Untile") : _("Unmaximize");
append (button);
} else {
maximize_label = _("Maximize");
append (new Gtk.SeparatorMenuItem ());
}

maximize.get_child ().destroy ();
maximize.add (
new Granite.AccelLabel (
maximize_label,
keybind_settings.get_strv ("toggle-maximized")[0]
)
);
}


move.visible = Gala.WindowFlags.ALLOWS_MOVE in flags;
if (move.visible) {
move_accellabel.accel_string = keybind_settings.get_strv ("begin-move")[0];
}

resize.visible = Gala.WindowFlags.ALLOWS_RESIZE in flags;
if (resize.visible) {
resize_accellabel.accel_string = keybind_settings.get_strv ("begin-resize")[0];
}

// Setting active causes signal fires on activate so
// we temporarily block those signals from emissions
SignalHandler.block (always_on_top, always_on_top_sid);
SignalHandler.block (on_visible_workspace, on_visible_workspace_sid);

always_on_top.active = Gala.WindowFlags.ALWAYS_ON_TOP in flags;
always_on_top_accellabel.accel_string = keybind_settings.get_strv ("always-on-top")[0];

on_visible_workspace.active = Gala.WindowFlags.ON_ALL_WORKSPACES in flags;
on_visible_workspace_accellabel.accel_string = keybind_settings.get_strv ("toggle-on-all-workspaces")[0];

SignalHandler.unblock (always_on_top, always_on_top_sid);
SignalHandler.unblock (on_visible_workspace, on_visible_workspace_sid);

move_right.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_RIGHT in flags;
if (move_right.sensitive) {
move_right_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-right")[0];
}

move_left.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_LEFT in flags;
if (move_left.sensitive) {
move_left_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-left")[0];
}

screenshot_accellabel.accel_string = gala_keybind_settings.get_strv ("window-screenshot")[0];

close.visible = Gala.WindowFlags.CAN_CLOSE in flags;
if (close.visible) {
close_accellabel.accel_string = keybind_settings.get_strv ("close")[0];
}
}
}
Loading
Loading