diff --git a/daemon-gtk3/DBus.vala b/daemon-gtk3/DBus.vala index 41a2e0c31..0d502e2aa 100644 --- a/daemon-gtk3/DBus.vala +++ b/daemon-gtk3/DBus.vala @@ -8,15 +8,6 @@ public interface Gala.WMDBus : GLib.Object { public abstract void perform_action (Gala.ActionType type) throws DBusError, IOError; } -public struct Gala.Daemon.MonitorLabelInfo { - public int monitor; - public string label; - public string background_color; - public string text_color; - public int x; - public int y; -} - [DBus (name = "org.pantheon.gala.daemon")] public class Gala.Daemon.DBus : GLib.Object { private const string DBUS_NAME = "org.pantheon.gala"; @@ -30,8 +21,6 @@ public class Gala.Daemon.DBus : GLib.Object { private WindowMenu? window_menu; private BackgroundMenu? background_menu; - private List monitor_labels = new List (); - construct { Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala); } @@ -113,21 +102,4 @@ public class Gala.Daemon.DBus : GLib.Object { }); } } - - public void show_monitor_labels (MonitorLabelInfo[] label_infos) throws GLib.DBusError, GLib.IOError { - hide_monitor_labels (); - - monitor_labels = new List (); - foreach (var info in label_infos) { - var label = new MonitorLabel (info); - monitor_labels.append (label); - label.present (); - } - } - - public void hide_monitor_labels () throws GLib.DBusError, GLib.IOError { - foreach (var monitor_label in monitor_labels) { - monitor_label.close (); - } - } } diff --git a/daemon-gtk3/MonitorLabel.vala b/daemon-gtk3/MonitorLabel.vala deleted file mode 100644 index 0ece57239..000000000 --- a/daemon-gtk3/MonitorLabel.vala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 elementary, Inc. (https://elementary.io) - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - - public class Gala.Daemon.MonitorLabel : Hdy.Window { - private const int SPACING = 12; - private const string COLORED_STYLE_CSS = """ - .%s { - background-color: alpha(%s, 0.8); - color: %s; - } - """; - - public MonitorLabelInfo info { get; construct; } - - public MonitorLabel (MonitorLabelInfo info) { - Object (info: info); - } - - construct { - child = new Gtk.Label (info.label); - - title = "LABEL-%i".printf (info.monitor); - - input_shape_combine_region (null); - accept_focus = false; - decorated = false; - resizable = false; - deletable = false; - can_focus = false; - skip_taskbar_hint = true; - skip_pager_hint = true; - type_hint = Gdk.WindowTypeHint.TOOLTIP; - set_keep_above (true); - - stick (); - - var scale_factor = get_style_context ().get_scale (); - move ( - (int) (info.x / scale_factor) + SPACING, - (int) (info.y / scale_factor) + SPACING - ); - - var provider = new Gtk.CssProvider (); - try { - provider.load_from_data (COLORED_STYLE_CSS.printf (title, info.background_color, info.text_color)); - get_style_context ().add_class (title); - get_style_context ().add_class ("monitor-label"); - - Gtk.StyleContext.add_provider_for_screen ( - Gdk.Screen.get_default (), - provider, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ); - } catch (Error e) { - warning ("Failed to load CSS: %s", e.message); - } - - show_all (); - } -} diff --git a/daemon-gtk3/meson.build b/daemon-gtk3/meson.build index c4a53ccec..43f7c0123 100644 --- a/daemon-gtk3/meson.build +++ b/daemon-gtk3/meson.build @@ -1,7 +1,6 @@ gala_daemon_sources = files( 'Main.vala', 'DBus.vala', - 'MonitorLabel.vala', 'Window.vala', 'WindowMenu.vala', 'BackgroundMenu.vala', diff --git a/daemon/DBus.vala b/daemon/DBus.vala index 75f996f73..59b9b2566 100644 --- a/daemon/DBus.vala +++ b/daemon/DBus.vala @@ -44,15 +44,6 @@ public interface Gala.WMDBus : GLib.Object { public abstract void perform_action (Gala.ActionType type) throws DBusError, IOError; } -public struct Gala.Daemon.MonitorLabelInfo { - public int monitor; - public string label; - public string background_color; - public string text_color; - public int x; - public int y; -} - [DBus (name = "org.pantheon.gala.daemon")] public class Gala.Daemon.DBus : GLib.Object { private const string DBUS_NAME = "org.pantheon.gala"; @@ -70,8 +61,6 @@ public class Gala.Daemon.DBus : GLib.Object { private WindowMenu? window_menu; private Gtk.PopoverMenu background_menu; - private List monitor_labels = new List (); - construct { Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala); @@ -182,23 +171,6 @@ public class Gala.Daemon.DBus : GLib.Object { }); } - public void show_monitor_labels (MonitorLabelInfo[] label_infos) throws GLib.DBusError, GLib.IOError { - hide_monitor_labels (); - - monitor_labels = new List (); - foreach (var info in label_infos) { - var label = new MonitorLabel (info); - monitor_labels.append (label); - label.present (); - } - } - - public void hide_monitor_labels () throws GLib.DBusError, GLib.IOError { - foreach (var monitor_label in monitor_labels) { - monitor_label.close (); - } - } - private static void action_launch (SimpleAction action, Variant? variant) { try { AppInfo.launch_default_for_uri (variant.get_string (), null); diff --git a/daemon/MonitorLabel.vala b/daemon/MonitorLabel.vala deleted file mode 100644 index c3a19f4bb..000000000 --- a/daemon/MonitorLabel.vala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 elementary, Inc. (https://elementary.io) - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - - public class Gala.Daemon.MonitorLabel : Gtk.Window { - private const int SPACING = 12; - private const string COLORED_STYLE_CSS = """ - .%s { - background-color: alpha(%s, 0.8); - color: %s; - } - """; - - public MonitorLabelInfo info { get; construct; } - - public MonitorLabel (MonitorLabelInfo info) { - Object (info: info); - } - - construct { - child = new Gtk.Label (info.label); - - title = "LABEL-%i".printf (info.monitor); - - decorated = false; - resizable = false; - deletable = false; - can_focus = false; - titlebar = new Gtk.Grid (); - - var provider = new Gtk.CssProvider (); - try { - provider.load_from_string (COLORED_STYLE_CSS.printf (title, info.background_color, info.text_color)); - get_style_context ().add_class (title); - get_style_context ().add_class ("monitor-label"); - - Gtk.StyleContext.add_provider_for_display ( - Gdk.Display.get_default (), - provider, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ); - } catch (Error e) { - warning ("Failed to load CSS: %s", e.message); - } - - present (); - } -} diff --git a/daemon/meson.build b/daemon/meson.build index 595d1dfa3..ff49decd8 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -1,7 +1,6 @@ gala_daemon_sources = files( 'Main.vala', 'DBus.vala', - 'MonitorLabel.vala', 'Window.vala', 'WindowMenu.vala', ) diff --git a/data/gala-daemon.css b/data/gala-daemon.css index 43a0f9eb1..81c528760 100644 --- a/data/gala-daemon.css +++ b/data/gala-daemon.css @@ -6,13 +6,3 @@ daemon-window { background: transparent; } - -.monitor-label { - border-radius: 9px; - font-weight: 600; -} - -.monitor-label label { - margin: 1em; - text-shadow: 0 1px 1px alpha(white, 0.1); -} diff --git a/protocol/pantheon-desktop-shell-v1.xml b/protocol/pantheon-desktop-shell-v1.xml index 63d9d5982..2c8f716ba 100644 --- a/protocol/pantheon-desktop-shell-v1.xml +++ b/protocol/pantheon-desktop-shell-v1.xml @@ -119,6 +119,15 @@ + + + Request to make the surface a monitor label for the given monitor. The surface will be placed + in the top left corner of the monitor and will be kept above other surfaces. + + + + + Request keyboard focus, taking it away from any other window. diff --git a/protocol/pantheon-desktop-shell.vapi b/protocol/pantheon-desktop-shell.vapi index 4c57de974..db2160182 100644 --- a/protocol/pantheon-desktop-shell.vapi +++ b/protocol/pantheon-desktop-shell.vapi @@ -57,6 +57,7 @@ namespace Pantheon.Desktop { public Destroy destroy; public SetKeepAbove set_keep_above; public MakeCentered make_centered; + public MakeMonitorLabel make_monitor_label; public Focus focus; } @@ -79,5 +80,7 @@ namespace Pantheon.Desktop { [CCode (has_target = false, has_typedef = false)] public delegate void MakeCentered (Wl.Client client, Wl.Resource resource); [CCode (has_target = false, has_typedef = false)] + public delegate void MakeMonitorLabel (Wl.Client client, Wl.Resource resource, int monitor_index); + [CCode (has_target = false, has_typedef = false)] public delegate void Destroy (Wl.Client client, Wl.Resource resource); } diff --git a/src/DaemonManager.vala b/src/DaemonManager.vala index 551f3f470..0ee8ccf80 100644 --- a/src/DaemonManager.vala +++ b/src/DaemonManager.vala @@ -89,18 +89,6 @@ public class Gala.DaemonManager : GLib.Object { } switch (info[0]) { - case "LABEL": - if (info.length < 2) { - return; - } - - var index = int.parse (info[1]); - - var monitor_geometry = display.get_monitor_geometry (index); - window.move_frame (false, monitor_geometry.x + SPACING, monitor_geometry.y + SPACING); - window.make_above (); - break; - case "MODAL": #if HAS_MUTTER46 daemon_client.make_dock (window); diff --git a/src/PantheonShell.vala b/src/PantheonShell.vala index 1e0f173ff..f8a301c3d 100644 --- a/src/PantheonShell.vala +++ b/src/PantheonShell.vala @@ -68,6 +68,7 @@ namespace Gala { destroy_extended_behavior_surface, set_keep_above, make_centered, + make_monitor_label, focus_extended_behavior, }; @@ -359,6 +360,21 @@ namespace Gala { ShellClientsManager.get_instance ().make_centered (window); } + internal static void make_monitor_label (Wl.Client client, Wl.Resource resource, int monitor_index) { + unowned ExtendedBehaviorSurface? eb_surface = resource.get_user_data (); + if (eb_surface.wayland_surface == null) { + return; + } + + Meta.Window? window; + eb_surface.wayland_surface.get ("window", out window, null); + if (window == null) { + return; + } + + ShellClientsManager.get_instance ().make_monitor_label (window, monitor_index); + } + internal static void destroy_panel_surface (Wl.Client client, Wl.Resource resource) { resource.destroy (); } diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index ad15ca9e8..780b4cdf1 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -25,8 +25,8 @@ public class Gala.ShellClientsManager : Object { private NotificationsClient notifications_client; private ManagedClient[] protocol_clients = {}; - private GLib.HashTable windows = new GLib.HashTable (null, null); - private GLib.HashTable centered_windows = new GLib.HashTable (null, null); + private GLib.HashTable panel_windows = new GLib.HashTable (null, null); + private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); private ShellClientsManager (WindowManager wm) { Object (wm: wm); @@ -144,18 +144,18 @@ public class Gala.ShellClientsManager : Object { } public void set_anchor (Meta.Window window, Meta.Side side) { - if (window in windows) { - windows[window].update_anchor (side); + if (window in panel_windows) { + panel_windows[window].update_anchor (side); return; } make_dock (window); // TODO: Return if requested by window that's not a trusted client? - windows[window] = new PanelWindow (wm, window, side); + panel_windows[window] = new PanelWindow (wm, window, side); // connect_after so we make sure the PanelWindow can destroy its barriers and struts - window.unmanaging.connect_after (() => windows.remove (window)); + window.unmanaging.connect_after ((_window) => panel_windows.remove (_window)); } /** @@ -166,37 +166,69 @@ public class Gala.ShellClientsManager : Object { * TODO: Maybe use for strut only? */ public void set_size (Meta.Window window, int width, int height) { - if (!(window in windows)) { + if (!(window in panel_windows)) { warning ("Set anchor for window before size."); return; } - windows[window].set_size (width, height); + panel_windows[window].set_size (width, height); } public void set_hide_mode (Meta.Window window, Pantheon.Desktop.HideMode hide_mode) { - if (!(window in windows)) { + if (!(window in panel_windows)) { warning ("Set anchor for window before hide mode."); return; } - windows[window].set_hide_mode (hide_mode); + panel_windows[window].set_hide_mode (hide_mode); } - public void make_centered (Meta.Window window) { - if (window in centered_windows) { + public void make_centered (Meta.Window window) requires (!is_itself_positioned (window)) { + positioned_windows[window] = new WindowPositioner (window, wm, (ref x, ref y) => { + unowned var display = wm.get_display (); + var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); + var window_rect = window.get_frame_rect (); + + x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; + y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2; + }); + + window.unmanaging.connect_after ((_window) => positioned_windows.remove (_window)); + } + + public void make_monitor_label (Meta.Window window, int monitor_index) requires (!is_itself_positioned (window)) { + if (monitor_index < 0 || monitor_index > wm.get_display ().get_n_monitors ()) { + warning ("Invalid monitor index provided: %d", monitor_index); return; } - centered_windows[window] = new CenteredWindow (wm, window); + window.make_above (); + + positioned_windows[window] = new WindowPositioner (window, wm, (ref x, ref y) => { + unowned var display = wm.get_display (); + + if (monitor_index >= display.get_n_monitors ()) { + critical ("Monitor index %d of monitor label window %s went out of bounds", monitor_index, window.title ?? "Unnamed"); + return; + } + + var monitor_geom = display.get_monitor_geometry (monitor_index); - window.unmanaging.connect_after (() => centered_windows.remove (window)); + x = monitor_geom.x + 12; + y = monitor_geom.y + 12; + }); + + window.unmanaging.connect_after ((_window) => positioned_windows.remove (_window)); + } + + private bool is_itself_positioned (Meta.Window window) { + return (window in positioned_windows) || (window in panel_windows); } public bool is_positioned_window (Meta.Window window) { - bool positioned = (window in centered_windows) || (window in windows); + bool positioned = is_itself_positioned (window); window.foreach_ancestor ((ancestor) => { - if (ancestor in centered_windows || ancestor in windows) { + if (ancestor in positioned_windows || ancestor in panel_windows) { positioned = true; } @@ -259,6 +291,15 @@ public class Gala.ShellClientsManager : Object { make_centered (window); break; + case "monitor-label": + int parsed; + if (int.try_parse (val, out parsed)) { + make_monitor_label (window, parsed); + } else { + warning ("Failed to parse %s as monitor label", val); + } + break; + default: break; } diff --git a/src/ShellClients/CenteredWindow.vala b/src/ShellClients/WindowPositioner.vala similarity index 59% rename from src/ShellClients/CenteredWindow.vala rename to src/ShellClients/WindowPositioner.vala index 6099c68d1..3f9df883a 100644 --- a/src/ShellClients/CenteredWindow.vala +++ b/src/ShellClients/WindowPositioner.vala @@ -5,27 +5,35 @@ * Authored by: Leonhard Kargl */ -public class Gala.CenteredWindow : Object { - public WindowManager wm { get; construct; } +public class Gala.WindowPositioner : Object { + public delegate void PositionFunc (ref int x, ref int y); + public Meta.Window window { get; construct; } + public WindowManager wm { get; construct; } + + private PositionFunc position_func; private uint idle_move_id = 0; - public CenteredWindow (WindowManager wm, Meta.Window window) { - Object (wm: wm, window: window); + public WindowPositioner (Meta.Window window, WindowManager wm, owned PositionFunc position_func) { + Object (window: window, wm: wm); + + this.position_func = (owned) position_func; } construct { - window.size_changed.connect (position_window); window.stick (); + window.size_changed.connect (position_window); + window.position_changed.connect (position_window); + window.shown.connect (position_window); + var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); - monitor_manager.monitors_changed.connect (() => position_window ()); + monitor_manager.monitors_changed.connect (position_window); + monitor_manager.monitors_changed_internal.connect (position_window); position_window (); - window.shown.connect (() => window.focus (wm.get_display ().get_current_time ())); - window.unmanaging.connect (() => { if (idle_move_id != 0) { Source.remove (idle_move_id); @@ -34,18 +42,14 @@ public class Gala.CenteredWindow : Object { } private void position_window () { - var display = wm.get_display (); - var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - var window_rect = window.get_frame_rect (); - - var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - var y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2; - if (idle_move_id != 0) { Source.remove (idle_move_id); } idle_move_id = Idle.add (() => { + int x = 0, y = 0; + position_func (ref x, ref y); + window.move_frame (false, x, y); idle_move_id = 0; diff --git a/src/meson.build b/src/meson.build index e2e61807b..7e6488fd1 100644 --- a/src/meson.build +++ b/src/meson.build @@ -41,13 +41,13 @@ gala_bin_sources = files( 'HotCorners/Barrier.vala', 'HotCorners/HotCorner.vala', 'HotCorners/HotCornerManager.vala', - 'ShellClients/CenteredWindow.vala', 'ShellClients/HideTracker.vala', 'ShellClients/ManagedClient.vala', 'ShellClients/NotificationsClient.vala', 'ShellClients/PanelClone.vala', 'ShellClients/PanelWindow.vala', 'ShellClients/ShellClientsManager.vala', + 'ShellClients/WindowPositioner.vala', 'Widgets/DwellClickTimer.vala', 'Widgets/IconGroup.vala', 'Widgets/IconGroupContainer.vala',