diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c41d1463..1d1166ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: - name: Install Dependencies run: | apt update - apt install -y libcanberra-gtk3-dev libgranite-dev libglib2.0-dev libgtk-3-dev libnotify-dev libpulse-dev libwingpanel-dev libxml2-utils meson valac + apt install -y libgranite-7-dev libglib2.0-dev libgtk-4-dev libnotify-dev libpulse-dev libwingpanel-9-dev libxml2-utils meson valac - name: Build env: DESTDIR: out diff --git a/README.md b/README.md index 09c7fa65..9c262485 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,12 @@ You'll need the following dependencies: - libcanberra-gtk-dev - libcanberra-gtk3-dev - libgranite-dev + libgranite-7-dev >= 7.7.0 libglib2.0-dev - libgtk-3-dev + libgtk-4-dev libnotify-dev libpulse-dev - libwingpanel-dev + libwingpanel-9-dev meson (>= 0.57.0) valac (>= 0.26) diff --git a/data/gresource.xml b/data/gresource.xml index 085054da..7ac75b32 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -1,7 +1,6 @@ - image-mask.svg indicator.css diff --git a/data/image-mask.svg b/data/image-mask.svg deleted file mode 100644 index 2209a4b6..00000000 --- a/data/image-mask.svg +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/meson.build b/meson.build index 89d03ef0..4d6843af 100644 --- a/meson.build +++ b/meson.build @@ -28,7 +28,7 @@ gresource = gnome.compile_resources( source_dir: 'data' ) -wingpanel_dep = dependency('wingpanel', version: '>=2.1.0') +wingpanel_dep = dependency('wingpanel-9') wingpanel_indicatorsdir = wingpanel_dep.get_pkgconfig_variable('indicatorsdir', define_variable: ['libdir', libdir]) config_data = configuration_data() @@ -43,6 +43,7 @@ config_file = configure_file( shared_module( meson.project_name(), config_file, + 'src/CanberraGtk4.vala', 'src/Device.vala', 'src/Indicator.vala', 'src/Widgets/DisplayWidget.vala', @@ -63,13 +64,14 @@ shared_module( dependency('glib-2.0'), dependency('gio-2.0'), dependency('gobject-2.0'), - dependency('granite', version: '>=6.0.0'), - dependency('gtk+-3.0'), + dependency('granite-7', version: '>=7.7.0'), + dependency('gtk4'), + dependency('gtk4-wayland'), + dependency('gtk4-x11'), dependency('libpulse'), dependency('libpulse-mainloop-glib'), dependency('libnotify'), dependency('libcanberra'), - dependency('libcanberra-gtk3'), meson.get_compiler('vala').find_library('libpulse-ext', dirs: join_paths(meson.source_root(), 'vapi')), wingpanel_dep ], diff --git a/src/CanberraGtk4.vala b/src/CanberraGtk4.vala new file mode 100644 index 00000000..b70cd195 --- /dev/null +++ b/src/CanberraGtk4.vala @@ -0,0 +1,82 @@ +/* + * Copyright 2022 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* code adapted from libcanberra */ + +namespace CanberraGtk4 { + private Canberra.Context? context = null; + + public unowned Canberra.Context? context_get () { + Canberra.Proplist proplist; + + if (context != null) { + return context; + } + + if (Canberra.Context.create (out context) != Canberra.SUCCESS) { + return null; + } + + if (Canberra.Proplist.create (out proplist) != Canberra.SUCCESS) { + return null; + } + + proplist.sets (Canberra.PROP_CANBERRA_XDG_THEME_NAME, Gtk.Settings.get_default ().gtk_sound_theme_name); + + unowned var name = GLib.Environment.get_application_name (); + if (name != null) { + proplist.sets (Canberra.PROP_APPLICATION_NAME, name); + } else { + proplist.sets (Canberra.PROP_APPLICATION_NAME, "libcanberra-gtk"); + proplist.sets (Canberra.PROP_APPLICATION_VERSION, "%i.%i".printf (Canberra.MAJOR, Canberra.MINOR)); + proplist.sets (Canberra.PROP_APPLICATION_ID, "org.freedesktop.libcanberra.gtk"); + } + + unowned var icon = Gtk.Window.get_default_icon_name (); + if (icon != null) { + proplist.sets (Canberra.PROP_APPLICATION_ICON_NAME, icon); + } + + unowned var display = Gdk.Display.get_default (); + if (display is Gdk.X11.Display) { + unowned var display_name = display.get_name (); + if (display_name != null) { + proplist.sets (Canberra.PROP_WINDOW_X11_SCREEN, display_name); + } + + var screen = "%i".printf (((Gdk.X11.Display) display).get_screen ().get_screen_number ()); + proplist.sets (Canberra.PROP_WINDOW_X11_SCREEN, screen); + } + + context.change_props_full (proplist); + + var val = Value (typeof (string)); + if (display.get_setting ("gtk-sound-theme-name", val)) { + context.change_props (Canberra.PROP_CANBERRA_XDG_THEME_NAME, val.get_string ()); + } + + val = Value (typeof (bool)); + if (display.get_setting ("gtk-enable-event-sounds", val)) { + unowned var env = GLib.Environment.get_variable ("CANBERRA_FORCE_EVENT_SOUNDS"); + context.change_props (Canberra.PROP_CANBERRA_ENABLE, env != null ? true : val.get_boolean ()); + } + + display.setting_changed.connect ((setting) => { + Value new_val; + if (setting == "gtk-sound-theme-name") { + new_val = Value (typeof (string)); + display.get_setting ("gtk-sound-theme-name", new_val); + context.change_props (Canberra.PROP_CANBERRA_ENABLE, new_val.get_string ()); + } else if (setting == "gtk-enable-event-sounds") { + new_val = Value (typeof (bool)); + unowned var env = GLib.Environment.get_variable ("CANBERRA_FORCE_EVENT_SOUNDS"); + display.get_setting ("gtk-enable-event-sounds", new_val); + context.change_props (Canberra.PROP_CANBERRA_ENABLE, env != null ? true : new_val.get_boolean ()); + } + }); + + return context; + } +} diff --git a/src/Indicator.vala b/src/Indicator.vala index 6d6b3b04..86787edb 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -77,13 +77,13 @@ public class Sound.Indicator : Wingpanel.Indicator { // Prevent a race that skips automatic resource loading // https://github.com/elementary/wingpanel-indicator-bluetooth/issues/203 - Gtk.IconTheme.get_default ().add_resource_path ("/org/elementary/wingpanel/icons"); + Gtk.IconTheme.get_for_display (Gdk.Display.get_default ()).add_resource_path ("/org/elementary/wingpanel/icons"); var provider = new Gtk.CssProvider (); provider.load_from_resource ("io/elementary/wingpanel/sound/indicator.css"); - Gtk.StyleContext.add_provider_for_screen ( - Gdk.Screen.get_default (), + Gtk.StyleContext.add_provider_for_display ( + Gdk.Display.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ); @@ -127,7 +127,7 @@ public class Sound.Indicator : Wingpanel.Indicator { mic_scale = new Widgets.Scale (mic_adjustment); - ca_context = CanberraGtk.context_get (); + ca_context = CanberraGtk4.context_get (); ca_context.change_props (Canberra.PROP_APPLICATION_NAME, "indicator-sound", Canberra.PROP_APPLICATION_ID, "wingpanel-indicator-sound", Canberra.PROP_APPLICATION_NAME, "start-here", @@ -328,14 +328,16 @@ public class Sound.Indicator : Wingpanel.Indicator { display_widget.icon_name = get_volume_icon (volume_control.volume.volume); } - private void on_volume_icon_scroll_event (Gdk.EventScroll e) { + private void on_volume_icon_scroll_event (Value event_value) { + unowned var e = (Gdk.ScrollEvent) event_value.peek_pointer (); double dir = 0.0; if (handle_scroll_event (e, out dir)) { handle_change (dir, false); } } - private void on_mic_icon_scroll_event (Gdk.EventScroll e) { + private void on_mic_icon_scroll_event (Value event_value) { + unowned var e = (Gdk.ScrollEvent) event_value.peek_pointer (); double dir = 0.0; if (handle_scroll_event (e, out dir)) { handle_change (dir, true); @@ -387,15 +389,15 @@ public class Sound.Indicator : Wingpanel.Indicator { mic_scale.active = !volume_control.micMute; - var settings_button = new Gtk.ModelButton () { + var settings_button = new Wingpanel.PopoverMenuItem () { text = _("Sound Settings…"), margin_top = 3 }; var input_controls_box = new Gtk.Box (VERTICAL, 0); - input_controls_box.add (mic_scale); - input_controls_box.add (input_device_manager); - input_controls_box.add (new Gtk.Separator (HORIZONTAL)); + input_controls_box.append (mic_scale); + input_controls_box.append (input_device_manager); + input_controls_box.append (new Gtk.Separator (HORIZONTAL)); var input_controls_revealer = new Gtk.Revealer () { child = input_controls_box @@ -403,15 +405,15 @@ public class Sound.Indicator : Wingpanel.Indicator { main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); if (is_in_session) { - main_box.add (mpris); - main_box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); + main_box.append (mpris); + main_box.append (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); } - main_box.add (volume_scale); - main_box.add (output_device_manager); + main_box.append (volume_scale); + main_box.append (output_device_manager); if (is_in_session) { - main_box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); - main_box.add (input_controls_revealer); - main_box.add (settings_button); + main_box.append (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); + main_box.append (input_controls_revealer); + main_box.append (settings_button); } mic_scale.notify["active"].connect (on_mic_switch_change); @@ -420,13 +422,12 @@ public class Sound.Indicator : Wingpanel.Indicator { volume_control.mic_volume = mic_adjustment.get_value (); }); - mic_scale.scroll_event.connect_after ((e) => { + mic_scale.scroll_event.connect_after ((event_value) => { + unowned var e = (Gdk.ScrollEvent) event_value.peek_pointer (); double dir = 0.0; if (handle_scroll_event (e, out dir)) { handle_change (dir, true); } - - return true; }); mpris.close.connect (() => { @@ -445,13 +446,12 @@ public class Sound.Indicator : Wingpanel.Indicator { volume_scale.slider_dropped.connect (play_volume_change_sound); - volume_scale.scroll_event.connect_after ((e) => { + volume_scale.scroll_event.connect_after ((event_value) => { + unowned var e = (Gdk.ScrollEvent) event_value.peek_pointer (); double dir = 0.0; if (handle_scroll_event (e, out dir)) { handle_change (dir, false); } - - return true; }); volume_scale.notify["active"].connect (on_volume_switch_change); @@ -480,10 +480,11 @@ public class Sound.Indicator : Wingpanel.Indicator { * In the case of diagonal scrolling, it ignores the event unless movement in one direction * is more than twice the movement in the other direction. */ - private bool handle_scroll_event (Gdk.EventScroll e, out double dir) { + private bool handle_scroll_event (Gdk.ScrollEvent e, out double dir) { dir = 0.0; bool natural_scroll; - var event_source = e.get_source_device ().input_source; + var event_source = e.get_device ().get_source (); + if (event_source == Gdk.InputSource.MOUSE) { natural_scroll = natural_scroll_mouse; } else if (event_source == Gdk.InputSource.TOUCHPAD) { @@ -492,19 +493,21 @@ public class Sound.Indicator : Wingpanel.Indicator { natural_scroll = false; } - switch (e.direction) { + switch (e.get_direction ()) { case Gdk.ScrollDirection.SMOOTH: - var abs_x = double.max (e.delta_x.abs (), 0.0001); - var abs_y = double.max (e.delta_y.abs (), 0.0001); + double dx, dy; + e.get_deltas (out dx, out dy); - if (abs_y / abs_x > 2.0) { - total_y_delta += e.delta_y; - } else if (abs_x / abs_y > 2.0) { - total_x_delta += e.delta_x; - } + var abs_x = double.max (dx.abs (), 0.0001); + var abs_y = double.max (dy.abs (), 0.0001); - break; + if (abs_y / abs_x > 2.0) { + total_y_delta += dy; + } else if (abs_x / abs_y > 2.0) { + total_x_delta += dx; + } + break; case Gdk.ScrollDirection.UP: total_y_delta = -1.0; break; diff --git a/src/Widgets/DeviceItem.vala b/src/Widgets/DeviceItem.vala index 9851f32d..1d31faea 100644 --- a/src/Widgets/DeviceItem.vala +++ b/src/Widgets/DeviceItem.vala @@ -12,14 +12,14 @@ public class Sound.Widgets.DeviceItem : Gtk.ListBoxRow { public Gtk.ListBoxRow? row { get; construct; } private bool is_priority; - private Gtk.RadioButton radio_button; + private Gtk.CheckButton radio_button; public DeviceItem (Device device, Gtk.ListBoxRow? row) { Object (device: device, row: row); } class construct { - set_css_name (Gtk.STYLE_CLASS_MENUITEM); + set_css_name ("modelbutton"); } construct { @@ -29,31 +29,29 @@ public class Sound.Widgets.DeviceItem : Gtk.ListBoxRow { ellipsize = MIDDLE }; - var image = new Gtk.Image.from_icon_name (device.icon_name + "-symbolic", MENU) { + var image = new Gtk.Image.from_icon_name (device.icon_name + "-symbolic") { use_fallback = true }; var box = new Gtk.Box (HORIZONTAL, 6); - box.add (label); - box.add (image); + box.append (label); + box.append (image); - radio_button = new Gtk.RadioButton (null) { + radio_button = new Gtk.CheckButton () { child = box, active = device.is_default, - hexpand = true, - xalign = 0 + hexpand = true }; if (row != null) { var item = (DeviceItem) row; - radio_button.set_group (item.radio_button.get_group ()); + radio_button.set_group (item.radio_button); } child = radio_button; - show_all (); selectable = false; - no_show_all = true; + visible = false; radio_button.toggled.connect (() => { if (radio_button.active) { diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 0a068274..3a4efbd9 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -37,10 +37,11 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Box { device_list = new Gtk.ListBox () { activate_on_single_click = true, + hexpand = true, visible = true }; - var scrolled_box = new Gtk.ScrolledWindow (null, null) { + var scrolled_box = new Gtk.ScrolledWindow () { child = device_list, hscrollbar_policy = Gtk.PolicyType.NEVER, propagate_natural_height = true, @@ -52,7 +53,7 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Box { child = scrolled_box }; - add (devices_revealer); + append (devices_revealer); update_showable (); @@ -71,7 +72,7 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Box { Gtk.ListBoxRow? row = device_list.get_row_at_index (0); var device_item = new DeviceItem (device, row); - device_list.add (device_item); + device_list.append (device_item); device_item.activated.connect (() => { pam.set_default_device.begin (device); @@ -80,7 +81,6 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Box { device.removed.connect (() => { device_list.remove (device_item); - device_list.show_all (); update_showable (); }); diff --git a/src/Widgets/DisplayWidget.vala b/src/Widgets/DisplayWidget.vala index 830ccb52..56c13a46 100644 --- a/src/Widgets/DisplayWidget.vala +++ b/src/Widgets/DisplayWidget.vala @@ -23,54 +23,58 @@ public class Sound.DisplayWidget : Gtk.Box { public bool mic_muted { get; set; } public string icon_name { get; set; } - public signal void volume_scroll_event (Gdk.EventScroll e); - public signal void mic_scroll_event (Gdk.EventScroll e); - - private Gtk.GestureMultiPress mic_gesture_click; - private Gtk.GestureMultiPress volume_gesture_click; + // HACK: Using Gdk.ScrollEvent instead of Value as the type of the parameter + // resulsts build error with valac 0.56.18 + // See https://gitlab.gnome.org/GNOME/vala/-/work_items/1671 + public signal void volume_scroll_event (Value event_value); + public signal void mic_scroll_event (Value event_value); construct { var volume_icon = new Gtk.Image () { pixel_size = 24 }; - var volume_event_box = new Gtk.EventBox () { - child = volume_icon - }; - volume_event_box.events = SCROLL_MASK | SMOOTH_SCROLL_MASK | BUTTON_PRESS_MASK | BUTTON_RELEASE_MASK; - var mic_icon = new Gtk.Spinner (); - mic_icon.get_style_context ().add_class ("mic-icon"); - mic_icon.get_style_context ().add_class ("composited-indicator"); - - var mic_event_box = new Gtk.EventBox () { - child = mic_icon - }; - mic_event_box.events = SCROLL_MASK | SMOOTH_SCROLL_MASK | BUTTON_PRESS_MASK | BUTTON_RELEASE_MASK; + mic_icon.add_css_class ("mic-icon"); + mic_icon.add_css_class ("composited-indicator"); var mic_revealer = new Gtk.Revealer () { - child = mic_event_box, + child = mic_icon, transition_type = SLIDE_LEFT }; valign = Gtk.Align.CENTER; - add (mic_revealer); - add (volume_event_box); - - /* SMOOTH_SCROLL_MASK has no effect on this widget for reasons that are not - * entirely clear. Only normal scroll events are received even if the SMOOTH_SCROLL_MASK - * is set. */ - mic_event_box.scroll_event.connect ((e) => { - mic_scroll_event (e); + append (mic_revealer); + append (volume_icon); + + var mic_scroll_controller = new Gtk.EventControllerLegacy (); + mic_scroll_controller.event.connect ((e) => { + if (e.get_event_type () != Gdk.EventType.SCROLL) { + return Gdk.EVENT_PROPAGATE; + } + + var event_value = Value (typeof (Gdk.ScrollEvent)); + event_value.set_instance (e); + mic_scroll_event (event_value); return Gdk.EVENT_STOP; }); - volume_event_box.scroll_event.connect ((e) => { - volume_scroll_event (e); + var volume_scroll_controller = new Gtk.EventControllerLegacy (); + volume_scroll_controller.event.connect ((e) => { + if (e.get_event_type () != Gdk.EventType.SCROLL) { + return Gdk.EVENT_PROPAGATE; + } + + var event_value = Value (typeof (Gdk.ScrollEvent)); + event_value.set_instance (e); + volume_scroll_event (event_value); return Gdk.EVENT_STOP; }); - mic_gesture_click = new Gtk.GestureMultiPress (mic_event_box) { + mic_icon.add_controller (mic_scroll_controller); + volume_icon.add_controller (volume_scroll_controller); + + var mic_gesture_click = new Gtk.GestureClick () { button = Gdk.BUTTON_MIDDLE }; mic_gesture_click.pressed.connect (() => { @@ -79,7 +83,9 @@ public class Sound.DisplayWidget : Gtk.Box { mic_gesture_click.reset (); }); - volume_gesture_click = new Gtk.GestureMultiPress (volume_event_box) { + mic_icon.add_controller (mic_gesture_click); + + var volume_gesture_click = new Gtk.GestureClick () { button = Gdk.BUTTON_MIDDLE }; volume_gesture_click.pressed.connect (() => { @@ -88,6 +94,8 @@ public class Sound.DisplayWidget : Gtk.Box { volume_gesture_click.reset (); }); + volume_icon.add_controller (volume_gesture_click); + bind_property ( "icon-name", volume_icon, @@ -103,9 +111,9 @@ public class Sound.DisplayWidget : Gtk.Box { notify["mic-muted"].connect (() => { if (mic_muted) { - mic_icon.get_style_context ().add_class ("disabled"); + mic_icon.add_css_class ("disabled"); } else { - mic_icon.get_style_context ().remove_class ("disabled"); + mic_icon.remove_css_class ("disabled"); } }); } diff --git a/src/Widgets/PlayerList.vala b/src/Widgets/PlayerList.vala index afcf207d..51a6f1ea 100644 --- a/src/Widgets/PlayerList.vala +++ b/src/Widgets/PlayerList.vala @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -public class Sound.Widgets.PlayerList : Gtk.Bin { +public class Sound.Widgets.PlayerList : Granite.Bin { public signal void close (); public Sound.Services.ObjectManager object_manager; @@ -35,9 +35,7 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { listbox = new Gtk.ListBox (); child = listbox; - margin_bottom = 3; - show_all (); Idle.add (() => { setup_dbus (); @@ -45,7 +43,6 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { }); object_manager = new Services.ObjectManager (); - object_manager.bind_property ("has-object", this, "visible", GLib.BindingFlags.SYNC_CREATE); object_manager.media_player_added.connect ((media_player, name, icon) => { bluetooth_widget = new PlayerRow.bluetooth (media_player, name, icon); @@ -53,14 +50,12 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { close (); }); - bluetooth_widget.show_all (); listbox.prepend (bluetooth_widget); }); object_manager.media_player_removed.connect ((media_player) => { debug ("Media player %s removed", media_player.name); listbox.remove (bluetooth_widget); - bluetooth_widget.destroy (); }); object_manager.media_player_status_changed.connect ((status, title, artist) => { @@ -82,7 +77,6 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { if (default_widget != null) { listbox.remove (default_widget); - default_widget.destroy (); } default_widget = new PlayerRow.default (new_player); @@ -91,8 +85,6 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { close (); }); - default_widget.show_all (); - listbox.prepend (default_widget); } } @@ -118,11 +110,9 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { default_widget.mpris_name = name; default_widget.client = iface; ifaces.insert (name, default_widget); - default_widget.no_show_all = false; default_widget.visible = true; } else { if (default_widget.mpris_name == "") { - default_widget.no_show_all = true; default_widget.visible = false; } @@ -130,8 +120,8 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { widg.close.connect (() => { close (); }); - widg.show_all (); - listbox.add (widg); + + listbox.append (widg); ifaces.insert (name, widg); } } @@ -148,19 +138,15 @@ public class Sound.Widgets.PlayerList : Gtk.Bin { var widg = ifaces[name]; if (widg != null) { listbox.remove (widg); - widg.destroy (); } } ifaces.remove (name); if (ifaces.length != 0 && default_widget.mpris_name == "") { - default_widget.no_show_all = true; default_widget.visible = false; } else { - default_widget.no_show_all = false; default_widget.visible = true; - show_all (); } } diff --git a/src/Widgets/PlayerRow.vala b/src/Widgets/PlayerRow.vala index 272ab40c..1443413a 100644 --- a/src/Widgets/PlayerRow.vala +++ b/src/Widgets/PlayerRow.vala @@ -17,8 +17,7 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { public signal void close (); - private Gtk.Image? background = null; - private Gtk.Image mask; + private Gtk.Image background; private Gtk.Label title_label; private Gtk.Label artist_label; private Gtk.Button prev_btn; @@ -31,8 +30,6 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { private string app_name = _("Music player"); private string last_art_url; - private Gtk.GestureMultiPress gesture_click; - public string mpris_name = ""; private AppInfo? ainfo; @@ -88,7 +85,7 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { }); } } else { - ((Gtk.Image) play_btn.image).icon_name = "media-playback-start-symbolic"; + play_btn.icon_name = "media-playback-start-symbolic"; prev_btn.sensitive = false; next_btn.sensitive = false; Sound.Indicator.settings.set_strv ( @@ -171,20 +168,6 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { pixel_size = ICON_SIZE }; - mask = new Gtk.Image.from_resource ("/io/elementary/wingpanel/sound/image-mask.svg") { - no_show_all = true, - pixel_size = 48 - }; - - var overlay = new Gtk.Overlay () { - can_focus = true, - child = background, - margin_bottom = 2, - margin_end = 4, - margin_start = 4 - }; - overlay.add_overlay (mask); - title_label = new Gtk.Label (null) { ellipsize = END, max_width_chars = 16, @@ -198,28 +181,23 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { halign = START, valign = START }; - artist_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); - artist_label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + artist_label.add_css_class (Granite.CssClass.DIM); + artist_label.add_css_class (Granite.CssClass.SMALL); var titles = new Gtk.Grid () { column_spacing = 3 }; - titles.attach (overlay, 0, 0, 1, 2); + titles.attach (background, 0, 0, 1, 2); titles.attach (title_label, 1, 0); titles.attach (artist_label, 1, 1); - var titles_events = new Gtk.EventBox () { - hexpand = true - }; - titles_events.add (titles); - prev_btn = new Gtk.Button.from_icon_name ( "media-skip-backward-symbolic" ) { sensitive = false, valign = CENTER }; - prev_btn.get_style_context ().add_class ("circular"); + prev_btn.add_css_class (Granite.CssClass.CIRCULAR); play_btn = new Gtk.Button.from_icon_name ( "media-playback-start-symbolic" @@ -227,7 +205,7 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { sensitive = true, valign = CENTER }; - play_btn.get_style_context ().add_class ("circular"); + play_btn.add_css_class (Granite.CssClass.CIRCULAR); next_btn = new Gtk.Button.from_icon_name ( "media-skip-forward-symbolic" @@ -235,14 +213,14 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { sensitive = false, valign = CENTER }; - next_btn.get_style_context ().add_class ("circular"); + next_btn.add_css_class (Granite.CssClass.CIRCULAR); spacing = 6; margin_end = 12; - add (titles_events); - add (prev_btn); - add (play_btn); - add (next_btn); + append (titles); + append (prev_btn); + append (play_btn); + append (next_btn); if (client != null) { connect_to_client (); @@ -251,11 +229,13 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { update_controls (); } - gesture_click = new Gtk.GestureMultiPress (titles_events) { + var gesture_click = new Gtk.GestureClick () { button = Gdk.BUTTON_PRIMARY }; gesture_click.released.connect (raise_player); + titles.add_controller (gesture_click); + prev_btn.clicked.connect (() => { Idle.add (() => { if (!Thread.supported ()) { @@ -392,7 +372,7 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { }); } - private void raise_player (Gtk.GestureMultiPress gesture, int n_press, double x, double y) { + private void raise_player (Gtk.GestureClick gesture, int n_press, double x, double y) { try { close (); if (client != null && client.player.can_raise) { @@ -429,9 +409,9 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { */ private void update_play_status () { if (client.player.playback_status == "Playing") { - ((Gtk.Image) play_btn.image).icon_name = "media-playback-pause-symbolic"; + play_btn.icon_name = "media-playback-pause-symbolic"; } else { - ((Gtk.Image) play_btn.image).icon_name = "media-playback-start-symbolic"; + play_btn.icon_name = "media-playback-start-symbolic"; } /** @@ -440,11 +420,9 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { * browsers, but in theory any app could have temporary MPRIS playback. */ if (client.player.playback_status == "Stopped" && app_info == null) { - no_show_all = true; - hide (); + visible = false; } else { - no_show_all = false; - show (); + visible = true; } } @@ -468,8 +446,6 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { if (!uri.has_prefix ("file://") && !uri.has_prefix ("http")) { background.gicon = app_icon; background.get_style_context ().set_scale (scale_factor); - mask.no_show_all = true; - mask.hide (); return; } @@ -499,16 +475,9 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { } } - try { - var pbuf = new Gdk.Pixbuf.from_file_at_size (fname, ICON_SIZE * scale_factor, ICON_SIZE * scale_factor); - background.gicon = mask_pixbuf (pbuf, scale_factor); - background.get_style_context ().set_scale (1); - mask.no_show_all = false; - mask.show (); - } catch (Error e) { - warning (e.message); - //background.gicon = app_icon; - } + background.gicon = new FileIcon (File.new_for_path (fname)); + background.get_style_context ().set_scale (1); + background.add_css_class (Granite.CssClass.CARD); } else { load_remote_art_cancel.cancel (); load_remote_art_cancel.reset (); @@ -522,16 +491,14 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { var stream = yield file.read_async (Priority.DEFAULT, load_remote_art_cancel); var pixbuf = yield new Gdk.Pixbuf.from_stream_async (stream, load_remote_art_cancel); if (pixbuf != null) { - background.gicon = mask_pixbuf (pixbuf, scale_factor); + background.gicon = pixbuf; background.get_style_context ().set_scale (1); - mask.no_show_all = false; - mask.show (); + background.add_css_class (Granite.CssClass.CARD); } } catch (Error e) { background.gicon = app_icon; background.get_style_context ().set_scale (scale_factor); - mask.no_show_all = true; - mask.hide (); + background.remove_css_class (Granite.CssClass.CARD); } } @@ -550,8 +517,7 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { last_art_url = ""; background.pixel_size = ICON_SIZE; background.gicon = app_icon; - mask.no_show_all = true; - mask.hide (); + background.remove_css_class (Granite.CssClass.CARD); } string title; @@ -576,37 +542,15 @@ public class Sound.Widgets.PlayerRow : Gtk.Box { } } - private static Gdk.Pixbuf? mask_pixbuf (Gdk.Pixbuf pixbuf, int scale) { - var size = ICON_SIZE * scale; - var mask_offset = 4 * scale; - var mask_size_offset = mask_offset * 2; - var mask_size = ICON_SIZE * scale; - var offset_x = mask_offset; - var offset_y = mask_offset + scale; - size = size - mask_size_offset; - - var input = pixbuf.scale_simple (size, size, Gdk.InterpType.BILINEAR); - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, mask_size, mask_size); - var cr = new Cairo.Context (surface); - - Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, offset_x, offset_y, size, size, mask_offset); - cr.clip (); - - Gdk.cairo_set_source_pixbuf (cr, input, offset_x, offset_y); - cr.paint (); - - return Gdk.pixbuf_get_from_surface (surface, 0, 0, mask_size, mask_size); - } - public void update_play (string playing, string title, string artist) { if (playing != "") { switch (playing) { case "playing": - ((Gtk.Image) play_btn.image).icon_name = "media-playback-pause-symbolic"; + play_btn.icon_name = "media-playback-pause-symbolic"; break; default: /* Stopped, Paused */ - ((Gtk.Image) play_btn.image).icon_name = "media-playback-start-symbolic"; + play_btn.icon_name = "media-playback-start-symbolic"; break; } } diff --git a/src/Widgets/Scale.vala b/src/Widgets/Scale.vala index f76d2122..e490b076 100644 --- a/src/Widgets/Scale.vala +++ b/src/Widgets/Scale.vala @@ -3,7 +3,11 @@ * SPDX-FileCopyrightText: 2015-2025 elementary, Inc. (https://elementary.io) */ -public class Sound.Widgets.Scale : Gtk.EventBox { +public class Sound.Widgets.Scale : Granite.Bin { + // HACK: Using Gdk.ScrollEvent instead of Value as the type of the parameter + // resulsts build error with valac 0.56.18 + // See https://gitlab.gnome.org/GNOME/vala/-/work_items/1671 + public signal void scroll_event (Value event_value); public signal void slider_dropped (); public Gtk.Adjustment adjustment { get; construct; } @@ -11,8 +15,6 @@ public class Sound.Widgets.Scale : Gtk.EventBox { public bool active { get; set; default = true; } - private Gtk.GestureMultiPress gesture_click; - public Scale (Gtk.Adjustment adjustment) { Object (adjustment: adjustment); } @@ -22,10 +24,7 @@ public class Sound.Widgets.Scale : Gtk.EventBox { } construct { - var image = new Gtk.Image (); - var toggle = new Gtk.ToggleButton (); - toggle.image = image; var scale_widget = new Gtk.Scale (HORIZONTAL, adjustment) { draw_value = false, @@ -40,25 +39,33 @@ public class Sound.Widgets.Scale : Gtk.EventBox { margin_bottom = 6, margin_end = 12 }; - box.add (toggle); - box.add (scale_widget); + box.append (toggle); + box.append (scale_widget); child = box; - add_events (Gdk.EventMask.SMOOTH_SCROLL_MASK); - above_child = false; - gesture_click = new Gtk.GestureMultiPress (scale_widget); + var gesture_click = new Gtk.GestureClick (); gesture_click.released.connect (() => { slider_dropped (); }); - scale_widget.scroll_event.connect ((e) => { - /* Re-emit the signal on the eventbox instead of using native handler */ - scroll_event (e); + var scroll_controller = new Gtk.EventControllerLegacy (); + scroll_controller.event.connect_after ((e) => { + if (e.get_event_type () != Gdk.EventType.SCROLL) { + return Gdk.EVENT_PROPAGATE; + } + + var event_value = Value (typeof (Gdk.ScrollEvent)); + event_value.set_instance (e); + scroll_event (event_value); + return Gdk.EVENT_STOP; }); - bind_property ("icon", image, "icon-name"); + scale_widget.add_controller (gesture_click); + scale_widget.add_controller (scroll_controller); + + bind_property ("icon", toggle, "icon-name"); bind_property ("active", scale_widget, "sensitive", BindingFlags.SYNC_CREATE); bind_property ("active", toggle, "active", BIDIRECTIONAL | SYNC_CREATE); diff --git a/vapi/libcanberra-gtk3.deps b/vapi/libcanberra-gtk3.deps deleted file mode 100644 index 7bd6beca..00000000 --- a/vapi/libcanberra-gtk3.deps +++ /dev/null @@ -1,2 +0,0 @@ -libcanberra -gtk+-3.0 diff --git a/vapi/libcanberra-gtk3.vapi b/vapi/libcanberra-gtk3.vapi deleted file mode 100644 index f788f8e0..00000000 --- a/vapi/libcanberra-gtk3.vapi +++ /dev/null @@ -1,37 +0,0 @@ -/*** - This file is part of libcanberra. - - Copyright 2009 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, see - . -***/ - -using Canberra; -using Gdk; -using Gtk; - -[CCode (cprefix = "CA_GTK_", lower_case_cprefix = "ca_gtk_", cheader_filename = "canberra-gtk.h")] -namespace CanberraGtk { - - public unowned Context? context_get(); - public unowned Context? context_get_for_screen(Gdk.Screen? screen); - - public int proplist_set_for_widget(Proplist p, Gtk.Widget w); - public int play_for_widget(Gtk.Widget w, uint32 id, ...); - public int proplist_set_for_event(Proplist p, Gdk.Event e); - public int play_for_event(Gdk.Event e, uint32 id, ...); - - public void widget_disable_sounds(Gtk.Widget w, bool enable = false); -}