diff --git a/daemon/MenuDaemon.vala b/daemon/DBus.vala similarity index 78% rename from daemon/MenuDaemon.vala rename to daemon/DBus.vala index 298807b32..41a2e0c31 100644 --- a/daemon/MenuDaemon.vala +++ b/daemon/DBus.vala @@ -8,8 +8,17 @@ 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.MenuDaemon : GLib.Object { +public class Gala.Daemon.DBus : GLib.Object { private const string DBUS_NAME = "org.pantheon.gala"; private const string DBUS_OBJECT_PATH = "/org/pantheon/gala"; @@ -21,6 +30,8 @@ public class Gala.Daemon.MenuDaemon : 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); } @@ -102,4 +113,21 @@ public class Gala.Daemon.MenuDaemon : 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/Main.vala b/daemon/Main.vala index 4c4ff6e6b..d2ffaaacc 100644 --- a/daemon/Main.vala +++ b/daemon/Main.vala @@ -19,6 +19,10 @@ public class Gala.Daemon.Application : Gtk.Application { granite_settings.notify["prefers-color-scheme"].connect (() => { gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; }); + + var app_provider = new Gtk.CssProvider (); + app_provider.load_from_resource ("io/elementary/desktop/gala-daemon/gala-daemon.css"); + Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), app_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } public override void activate () { @@ -28,7 +32,7 @@ public class Gala.Daemon.Application : Gtk.Application { public override bool dbus_register (DBusConnection connection, string object_path) throws Error { base.dbus_register (connection, object_path); - connection.register_object (object_path, new MenuDaemon ()); + connection.register_object (object_path, new DBus ()); return true; } diff --git a/daemon/MonitorLabel.vala b/daemon/MonitorLabel.vala new file mode 100644 index 000000000..54d6861e0 --- /dev/null +++ b/daemon/MonitorLabel.vala @@ -0,0 +1,57 @@ +/* + * 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 = """ + @define-color BG_COLOR %s; + @define-color TEXT_COLOR %s; + """; + + public MonitorLabelInfo info { get; construct; } + + public MonitorLabel (MonitorLabelInfo info) { + Object (info: info); + } + + construct { + child = new Gtk.Label (info.label) { + margin = 12 + }; + + 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 (info.background_color, info.text_color)); + + get_style_context ().add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + get_style_context ().add_class ("colored"); + } catch (Error e) { + warning ("Failed to load CSS: %s", e.message); + } + + show_all (); + } +} diff --git a/daemon/Window.vala b/daemon/Window.vala index 2d2894eaa..3e864139e 100644 --- a/daemon/Window.vala +++ b/daemon/Window.vala @@ -6,12 +6,6 @@ */ public class Gala.Daemon.Window : Gtk.Window { - static construct { - var app_provider = new Gtk.CssProvider (); - app_provider.load_from_resource ("io/elementary/desktop/gala-daemon/gala-daemon.css"); - Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), app_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - } - public Gtk.Box content { get; construct; } public Window (int width, int height) { @@ -37,6 +31,7 @@ public class Gala.Daemon.Window : Gtk.Window { type_hint = Gdk.WindowTypeHint.DOCK; set_keep_above (true); + title = "MODAL"; child = content = new Gtk.Box (HORIZONTAL, 0) { hexpand = true, vexpand = true diff --git a/daemon/meson.build b/daemon/meson.build index 9f95b1105..5f7382764 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -1,15 +1,16 @@ gala_daemon_sources = files( 'Main.vala', - 'MenuDaemon.vala', + 'DBus.vala', + 'MonitorLabel.vala', 'Window.vala', 'WindowMenu.vala', - 'BackgroundMenu.vala' + 'BackgroundMenu.vala', ) gala_daemon_bin = executable( 'gala-daemon', gala_daemon_sources, - dependencies: [gala_dep, gala_base_dep], + dependencies: [gala_dep, gala_base_dep, hdy_dep], include_directories: config_inc_dir, install: true, ) diff --git a/data/gala-daemon.css b/data/gala-daemon.css index 8332358b3..9871a8a00 100644 --- a/data/gala-daemon.css +++ b/data/gala-daemon.css @@ -6,3 +6,18 @@ daemon-window { background-color: transparent; } + +/* For better dark style support */ +@define-color BG_COLOR_ALPHA alpha(@BG_COLOR, 0.75); + +.colored { + background-color: @BG_COLOR_ALPHA; + color: @TEXT_COLOR; + text-shadow: 0 1px 1px alpha(white, 0.1); + -gtk-icon-shadow: 0 1px 1px alpha(white, 0.1); + -gtk-icon-palette: warning white; +} + +window.colored { + background-color: alpha (@BG_COLOR, 0.8); +} diff --git a/meson.build b/meson.build index 68100464a..2e19fe708 100644 --- a/meson.build +++ b/meson.build @@ -94,6 +94,7 @@ gee_dep = dependency('gee-0.8') granite_dep = dependency('granite', version: '>= 5.4.0') gnome_desktop_dep = dependency('gnome-desktop-3.0') gsd_dep = dependency('gnome-settings-daemon', version: '>= @0@'.format(gsd_version_required)) +hdy_dep = dependency('libhandy-1') m_dep = cc.find_library('m', required: false) posix_dep = vala.find_library('posix', required: false) sqlite3_dep = dependency('sqlite3') diff --git a/src/DaemonManager.vala b/src/DaemonManager.vala index 6adc9738c..75215314f 100644 --- a/src/DaemonManager.vala +++ b/src/DaemonManager.vala @@ -81,8 +81,31 @@ public class Gala.DaemonManager : GLib.Object { } private void handle_daemon_window (Meta.Window window) { - window.move_frame (false, 0, 0); - window.make_above (); + var info = window.title.split ("-"); + + if (info.length == 0) { + critical ("Couldn't handle daemon window: No title provided"); + return; + } + + 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": + window.move_frame (false, 0, 0); + window.make_above (); + break; + } } private void lost_daemon () {