diff --git a/data/Indicator.css b/data/Indicator.css index 82bddbc8..6b64bd3b 100644 --- a/data/Indicator.css +++ b/data/Indicator.css @@ -3,26 +3,31 @@ * SPDX-FileCopyrightText: 2023 elementary, Inc. (https://elementary.io) */ -network .image-button.toggle { +network .image-button.toggle, +settings-toggle .image-button.toggle { border-radius: 1em; } -network .image-button { +network .image-button, +settings-toggle .image-button { border: none; box-shadow: none; min-height: 2.1666rem; /* 26px */ min-width: 2.1666rem; /* 26px */ } -network .image-button { +network .image-button, +settings-toggle .image-button { background: alpha(@text_color, 0.1); } -network .image-button:checked { +network.image-button:checked, +settings-toggle .image-button:checked { background: @selected_bg_color; color: @selected_fg_color; } -network .image-button:disabled { +network .image-button:disabled, +settings-toggle .image-button:disabled { background: @insensitive_bg_color; } diff --git a/meson.build b/meson.build index 33910c4c..ff6fd7cf 100644 --- a/meson.build +++ b/meson.build @@ -50,6 +50,7 @@ shared_module( 'src/Widgets/EtherInterface.vala', 'src/Widgets/ModemInterface.vala', 'src/Widgets/PopoverWidget.vala', + 'src/Widgets/SettingsToggle.vala', 'src/Widgets/VpnInterface.vala', 'src/Widgets/VpnMenuItem.vala', 'src/Widgets/WidgetNMInterface.vala', diff --git a/src/Indicator.vala b/src/Indicator.vala index 5337739a..a8ddeda5 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -23,6 +23,7 @@ public class Network.Indicator : Wingpanel.Indicator { NetworkMonitor network_monitor; private Gtk.GestureMultiPress gesture_click; + private SimpleAction airplane_action; public bool is_in_session { get; set; default = false; } @@ -60,20 +61,30 @@ public class Network.Indicator : Wingpanel.Indicator { }; gesture_click.pressed.connect (() => { - popover_widget.nm_client.dbus_call.begin ( - NM.DBUS_PATH, NM.DBUS_INTERFACE, - "Enable", new Variant.tuple ({new Variant.boolean (!popover_widget.nm_client.networking_get_enabled ())}), - null, -1, null, (obj, res) => { - try { - ((NM.Client) obj).dbus_set_property.end (res); - } catch (Error e) { - warning ("Error setting airplane mode: %s", e.message); - } - } - ); + airplane_action.activate (null); }); } + airplane_action = new SimpleAction.stateful ("airplane-mode", null, new Variant.boolean (popover_widget.nm_client.networking_get_enabled ())); + airplane_action.activate.connect (() => { + popover_widget.nm_client.dbus_call.begin ( + NM.DBUS_PATH, NM.DBUS_INTERFACE, + "Enable", new Variant.tuple ({new Variant.boolean (!popover_widget.nm_client.networking_get_enabled ())}), + null, -1, null, (obj, res) => { + try { + ((NM.Client) obj).dbus_set_property.end (res); + } catch (Error e) { + warning ("Error setting airplane mode: %s", e.message); + } + } + ); + }); + + var action_group = new SimpleActionGroup (); + action_group.add_action (airplane_action); + + popover_widget.insert_action_group ("network", action_group); + update_tooltip (); on_state_changed (); start_monitor (); @@ -93,6 +104,8 @@ public class Network.Indicator : Wingpanel.Indicator { display_widget.update_state (popover_widget.state, popover_widget.secure, popover_widget.extra_info); + airplane_action.set_state (new Variant.boolean (!popover_widget.nm_client.networking_get_enabled ())); + update_tooltip (); } diff --git a/src/Widgets/EtherInterface.vala b/src/Widgets/EtherInterface.vala index e1869425..c2d9022b 100644 --- a/src/Widgets/EtherInterface.vala +++ b/src/Widgets/EtherInterface.vala @@ -17,33 +17,26 @@ */ public class Network.EtherInterface : Network.WidgetNMInterface { - private Gtk.ToggleButton ethernet_item; + private SettingsToggle ethernet_item; + private SimpleAction toggle_ethernet_action; public EtherInterface (NM.Client nm_client, NM.Device? _device) { device = _device; - ethernet_item = new Gtk.ToggleButton () { - halign = Gtk.Align.CENTER, - image = new Gtk.Image.from_icon_name ("panel-network-wired-connected-symbolic", Gtk.IconSize.MENU) + ethernet_item = new SettingsToggle () { + action_name = "ethernet.toggle", + icon_name = "panel-network-wired-connected-symbolic", + settings_uri = "settings://network", + text = display_title }; - var label = new Gtk.Label (display_title) { - ellipsize = Pango.EllipsizeMode.MIDDLE, - max_width_chars = 16 - }; - label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); - - hexpand = true; - orientation = Gtk.Orientation.VERTICAL; - spacing = 3; add (ethernet_item); - add (label); - bind_property ("display-title", label, "label"); + bind_property ("display-title", ethernet_item, "text"); - ethernet_item.toggled.connect (() => { - debug ("update"); - if (ethernet_item.active && device.get_state () == NM.DeviceState.DISCONNECTED) { + toggle_ethernet_action = new SimpleAction.stateful ("toggle", null, new Variant.boolean (true)); + toggle_ethernet_action.activate.connect (() => { + if (device.get_state () == NM.DeviceState.DISCONNECTED) { var connection = NM.SimpleConnection.new (); var remote_array = device.get_available_connections (); if (remote_array == null) { @@ -52,13 +45,18 @@ public class Network.EtherInterface : Network.WidgetNMInterface { connection.set_path (remote_array.get (0).get_path ()); nm_client.activate_connection_async.begin (connection, device, null, null, null); } - } else if (!ethernet_item.active && device.get_state () == NM.DeviceState.ACTIVATED) { + } else if (device.get_state () == NM.DeviceState.ACTIVATED) { device.disconnect_async.begin (null, () => { debug ("Successfully disconnected."); }); } }); update (); device.state_changed.connect (update); + + var action_group = new SimpleActionGroup (); + action_group.add_action (toggle_ethernet_action); + + insert_action_group ("ethernet", action_group); } private void update () { @@ -67,23 +65,23 @@ public class Network.EtherInterface : Network.WidgetNMInterface { case NM.DeviceState.UNMANAGED: case NM.DeviceState.DEACTIVATING: case NM.DeviceState.FAILED: - sensitive = false; - ethernet_item.active = false; + toggle_ethernet_action.set_state (new Variant.boolean (false)); + toggle_ethernet_action.set_enabled (false); state = State.FAILED; - ((Gtk.Image ) ethernet_item.image).icon_name = "panel-network-wired-error-symbolic"; + ethernet_item.icon_name = "panel-network-wired-error-symbolic"; break; case NM.DeviceState.UNAVAILABLE: - sensitive = false; - ethernet_item.active = false; + toggle_ethernet_action.set_state (new Variant.boolean (false)); + toggle_ethernet_action.set_enabled (false); state = State.WIRED_UNPLUGGED; - ((Gtk.Image ) ethernet_item.image).icon_name = "panel-network-wired-no-route-symbolic"; + ethernet_item.icon_name = "panel-network-wired-no-route-symbolic"; break; case NM.DeviceState.DISCONNECTED: - sensitive = true; - ethernet_item.active = false; + toggle_ethernet_action.set_state (new Variant.boolean (false)); + toggle_ethernet_action.set_enabled (true); state = State.WIRED_UNPLUGGED; - ((Gtk.Image ) ethernet_item.image).icon_name = "panel-network-wired-offline-symbolic"; + ethernet_item.icon_name = "panel-network-wired-offline-symbolic"; break; case NM.DeviceState.PREPARE: @@ -92,17 +90,17 @@ public class Network.EtherInterface : Network.WidgetNMInterface { case NM.DeviceState.IP_CONFIG: case NM.DeviceState.IP_CHECK: case NM.DeviceState.SECONDARIES: - sensitive = true; - ethernet_item.active = true; + toggle_ethernet_action.set_enabled (true); + toggle_ethernet_action.set_state (new Variant.boolean (true)); state = State.CONNECTING_WIRED; - ((Gtk.Image ) ethernet_item.image).icon_name = "panel-network-wired-acquiring-symbolic"; + ethernet_item.icon_name = "panel-network-wired-acquiring-symbolic"; break; case NM.DeviceState.ACTIVATED: - sensitive = true; - ethernet_item.active = true; + toggle_ethernet_action.set_enabled (true); + toggle_ethernet_action.set_state (new Variant.boolean (true)); state = State.CONNECTED_WIRED; - ((Gtk.Image ) ethernet_item.image).icon_name = "panel-network-wired-connected-symbolic-symbolic"; + ethernet_item.icon_name = "panel-network-wired-connected-symbolic-symbolic"; break; } } diff --git a/src/Widgets/PopoverWidget.vala b/src/Widgets/PopoverWidget.vala index 44fe38c0..4e216d9d 100644 --- a/src/Widgets/PopoverWidget.vala +++ b/src/Widgets/PopoverWidget.vala @@ -56,43 +56,20 @@ public class Network.Widgets.PopoverWidget : Gtk.Box { } if (is_in_session) { - var airplane_toggle = new Gtk.ToggleButton () { - halign = Gtk.Align.CENTER, - image = new Gtk.Image.from_icon_name ("airplane-mode-symbolic", Gtk.IconSize.MENU) + var airplane_toggle = new SettingsToggle () { + action_name = "network.airplane-mode", + icon_name = "airplane-mode-symbolic", + settings_uri = "settings://network", + text = _("Airplane Mode") }; - var airplane_label = new Gtk.Label (_("Airplane Mode")); - airplane_label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); - - var airplane_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 3); - airplane_box.add (airplane_toggle); - airplane_box.add (airplane_label); - var airplane_child = new Gtk.FlowBoxChild () { // Prevent weird double focus border can_focus = false, - child = airplane_box + child = airplane_toggle }; other_box.add (airplane_child); - - airplane_toggle.toggled.connect (() => { - nm_client.dbus_call.begin ( - NM.DBUS_PATH, NM.DBUS_INTERFACE, - "Enable", new Variant.tuple ({new Variant.boolean (!airplane_toggle.active)}), - null, -1, null, (obj, res) => { - try { - ((NM.Client) obj).dbus_set_property.end (res); - } catch (Error e) { - warning ("Error setting airplane mode: %s", e.message); - } - } - ); - }); - - if (!airplane_toggle.active && !nm_client.networking_get_enabled ()) { - airplane_toggle.activate (); - } } var other_sep = new Gtk.Separator (Gtk.Orientation.HORIZONTAL) { diff --git a/src/Widgets/SettingsToggle.vala b/src/Widgets/SettingsToggle.vala new file mode 100644 index 00000000..8be10f3b --- /dev/null +++ b/src/Widgets/SettingsToggle.vala @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2023 elementary, Inc. (https://elementary.io) + */ + +public class Network.SettingsToggle : Gtk.Box { + public string action_name { get; set; } + public string icon_name { get; set; } + public string text { get; set; } + public string settings_uri { get; set; default = "settings://"; } + + private Gtk.GestureMultiPress middle_click_gesture; + + class construct { + set_css_name ("settings-toggle"); + } + + construct { + var image = new Gtk.Image (); + + var button = new Gtk.ToggleButton () { + halign = CENTER, + image = image + }; + + var label = new Gtk.Label (null) { + ellipsize = MIDDLE, + justify = CENTER, + lines = 2, + max_width_chars = 13, + mnemonic_widget = button + }; + label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + + halign = CENTER; + orientation = VERTICAL; + spacing = 3; + add (button); + add (label); + + bind_property ("action-name", button, "action-name"); + bind_property ("icon-name", image, "icon-name"); + bind_property ("text", label, "label"); + + middle_click_gesture = new Gtk.GestureMultiPress (this) { + button = Gdk.BUTTON_MIDDLE + }; + middle_click_gesture.pressed.connect (() => { + try { + AppInfo.launch_default_for_uri (settings_uri, null); + + var popover = (Gtk.Popover) get_ancestor (typeof (Gtk.Popover)); + popover.popdown (); + } catch (Error e) { + critical ("Failed to open system settings: %s", e.message); + } + }); + } +} diff --git a/src/Widgets/VpnMenuItem.vala b/src/Widgets/VpnMenuItem.vala index be11a447..32fcaf4b 100644 --- a/src/Widgets/VpnMenuItem.vala +++ b/src/Widgets/VpnMenuItem.vala @@ -36,53 +36,47 @@ public class Network.VpnMenuItem : Gtk.FlowBoxChild { _vpn_connection = null; } - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-disconnected-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); } } - private Gtk.ToggleButton toggle_button; + private SettingsToggle toggle_button; + private SimpleAction toggle_action; public VpnMenuItem (NM.RemoteConnection remote_connection) { Object (remote_connection: remote_connection); } construct { - toggle_button = new Gtk.ToggleButton () { - halign = Gtk.Align.CENTER, - image = new Gtk.Image.from_icon_name ("panel-network-vpn-disconnected-symbolic", Gtk.IconSize.MENU) + toggle_button = new SettingsToggle () { + action_name = "vpn.toggle", + hexpand = true, + icon_name = "panel-network-vpn-disconnected-symbolic", + settings_uri = "settings://network/vpn", + text = remote_connection.get_id () }; - var label = new Gtk.Label (remote_connection.get_id ()) { - ellipsize = Pango.EllipsizeMode.MIDDLE, - max_width_chars = 16 - }; - label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + can_focus = false; + child = toggle_button; - var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 3) { - hexpand = true - }; - box.add (toggle_button); - box.add (label); + toggle_action = new SimpleAction.stateful ("toggle", null, new Variant.boolean (false)); + toggle_action.activate.connect (() => activate ()); - can_focus = false; - add (box); + var action_group = new SimpleActionGroup (); + action_group.add_action (toggle_action); - // We can't use clicked because we get in a weird loop state - toggle_button.button_release_event.connect ((b, ev) => { - activate (); - return Gdk.EVENT_STOP; - }); + insert_action_group ("vpn", action_group); remote_connection.changed.connect (() => { - label.label = remote_connection.get_id (); + toggle_button.text = remote_connection.get_id (); }); } private void update_state () { if (_vpn_connection == null) { - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-disconnected-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); return; } unowned string connection_type = _vpn_connection.get_connection_type (); @@ -92,39 +86,41 @@ public class Network.VpnMenuItem : Gtk.FlowBoxChild { case NM.VpnConnectionState.IP_CONFIG_GET: case NM.VpnConnectionState.NEED_AUTH: case NM.VpnConnectionState.PREPARE: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-acquiring-symbolic"; + toggle_button.icon_name = "panel-network-vpn-acquiring-symbolic"; + toggle_action.set_state (new Variant.boolean (true)); break; case NM.VpnConnectionState.ACTIVATED: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-connected-symbolic"; - toggle_button.active = true; + toggle_button.icon_name = "panel-network-vpn-connected-symbolic"; + toggle_action.set_state (new Variant.boolean (true)); break; case NM.VpnConnectionState.DISCONNECTED: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-disconnected-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); break; case NM.VpnConnectionState.FAILED: case NM.VpnConnectionState.UNKNOWN: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-error-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-error-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); break; } } else if (connection_type == NM.SettingWireGuard.SETTING_NAME) { switch (_vpn_connection.get_state ()) { case NM.ActiveConnectionState.UNKNOWN: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-error-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-error-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); break; case NM.ActiveConnectionState.DEACTIVATED: case NM.ActiveConnectionState.DEACTIVATING: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic"; - toggle_button.active = false; + toggle_button.icon_name = "panel-network-vpn-disconnected-symbolic"; + toggle_action.set_state (new Variant.boolean (false)); break; case NM.ActiveConnectionState.ACTIVATING: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-acquiring-symbolic"; + toggle_button.icon_name = "panel-network-vpn-acquiring-symbolic"; + toggle_action.set_state (new Variant.boolean (true)); break; case NM.ActiveConnectionState.ACTIVATED: - ((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-connected-symbolic"; - toggle_button.active = true; + toggle_button.icon_name = "panel-network-vpn-connected-symbolic"; + toggle_action.set_state (new Variant.boolean (true)); break; } }