diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 988f08f3..6ba423c9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: - name: Install Dependencies run: | apt update - apt install -y libgee-0.8-dev libglib2.0-dev libgranite-7-dev libgtk-4-dev libadwaita-1-dev meson sassc valac + apt install -y libadwaita-1-dev libgee-0.8-dev libglib2.0-dev libgranite-7-dev libgtk-4-dev libpantheon-wayland-1-dev meson sassc valac - name: Build run: | meson build -Dexample=true diff --git a/README.md b/README.md index 4377ec42..a6495700 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ Switchboard is just the container application for Switchboard Plugs, which provi You'll need the following dependencies: +* libadwaita-1-dev (>= 1.4) * libgee-0.8-dev * libglib2.0-dev * libgranite-7-dev * libgtk-4-dev -* libadwaita-1-dev (>= 1.4) +* libpantheon-wayland-1-dev * meson (>= 0.57.0) * sassc * valac diff --git a/meson.build b/meson.build index 153d0cc6..11d58e23 100644 --- a/meson.build +++ b/meson.build @@ -29,6 +29,7 @@ gnome = import('gnome') i18n = import('i18n') pkg = import('pkgconfig') +adwaita_dep = dependency('libadwaita-1', version: '>=1.4') glib_dep = dependency('glib-2.0', version: '>=2.32') gio_dep = dependency('gio-2.0') gio_unix_dep = dependency('gio-unix-2.0') @@ -36,7 +37,7 @@ gmodule_dep = dependency('gmodule-2.0') gtk_dep = dependency('gtk4', version: '>=3.10') gee_dep = dependency('gee-0.8') granite_dep = dependency('granite-7', version: '>=7.0.0') -adwaita_dep = dependency('libadwaita-1', version: '>=1.4') +pantheon_wayland_dep = dependency('pantheon-wayland-1') m_dep = meson.get_compiler('c').find_library('m', required : false) subdir('data') diff --git a/src/Application.vala b/src/Application.vala index 0a14804b..b959910c 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -1,5 +1,5 @@ /* -* Copyright 2011-2021 elementary, Inc. (https://elementary.io) +* Copyright 2011-2025 elementary, Inc. (https://elementary.io) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,15 +21,9 @@ namespace Switchboard { public class SwitchboardApp : Gtk.Application { - private GLib.HashTable plug_widgets; - private Adw.NavigationView navigation_view; - private Gtk.Window main_window; - private Switchboard.CategoryView category_view; + private MainWindow main_window; - private static bool opened_directly = false; private static string? link = null; - private static string? open_window = null; - private static string? plug_to_open = null; construct { application_id = "io.elementary.settings"; @@ -66,6 +60,7 @@ namespace Switchboard { } activate (); + ((PantheonWayland.ExtendedBehavior) main_window).focus (); } public override void startup () { @@ -82,19 +77,20 @@ namespace Switchboard { gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; }); - var back_action = new SimpleAction ("back", null); var quit_action = new SimpleAction ("quit", null); - - add_action (back_action); - add_action (quit_action); - - set_accels_for_action ("app.quit", {"q"}); - - back_action.activate.connect (action_navigate_back); quit_action.activate.connect (quit); + set_accels_for_action ("app.quit", {"q"}); + add_action (quit_action); } public override void activate () { + if (main_window == null) { + main_window = new MainWindow (); + add_window (main_window); + } + + main_window.present (); + var plugsmanager = Switchboard.PlugsManager.get_default (); if (link != null) { bool plug_found = load_setting_path (link, plugsmanager); @@ -103,132 +99,11 @@ namespace Switchboard { link = null; // If plug_to_open was set from the command line - opened_directly = true; + main_window.opened_directly = true; } else { warning (_("Specified link '%s' does not exist, going back to the main panel").printf (link)); } - } else if (plug_to_open != null) { - foreach (var plug in plugsmanager.get_plugs ()) { - if (plug_to_open.has_suffix (plug.code_name)) { - load_plug (plug); - plug_to_open = null; - - // If plug_to_open was set from the command line - opened_directly = true; - break; - } - } - } - - // If app is already running, present the current window. - if (get_windows ().length () > 0) { - get_windows ().data.present (); - return; - } - - plug_widgets = new GLib.HashTable (null, null); - - category_view = new Switchboard.CategoryView (plug_to_open); - - navigation_view = new Adw.NavigationView () { - pop_on_escape = false - }; - navigation_view.add (category_view); - - main_window = new Gtk.Window () { - application = this, - child = navigation_view, - height_request = 500, - icon_name = application_id, - title = _("System Settings"), - titlebar = new Gtk.Grid () { visible = false } - }; - add_window (main_window); - main_window.present (); - - /* - * This is very finicky. Bind size after present else set_titlebar gives us bad sizes - * Set maximize after height/width else window is min size on unmaximize - * Bind maximize as SET else get get bad sizes - */ - var settings = new Settings ("io.elementary.settings"); - settings.bind ("window-height", main_window, "default-height", SettingsBindFlags.DEFAULT); - settings.bind ("window-width", main_window, "default-width", SettingsBindFlags.DEFAULT); - - if (settings.get_boolean ("window-maximized")) { - main_window.maximize (); - } - - settings.bind ("window-maximized", main_window, "maximized", SettingsBindFlags.SET); - - shutdown.connect (() => { - navigation_view.visible_page.hidden (); - }); - - navigation_view.popped.connect (update_navigation); - navigation_view.pushed.connect (update_navigation); - } - - private void update_navigation () { - main_window.title = navigation_view.visible_page.title; - } - - public void load_plug (Switchboard.Plug plug) { - Idle.add (() => { - var plug_widget = plug.get_widget (); - if (plug_widget.parent == null) { - var navigation_page = new Adw.NavigationPage (plug_widget, plug.display_name); - navigation_page.hidden.connect (plug.hidden); - navigation_page.shown.connect (plug.shown); - - navigation_view.add (navigation_page); - } - - if (plug_widgets[plug_widget] == null) { - plug_widgets[plug_widget] = plug; - } - - category_view.plug_search_result.foreach ((entry) => { - if (plug.display_name == entry.plug_name) { - if (entry.open_window == null) { - plug.search_callback (""); // open default in the switch - } else { - plug.search_callback (entry.open_window); - } - debug ("open section:%s of plug: %s", entry.open_window, plug.display_name); - return true; - } - - return false; - }); - - // open window was set by command line argument - if (open_window != null) { - plug.search_callback (open_window); - open_window = null; - } - - if (opened_directly) { - navigation_view.animate_transitions = false; - opened_directly = false; - } else if (navigation_view.animate_transitions == false) { - navigation_view.animate_transitions = true; - } - - navigation_view.push ((Adw.NavigationPage) plug.get_widget ().parent); - - return false; - }, GLib.Priority.DEFAULT_IDLE); - } - - // Handles clicking the navigation button - private void action_navigate_back () { - if (navigation_view.get_previous_page (navigation_view.visible_page) == category_view) { - opened_directly = false; - navigation_view.animate_transitions = true; } - - navigation_view.pop (); } // Try to find a supported plug, fallback paths like "foo/bar" to "foo" @@ -240,8 +115,8 @@ namespace Switchboard { } if (supported_settings.has_key (setting_path)) { - load_plug (plug); - open_window = supported_settings.get (setting_path); + main_window.load_plug (plug); + main_window.open_window = supported_settings.get (setting_path); return true; } } diff --git a/src/CategoryView.vala b/src/CategoryView.vala index 31dc5d20..63187bfa 100644 --- a/src/CategoryView.vala +++ b/src/CategoryView.vala @@ -7,7 +7,6 @@ public class Switchboard.CategoryView : Adw.NavigationPage { public Gee.ArrayList plug_search_result { get; private set; } - public string? plug_to_open { get; construct set; default = null; } private Gtk.SearchEntry search_box; private Gtk.Stack stack; @@ -149,10 +148,6 @@ public class Switchboard.CategoryView : Adw.NavigationPage { add_controller (eventcontrollerkey); } - public CategoryView (string? plug = null) { - Object (plug_to_open: plug); - } - public async void load_default_plugs () { var plugsmanager = Switchboard.PlugsManager.get_default (); plugsmanager.plug_added.connect ((plug) => { @@ -205,12 +200,6 @@ public class Switchboard.CategoryView : Adw.NavigationPage { if (any_found) { stack.visible_child_name = "category-grid"; } - - if (plug_to_open != null && plug_to_open.has_suffix (plug.code_name)) { - unowned var app = (SwitchboardApp) GLib.Application.get_default (); - app.load_plug (plug); - plug_to_open = null; - } } public static string? get_category_name (Switchboard.Plug.Category category) { diff --git a/src/MainWindow.vala b/src/MainWindow.vala new file mode 100644 index 00000000..25ee231a --- /dev/null +++ b/src/MainWindow.vala @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + */ + +public class Switchboard.MainWindow : Gtk.ApplicationWindow, PantheonWayland.ExtendedBehavior { + public string? open_window { get; set; default = null; } + public bool opened_directly { get; set; default = false; } + + private GLib.HashTable plug_widgets; + private CategoryView category_view; + private Adw.NavigationView navigation_view; + + construct { + plug_widgets = new GLib.HashTable (null, null); + + category_view = new Switchboard.CategoryView (); + + navigation_view = new Adw.NavigationView () { + pop_on_escape = false + }; + navigation_view.add (category_view); + + height_request = 500; + title = _("System Settings"); + titlebar = new Gtk.Grid () { visible = false }; + child = navigation_view; + + navigation_view.popped.connect (update_navigation); + navigation_view.pushed.connect (update_navigation); + + /* + * This is very finicky. Bind size after present else set_titlebar gives us bad sizes + * Set maximize after height/width else window is min size on unmaximize + * Bind maximize as SET else get get bad sizes + */ + var settings = new Settings ("io.elementary.settings"); + settings.bind ("window-height", this, "default-height", SettingsBindFlags.DEFAULT); + settings.bind ("window-width", this, "default-width", SettingsBindFlags.DEFAULT); + + if (settings.get_boolean ("window-maximized")) { + maximize (); + } + + settings.bind ("window-maximized", this, "maximized", SettingsBindFlags.SET); + + child.realize.connect (connect_to_shell); + } + + private void update_navigation () { + title = navigation_view.visible_page.title; + } + + public void load_plug (Switchboard.Plug plug) { + Idle.add (() => { + var plug_widget = plug.get_widget (); + if (plug_widget.parent == null) { + var navigation_page = new Adw.NavigationPage (plug_widget, plug.display_name); + navigation_page.hidden.connect (plug.hidden); + navigation_page.shown.connect (plug.shown); + + navigation_view.add (navigation_page); + } + + if (plug_widgets[plug_widget] == null) { + plug_widgets[plug_widget] = plug; + } + + category_view.plug_search_result.foreach ((entry) => { + if (plug.display_name == entry.plug_name) { + if (entry.open_window == null) { + plug.search_callback (""); // open default in the switch + } else { + plug.search_callback (entry.open_window); + } + debug ("open section:%s of plug: %s", entry.open_window, plug.display_name); + return true; + } + + return false; + }); + + // open window was set by command line argument + if (open_window != null) { + plug.search_callback (open_window); + open_window = null; + } + + if (opened_directly) { + navigation_view.animate_transitions = false; + opened_directly = false; + } else if (navigation_view.animate_transitions == false) { + navigation_view.animate_transitions = true; + } + + navigation_view.push ((Adw.NavigationPage) plug.get_widget ().parent); + + return Source.REMOVE; + }); + } +} diff --git a/src/Widgets/CategoryFlowBox.vala b/src/Widgets/CategoryFlowBox.vala index 04b2d025..5248cbda 100644 --- a/src/Widgets/CategoryFlowBox.vala +++ b/src/Widgets/CategoryFlowBox.vala @@ -48,7 +48,7 @@ namespace Switchboard { attach (flowbox, 0, 1, 2, 1); flowbox.child_activated.connect ((child) => { - ((SwitchboardApp) GLib.Application.get_default ()).load_plug (((CategoryIcon) child).plug); + ((MainWindow) get_root ()).load_plug (((CategoryIcon) child).plug); }); flowbox.set_sort_func (plug_sort_func); diff --git a/src/meson.build b/src/meson.build index bdc4873b..c9facb40 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,15 +2,17 @@ switchboard_files = files( 'Application.vala', 'PlugsSearch.vala', 'CategoryView.vala', + 'MainWindow.vala', 'SearchView.vala', 'Widgets/CategoryIcon.vala', 'Widgets/CategoryFlowBox.vala', ) switchboard_deps = [ + adwaita_dep, libswitchboard_dep, granite_dep, - adwaita_dep, + pantheon_wayland_dep, m_dep ]