From 67bac412c9ebc9302a08a3dc2ded1633e980bc2d Mon Sep 17 00:00:00 2001 From: stilic Date: Thu, 10 Sep 2026 18:33:20 +0200 Subject: [PATCH 1/4] feat: layer-shell support --- README.md | 1 + meson.build | 21 +++++++++++++++------ src/PanelWindow.vala | 19 ++++++++++++++++++- src/Services/PopoverManager.vala | 19 +++++++++++++++++-- src/Widgets/Panel.vala | 2 ++ src/meson.build | 5 ++++- wingpanel-interface/meson.build | 1 - 7 files changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1abf7b1c..1868589b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ The extensible top panel for Pantheon. Wingpanel is an empty container that acce You'll need the following dependencies: +* libgtk4-layer-shell-dev >= 1.0.4 * libgala-dev >= 8.3.0 * libgee-0.8-dev * libglib2.0-dev diff --git a/meson.build b/meson.build index 57719c49..096a8c99 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,8 @@ i18n = import('i18n') gnome = import('gnome') pkg = import('pkgconfig') +gtkls_dep = dependency('gtk4-layer-shell-0') +gala_dep = dependency('gala', version: '>= 8.3.0', required: false) glib_dep = dependency('glib-2.0', version: '>=2.32') gio_dep = dependency('gio-2.0') gio_unix_dep = dependency('gio-unix-2.0') @@ -35,19 +37,26 @@ granite_dep = dependency('granite-7', version: '>=7.6.0') posix_dep = vala.find_library('posix') wl_client_dep = dependency('wayland-client') -subdir('protocol') subdir('data') subdir('lib') subdir('schemas') + +if gala_dep.found() + add_project_arguments('-D', 'USE_GALA', language: 'vala') + + subdir('protocol') + subdir('wingpanel-interface') + + vapigen = find_program('vapigen', required: false) + if vapigen.found() + subdir('vapi') + endif +endif + subdir('src') -subdir('wingpanel-interface') if get_option('example') subdir('sample') endif subdir('po') -vapigen = find_program('vapigen', required: false) -if vapigen.found() - subdir('vapi') -endif gnome.post_install(glib_compile_schemas: true) diff --git a/src/PanelWindow.vala b/src/PanelWindow.vala index 88c23192..06684696 100644 --- a/src/PanelWindow.vala +++ b/src/PanelWindow.vala @@ -29,14 +29,16 @@ public class Wingpanel.PanelWindow : Gtk.Window { private Services.PopoverManager popover_manager; private Widgets.Panel panel; + #if USE_GALA private Pantheon.Desktop.Shell? desktop_shell; private Pantheon.Desktop.Panel? desktop_panel; private bool initialized_background_manager = false; private Gtk.CssProvider? style_provider = null; + #endif construct { - popover_manager = new Services.PopoverManager (); + popover_manager = new Services.PopoverManager (this); panel = new Widgets.Panel (popover_manager); panel.realize.connect (on_realize); @@ -50,6 +52,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { decorated = false; resizable = false; + #if USE_GALA popover_manager.notify["indicator-open"].connect (() => { if (!popover_manager.indicator_open) { Services.BackgroundManager.get_default ().restore_window (); @@ -60,6 +63,14 @@ public class Wingpanel.PanelWindow : Gtk.Window { }); Services.BackgroundManager.get_default ().background_state_changed.connect (update_background); + #endif + + GtkLayerShell.init_for_window (this); + GtkLayerShell.set_layer (this, GtkLayerShell.Layer.TOP); + GtkLayerShell.auto_exclusive_zone_enable (this); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true); + GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true); } private void on_realize () { @@ -69,7 +80,9 @@ public class Wingpanel.PanelWindow : Gtk.Window { surface.enter_monitor.connect (on_enter_monitor); + #if USE_GALA init_wl (); + #endif } private static void on_compute_size (Gdk.ToplevelSize top_level_size) { @@ -83,10 +96,12 @@ public class Wingpanel.PanelWindow : Gtk.Window { }) ); + #if USE_GALA if (!initialized_background_manager) { Services.BackgroundManager.initialize (panel.get_height ()); initialized_background_manager = true; } + #endif } private void on_enter_monitor (Gdk.Monitor new_monitor) { @@ -101,6 +116,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { panel.toggle_indicator (name); } + #if USE_GALA public void registry_handle_global (Wl.Registry wl_registry, uint32 name, string @interface, uint32 version) { if (@interface == "io_elementary_pantheon_shell_v1") { desktop_shell = wl_registry.bind (name, ref Pantheon.Desktop.Shell.iface, uint32.min (version, 1)); @@ -188,6 +204,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { break; } } + #endif public override void measure ( Gtk.Orientation orientation, diff --git a/src/Services/PopoverManager.vala b/src/Services/PopoverManager.vala index 78bfe843..f478e94b 100644 --- a/src/Services/PopoverManager.vala +++ b/src/Services/PopoverManager.vala @@ -20,6 +20,7 @@ public class Wingpanel.Services.PopoverManager : Object { public bool indicator_open { get; private set; default = false; } + private unowned Wingpanel.PanelWindow? owner; private Gtk.Popover popover; private Wingpanel.Widgets.IndicatorEntry? _current_indicator = null; @@ -57,17 +58,31 @@ public class Wingpanel.Services.PopoverManager : Object { popover.child = _current_indicator.indicator_widget; _current_indicator.display_widget.has_tooltip = false; popover.set_parent (_current_indicator); + _current_indicator.base_indicator.opened (); + GtkLayerShell.set_keyboard_mode (owner, GtkLayerShell.KeyboardMode.ON_DEMAND); popover.popup (); _current_indicator.set_state_flags (CHECKED, true); - _current_indicator.base_indicator.opened (); + if (owner.is_active) { + _current_indicator.indicator_widget.child_focus (Gtk.DirectionType.TAB_FORWARD); + } else { + ulong handler_id = 0; + handler_id = owner.notify["is-active"].connect (() => { + if (owner.is_active && _current_indicator != null) { + _current_indicator.indicator_widget.child_focus (Gtk.DirectionType.TAB_FORWARD); + SignalHandler.disconnect (owner, handler_id); + } + }); + } } else { ((Widgets.IndicatorEntry)popover.parent).display_widget.has_tooltip = true; + GtkLayerShell.set_keyboard_mode (owner, GtkLayerShell.KeyboardMode.NONE); popover.popdown (); } } } - construct { + public PopoverManager (Wingpanel.PanelWindow? owner) { + this.owner = owner; popover = new Gtk.Popover () { has_arrow = false, position = BOTTOM diff --git a/src/Widgets/Panel.vala b/src/Widgets/Panel.vala index 6999e9c3..eafe760d 100644 --- a/src/Widgets/Panel.vala +++ b/src/Widgets/Panel.vala @@ -151,8 +151,10 @@ public class Wingpanel.Widgets.Panel : Granite.Bin { private void begin_drag (double x, double y) { popover_manager.current_indicator = null; + #if USE_GALA var background_manager = Services.BackgroundManager.get_default (); background_manager.begin_grab_focused_window ((int) x, (int) y); + #endif } private void cycle (Gtk.DirectionType direction) { diff --git a/src/meson.build b/src/meson.build index 876f50d4..96de8625 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,8 +16,11 @@ wingpanel_deps = [ gdk_wl_dep, posix_dep, wl_client_dep, - pantheon_desktop_shell_dep + gtkls_dep ] +if gala_dep.found() + wingpanel_deps += [pantheon_desktop_shell_dep] +endif executable(meson.project_name(), css_gresource, diff --git a/wingpanel-interface/meson.build b/wingpanel-interface/meson.build index fcda54d6..84cf1a4b 100644 --- a/wingpanel-interface/meson.build +++ b/wingpanel-interface/meson.build @@ -81,7 +81,6 @@ endif mutter_typelib_dir = libmutter_dep.get_variable('typelibdir') -gala_dep = dependency('gala', version: '>= 8.3.0') m_dep = meson.get_compiler('c').find_library('m', required: false) shared_module( From 80958cabb7f60556b26fd87369711b08d4c5acf4 Mon Sep 17 00:00:00 2001 From: stilic Date: Sat, 12 Sep 2026 21:51:56 +0200 Subject: [PATCH 2/4] reindent USE_GALA compile-time checks --- src/PanelWindow.vala | 20 ++++++++++---------- src/Widgets/Panel.vala | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/PanelWindow.vala b/src/PanelWindow.vala index 06684696..913eac40 100644 --- a/src/PanelWindow.vala +++ b/src/PanelWindow.vala @@ -29,13 +29,13 @@ public class Wingpanel.PanelWindow : Gtk.Window { private Services.PopoverManager popover_manager; private Widgets.Panel panel; - #if USE_GALA +#if USE_GALA private Pantheon.Desktop.Shell? desktop_shell; private Pantheon.Desktop.Panel? desktop_panel; private bool initialized_background_manager = false; private Gtk.CssProvider? style_provider = null; - #endif +#endif construct { popover_manager = new Services.PopoverManager (this); @@ -52,7 +52,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { decorated = false; resizable = false; - #if USE_GALA +#if USE_GALA popover_manager.notify["indicator-open"].connect (() => { if (!popover_manager.indicator_open) { Services.BackgroundManager.get_default ().restore_window (); @@ -63,7 +63,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { }); Services.BackgroundManager.get_default ().background_state_changed.connect (update_background); - #endif +#endif GtkLayerShell.init_for_window (this); GtkLayerShell.set_layer (this, GtkLayerShell.Layer.TOP); @@ -80,9 +80,9 @@ public class Wingpanel.PanelWindow : Gtk.Window { surface.enter_monitor.connect (on_enter_monitor); - #if USE_GALA +#if USE_GALA init_wl (); - #endif +#endif } private static void on_compute_size (Gdk.ToplevelSize top_level_size) { @@ -96,12 +96,12 @@ public class Wingpanel.PanelWindow : Gtk.Window { }) ); - #if USE_GALA +#if USE_GALA if (!initialized_background_manager) { Services.BackgroundManager.initialize (panel.get_height ()); initialized_background_manager = true; } - #endif +#endif } private void on_enter_monitor (Gdk.Monitor new_monitor) { @@ -116,7 +116,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { panel.toggle_indicator (name); } - #if USE_GALA +#if USE_GALA public void registry_handle_global (Wl.Registry wl_registry, uint32 name, string @interface, uint32 version) { if (@interface == "io_elementary_pantheon_shell_v1") { desktop_shell = wl_registry.bind (name, ref Pantheon.Desktop.Shell.iface, uint32.min (version, 1)); @@ -204,7 +204,7 @@ public class Wingpanel.PanelWindow : Gtk.Window { break; } } - #endif +#endif public override void measure ( Gtk.Orientation orientation, diff --git a/src/Widgets/Panel.vala b/src/Widgets/Panel.vala index eafe760d..5d46ef28 100644 --- a/src/Widgets/Panel.vala +++ b/src/Widgets/Panel.vala @@ -151,10 +151,10 @@ public class Wingpanel.Widgets.Panel : Granite.Bin { private void begin_drag (double x, double y) { popover_manager.current_indicator = null; - #if USE_GALA +#if USE_GALA var background_manager = Services.BackgroundManager.get_default (); background_manager.begin_grab_focused_window ((int) x, (int) y); - #endif +#endif } private void cycle (Gtk.DirectionType direction) { From d12cff84659242e2a1de1c595cd0c6a94a766f20 Mon Sep 17 00:00:00 2001 From: stilic Date: Sat, 12 Sep 2026 22:02:54 +0200 Subject: [PATCH 3/4] PopoverManager: switch to GObject style constructor --- src/Services/PopoverManager.vala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Services/PopoverManager.vala b/src/Services/PopoverManager.vala index f478e94b..11aabc3c 100644 --- a/src/Services/PopoverManager.vala +++ b/src/Services/PopoverManager.vala @@ -19,8 +19,8 @@ public class Wingpanel.Services.PopoverManager : Object { public bool indicator_open { get; private set; default = false; } + public unowned Wingpanel.PanelWindow owner { get; construct; } - private unowned Wingpanel.PanelWindow? owner; private Gtk.Popover popover; private Wingpanel.Widgets.IndicatorEntry? _current_indicator = null; @@ -81,8 +81,11 @@ public class Wingpanel.Services.PopoverManager : Object { } } - public PopoverManager (Wingpanel.PanelWindow? owner) { - this.owner = owner; + public PopoverManager (Wingpanel.PanelWindow owner) { + Object (owner: owner); + } + + construct { popover = new Gtk.Popover () { has_arrow = false, position = BOTTOM From 991cad9d1625419fd3e768f5f1c2ea94761706ac Mon Sep 17 00:00:00 2001 From: stilic Date: Sun, 13 Sep 2026 12:43:04 +0200 Subject: [PATCH 4/4] feat: make gtk4-layer-shell optional --- meson.build | 13 +++++++++---- meson_options.txt | 1 + src/PanelWindow.vala | 2 ++ src/Services/PopoverManager.vala | 4 ++++ src/meson.build | 6 ++++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 096a8c99..3ee0b666 100644 --- a/meson.build +++ b/meson.build @@ -24,8 +24,8 @@ i18n = import('i18n') gnome = import('gnome') pkg = import('pkgconfig') -gtkls_dep = dependency('gtk4-layer-shell-0') -gala_dep = dependency('gala', version: '>= 8.3.0', required: false) +gtkls_dep = dependency('gtk4-layer-shell-0', required: get_option('layer_shell')) +gala_dep = dependency('gala', version: '>= 8.3.0', required: not gtkls_dep.found()) glib_dep = dependency('glib-2.0', version: '>=2.32') gio_dep = dependency('gio-2.0') gio_unix_dep = dependency('gio-unix-2.0') @@ -37,13 +37,18 @@ granite_dep = dependency('granite-7', version: '>=7.6.0') posix_dep = vala.find_library('posix') wl_client_dep = dependency('wayland-client') +if gala_dep.found() + add_project_arguments('-D', 'USE_GALA', language: 'vala') +endif +if gtkls_dep.found() + add_project_arguments('-D', 'USE_LAYER_SHELL', language: 'vala') +endif + subdir('data') subdir('lib') subdir('schemas') if gala_dep.found() - add_project_arguments('-D', 'USE_GALA', language: 'vala') - subdir('protocol') subdir('wingpanel-interface') diff --git a/meson_options.txt b/meson_options.txt index a57e35b6..114343ef 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ +option('layer_shell', type: 'feature', value: 'auto', description: 'Build with wlr-layer-shell-unstable-v1 support') option('example', type: 'boolean', value: false, description: 'Build an example that shows how it works') diff --git a/src/PanelWindow.vala b/src/PanelWindow.vala index 913eac40..3f8f82db 100644 --- a/src/PanelWindow.vala +++ b/src/PanelWindow.vala @@ -65,12 +65,14 @@ public class Wingpanel.PanelWindow : Gtk.Window { Services.BackgroundManager.get_default ().background_state_changed.connect (update_background); #endif +#if USE_LAYER_SHELL GtkLayerShell.init_for_window (this); GtkLayerShell.set_layer (this, GtkLayerShell.Layer.TOP); GtkLayerShell.auto_exclusive_zone_enable (this); GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true); GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true); GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true); +#endif } private void on_realize () { diff --git a/src/Services/PopoverManager.vala b/src/Services/PopoverManager.vala index 11aabc3c..6f248e2f 100644 --- a/src/Services/PopoverManager.vala +++ b/src/Services/PopoverManager.vala @@ -59,7 +59,9 @@ public class Wingpanel.Services.PopoverManager : Object { _current_indicator.display_widget.has_tooltip = false; popover.set_parent (_current_indicator); _current_indicator.base_indicator.opened (); +#if USE_LAYER_SHELL GtkLayerShell.set_keyboard_mode (owner, GtkLayerShell.KeyboardMode.ON_DEMAND); +#endif popover.popup (); _current_indicator.set_state_flags (CHECKED, true); if (owner.is_active) { @@ -75,7 +77,9 @@ public class Wingpanel.Services.PopoverManager : Object { } } else { ((Widgets.IndicatorEntry)popover.parent).display_widget.has_tooltip = true; +#if USE_LAYER_SHELL GtkLayerShell.set_keyboard_mode (owner, GtkLayerShell.KeyboardMode.NONE); +#endif popover.popdown (); } } diff --git a/src/meson.build b/src/meson.build index 96de8625..c2dbfa2b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,9 +15,11 @@ wingpanel_deps = [ granite_dep, gdk_wl_dep, posix_dep, - wl_client_dep, - gtkls_dep + wl_client_dep ] +if gtkls_dep.found() + wingpanel_deps += [gtkls_dep] +endif if gala_dep.found() wingpanel_deps += [pantheon_desktop_shell_dep] endif