diff --git a/data/indicator.css b/data/indicator.css index 91ba2839..883bf993 100644 --- a/data/indicator.css +++ b/data/indicator.css @@ -30,7 +30,7 @@ 100% { -gtk-icon-source: -gtk-icontheme("indicator-microphone-muted-symbolic"); } } -menuitem .image-button, +device-manager .image-button, player-row .image-button { background: alpha(@text_color, 0.1); border: none; @@ -39,18 +39,18 @@ player-row .image-button { min-width: 2.1666rem; /* 26px */ } -menuitem .image-button.toggle, +device-manager .image-button.toggle, player-row .image-button.toggle { border-radius: 1em; } -menuitem .image-button:checked, +device-manager .image-button:checked, player-row .image-button:checked { background: @selected_bg_color; color: @selected_fg_color; } -menuitem .image-button:disabled, +device-manager .image-button:disabled, player-row .image-button:disabled { background: @insensitive_bg_color; } diff --git a/meson.build b/meson.build index 7e49b012..509addfc 100644 --- a/meson.build +++ b/meson.build @@ -49,7 +49,6 @@ shared_module( 'src/Widgets/Scale.vala', 'src/Widgets/PlayerRow.vala', 'src/Widgets/PlayerList.vala', - 'src/Widgets/DeviceManagerWidget.vala', 'src/Widgets/DeviceItem.vala', 'src/Services/PulseAudioManager.vala', 'src/Services/MprisClient.vala', diff --git a/src/Indicator.vala b/src/Indicator.vala index 07f49ea7..5bb2cda8 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -28,8 +28,6 @@ public class Sound.Indicator : Wingpanel.Indicator { private Widgets.PlayerList mpris; private Widgets.Scale volume_scale; private Widgets.Scale mic_scale; - private Widgets.DeviceManagerWidget output_device_manager; - private Widgets.DeviceManagerWidget input_device_manager; private Gtk.Separator mic_separator; private Notify.Notification? notification; private Services.VolumeControlPulse volume_control; @@ -103,13 +101,6 @@ public class Sound.Indicator : Wingpanel.Indicator { volume_control.notify["volume"].connect (update_tooltip); volume_control.notify["mute"].connect (update_tooltip); - output_device_manager = new Widgets.DeviceManagerWidget () { - direction = OUTPUT - }; - input_device_manager = new Widgets.DeviceManagerWidget () { - direction = INPUT - }; - Notify.init ("wingpanel-indicator-sound"); settings.notify["max-volume"].connect (set_max_volume); @@ -127,9 +118,13 @@ public class Sound.Indicator : Wingpanel.Indicator { volume_adjustment = new Gtk.Adjustment (0, 0, max_volume, 0.01, 0, 0); mic_adjustment = new Gtk.Adjustment (0, 0, 1, 0.01, 0, 0); - volume_scale = new Widgets.Scale ("audio-volume-high-symbolic", volume_adjustment); + volume_scale = new Widgets.Scale ("audio-volume-high-symbolic", volume_adjustment) { + direction = OUTPUT + }; - mic_scale = new Widgets.Scale ("indicator-microphone-symbolic", mic_adjustment); + mic_scale = new Widgets.Scale ("indicator-microphone-symbolic", mic_adjustment) { + direction = INPUT + }; ca_context = CanberraGtk.context_get (); ca_context.change_props (Canberra.PROP_APPLICATION_NAME, "indicator-sound", @@ -357,16 +352,12 @@ public class Sound.Indicator : Wingpanel.Indicator { mic_scale.show_all (); mic_separator.no_show_all = false; mic_separator.show (); - input_device_manager.no_show_all = false; - input_device_manager.show (); display_widget.show_mic = true; } else { mic_scale.no_show_all = true; mic_scale.hide (); mic_separator.no_show_all = true; mic_separator.hide (); - input_device_manager.no_show_all = true; - input_device_manager.hide (); display_widget.show_mic = false; } } @@ -431,11 +422,9 @@ public class Sound.Indicator : Wingpanel.Indicator { main_box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); } main_box.add (volume_scale); - main_box.add (output_device_manager); if (is_in_session) { main_box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); main_box.add (mic_scale); - main_box.add (input_device_manager); main_box.add (mic_separator); main_box.add (settings_button); } diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala deleted file mode 100644 index 143a7055..00000000 --- a/src/Widgets/DeviceManagerWidget.vala +++ /dev/null @@ -1,135 +0,0 @@ -// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*- -/*- - * Copyright 2021 elementary, Inc. (https://elementary.io) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * Authored by: Sean Davis - */ - -public class Sound.Widgets.DeviceManagerWidget : Gtk.Box { - public PulseAudio.Direction direction { get; set; } - - private Gtk.ListBox device_list; - private Gtk.Revealer devices_revealer; - - private unowned PulseAudioManager pam; - - construct { - pam = PulseAudioManager.get_default (); - pam.new_device.connect (add_device); - pam.notify["default-output"].connect (default_output_changed); - pam.notify["default-input"].connect (default_input_changed); - pam.start (); - - device_list = new Gtk.ListBox () { - activate_on_single_click = true, - visible = true - }; - - var scrolled_box = new Gtk.ScrolledWindow (null, null) { - child = device_list, - hscrollbar_policy = Gtk.PolicyType.NEVER, - propagate_natural_height = true, - max_content_height = 256, - margin_bottom = 3 - }; - - devices_revealer = new Gtk.Revealer () { - child = scrolled_box - }; - - add (devices_revealer); - - update_showable (); - } - - private void add_device (Device device) { - // Avoid Built-in Analog input - output devices - if (device.direction != direction || device.port_name == "analog-output" || device.port_name == "analog-input") { - return; - } - - Gtk.ListBoxRow? row = device_list.get_row_at_index (0); - var device_item = new DeviceItem (device, row); - device_list.add (device_item); - - device_item.activated.connect (() => { - pam.set_default_device.begin (device); - update_preferred_devices (device); - }); - - device.removed.connect (() => { - device_list.remove (device_item); - device_list.show_all (); - update_showable (); - }); - - device.defaulted.connect (() => { - device_item.set_default (); - update_preferred_devices (device); - update_showable (); - }); - - update_showable (); - } - - /** - * Preferred devices are stored as: - * { - * device_a: last_used_unix_timestamp, - * device_b: last_used_unix_timestamp - * } - * If a device hasn't been selected in 7 days, it is removed from preferred devices. - * Device selection happens when the user selects the device, and when the plugin - * is initialized. - */ - private void update_preferred_devices (Device device) { - VariantBuilder builder = new VariantBuilder (new VariantType ("a{si}")); - var preferred_devices = Sound.Indicator.settings.get_value ("preferred-devices"); - int32 now = (int32) (GLib.get_real_time () / 1000000); - int32 preferred_expiry = now - (86400 * 7); // Expire unused after 7 days - - builder.add ("{si}", device.id, now); - foreach (var dev in preferred_devices) { - var name = dev.get_child_value (0).get_string (); - var last_used = dev.get_child_value (1).get_int32 (); - if (name == device.id) { - continue; - } - if (last_used < preferred_expiry) { - continue; - } - builder.add ("{si}", name, last_used); - } - Variant dictionary = builder.end (); - - device.is_priority = true; - Sound.Indicator.settings.set_value ("preferred-devices", dictionary); - } - - private void update_showable () { - devices_revealer.reveal_child = device_list.get_row_at_index (1) != null; - } - - private void default_output_changed () { - pam.default_output.defaulted (); - } - - private void default_input_changed () { - pam.default_input.defaulted (); - } -} diff --git a/src/Widgets/Scale.vala b/src/Widgets/Scale.vala index 53df6904..2fff9c1d 100644 --- a/src/Widgets/Scale.vala +++ b/src/Widgets/Scale.vala @@ -1,5 +1,5 @@ /* -* Copyright 2015-2021 elementary, Inc. (https://elementary.io) +* Copyright 2015-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 General Public @@ -13,15 +13,23 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . +* +* Authored by: Sean Davis */ public class Sound.Widgets.Scale : Gtk.EventBox { + public bool active { get; set; default = true; } + public PulseAudio.Direction direction { get; set; } + public Gtk.Adjustment adjustment { get; construct; } public string icon { get; construct set; } - public bool active { get; set; default = true; } public Gtk.Scale scale_widget { get; private set; } + private Gtk.ListBox device_list; + private Gtk.Revealer devices_revealer; + private unowned PulseAudioManager pam; + public Scale (string icon, Gtk.Adjustment adjustment) { Object ( icon: icon, @@ -30,7 +38,7 @@ public class Sound.Widgets.Scale : Gtk.EventBox { } class construct { - set_css_name (Gtk.STYLE_CLASS_MENUITEM); + set_css_name ("device-manager"); } construct { @@ -45,20 +53,49 @@ public class Sound.Widgets.Scale : Gtk.EventBox { width_request = 175 }; - var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12) { + var scale_box = new Gtk.Box (HORIZONTAL, 12) { hexpand = true, margin_top = 6, margin_start = 6, margin_bottom = 6, margin_end = 12 }; - box.add (toggle); - box.add (scale_widget); + scale_box.add (toggle); + scale_box.add (scale_widget); + + pam = PulseAudioManager.get_default (); + pam.new_device.connect (add_device); + pam.notify["default-output"].connect (default_output_changed); + pam.notify["default-input"].connect (default_input_changed); + pam.start (); + + device_list = new Gtk.ListBox () { + activate_on_single_click = true, + visible = true + }; + + var scrolled_box = new Gtk.ScrolledWindow (null, null) { + child = device_list, + hscrollbar_policy = Gtk.PolicyType.NEVER, + propagate_natural_height = true, + max_content_height = 256, + margin_bottom = 3 + }; + + devices_revealer = new Gtk.Revealer () { + child = scrolled_box + }; - add (box); + var box = new Gtk.Box (VERTICAL, 0); + box.add (scale_box); + box.add (devices_revealer); + + child = box; add_events (Gdk.EventMask.SMOOTH_SCROLL_MASK); above_child = false; + update_showable (); + scale_widget.scroll_event.connect ((e) => { /* Re-emit the signal on the eventbox instead of using native handler */ scroll_event (e); @@ -79,4 +116,80 @@ public class Sound.Widgets.Scale : Gtk.EventBox { return true; }, null); } + + private void add_device (Device device) { + // Avoid Built-in Analog input - output devices + if (device.direction != direction || device.port_name == "analog-output" || device.port_name == "analog-input") { + return; + } + + Gtk.ListBoxRow? row = device_list.get_row_at_index (0); + var device_item = new DeviceItem (device, row); + device_list.add (device_item); + + device_item.activated.connect (() => { + pam.set_default_device.begin (device); + update_preferred_devices (device); + }); + + device.removed.connect (() => { + device_list.remove (device_item); + device_list.show_all (); + update_showable (); + }); + + device.defaulted.connect (() => { + device_item.set_default (); + update_preferred_devices (device); + update_showable (); + }); + + update_showable (); + } + + /** + * Preferred devices are stored as: + * { + * device_a: last_used_unix_timestamp, + * device_b: last_used_unix_timestamp + * } + * If a device hasn't been selected in 7 days, it is removed from preferred devices. + * Device selection happens when the user selects the device, and when the plugin + * is initialized. + */ + private void update_preferred_devices (Device device) { + VariantBuilder builder = new VariantBuilder (new VariantType ("a{si}")); + var preferred_devices = Sound.Indicator.settings.get_value ("preferred-devices"); + int32 now = (int32) (GLib.get_real_time () / 1000000); + int32 preferred_expiry = now - (86400 * 7); // Expire unused after 7 days + + builder.add ("{si}", device.id, now); + foreach (var dev in preferred_devices) { + var name = dev.get_child_value (0).get_string (); + var last_used = dev.get_child_value (1).get_int32 (); + if (name == device.id) { + continue; + } + if (last_used < preferred_expiry) { + continue; + } + builder.add ("{si}", name, last_used); + } + Variant dictionary = builder.end (); + + device.is_priority = true; + Sound.Indicator.settings.set_value ("preferred-devices", dictionary); + } + + private void update_showable () { + devices_revealer.reveal_child = device_list.get_row_at_index (1) != null; + } + + private void default_output_changed () { + pam.default_output.defaulted (); + } + + private void default_input_changed () { + pam.default_input.defaulted (); + } }