diff --git a/README.md b/README.md
index 7a1d93aa..cabc3997 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ You'll need the following dependencies:
gobject-introspection
libglib2.0-dev
+ libgtk-4-dev >=4.22
libgranite-7-dev >= 7.0.0
libwingpanel-9-dev
meson >= 0.58.0
diff --git a/data/gresource.xml b/data/gresource.xml
index a6a0c06a..4433612c 100644
--- a/data/gresource.xml
+++ b/data/gresource.xml
@@ -1,35 +1,6 @@
- indicator.css
-
-
- icons/active.svg
- icons/disabled.svg
- icons/paired.svg
-
- icons/disabled-10.svg
- icons/disabled-20.svg
- icons/disabled-30.svg
- icons/disabled-40.svg
- icons/disabled-50.svg
- icons/disabled-60.svg
- icons/disabled-70.svg
- icons/disabled-80.svg
- icons/disabled-90.svg
-
- icons/active.svg
- icons/disabled.svg
- icons/paired.svg
-
- icons/disabled-10.svg
- icons/disabled-20.svg
- icons/disabled-30.svg
- icons/disabled-40.svg
- icons/disabled-50.svg
- icons/disabled-60.svg
- icons/disabled-70.svg
- icons/disabled-80.svg
- icons/disabled-90.svg
+ icons/bluetooth.svg
diff --git a/data/icons/active.svg b/data/icons/active.svg
deleted file mode 100644
index c6a05fbb..00000000
--- a/data/icons/active.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
diff --git a/data/icons/bluetooth.svg b/data/icons/bluetooth.svg
new file mode 100644
index 00000000..e9932cdf
--- /dev/null
+++ b/data/icons/bluetooth.svg
@@ -0,0 +1,122 @@
+
diff --git a/data/icons/disabled-10.svg b/data/icons/disabled-10.svg
deleted file mode 100644
index e2b63b02..00000000
--- a/data/icons/disabled-10.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
diff --git a/data/icons/disabled-20.svg b/data/icons/disabled-20.svg
deleted file mode 100644
index 245d1343..00000000
--- a/data/icons/disabled-20.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
diff --git a/data/icons/disabled-30.svg b/data/icons/disabled-30.svg
deleted file mode 100644
index 07e96485..00000000
--- a/data/icons/disabled-30.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-40.svg b/data/icons/disabled-40.svg
deleted file mode 100644
index f33f474e..00000000
--- a/data/icons/disabled-40.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-50.svg b/data/icons/disabled-50.svg
deleted file mode 100644
index 2a85122f..00000000
--- a/data/icons/disabled-50.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-60.svg b/data/icons/disabled-60.svg
deleted file mode 100644
index a43ed4e5..00000000
--- a/data/icons/disabled-60.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-70.svg b/data/icons/disabled-70.svg
deleted file mode 100644
index b57f479f..00000000
--- a/data/icons/disabled-70.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-80.svg b/data/icons/disabled-80.svg
deleted file mode 100644
index 2bb7b89a..00000000
--- a/data/icons/disabled-80.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
diff --git a/data/icons/disabled-90.svg b/data/icons/disabled-90.svg
deleted file mode 100644
index e2601b86..00000000
--- a/data/icons/disabled-90.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
diff --git a/data/icons/disabled.svg b/data/icons/disabled.svg
deleted file mode 100644
index 76e74907..00000000
--- a/data/icons/disabled.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/paired.svg b/data/icons/paired.svg
deleted file mode 100644
index ac6b6a3c..00000000
--- a/data/icons/paired.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
diff --git a/data/indicator.css b/data/indicator.css
deleted file mode 100644
index 0bdc8cc6..00000000
--- a/data/indicator.css
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-* Copyright 2017 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
-* License as published by the Free Software Foundation; either
-* version 2 of the License, or (at your option) any later version.
-*
-* This program 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
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public
-* License along with this program; if not, write to the
-* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-* Boston, MA 02110-1301 USA.
-*/
-
-.bluetooth-icon {
- animation: none;
- min-width: 24px;
- opacity: 1;
- transition: none;
- -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-active-symbolic");
-}
-
-.bluetooth-icon.paired {
- animation: none;
- -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-paired-symbolic");
-}
-
-.bluetooth-icon.disabled {
- animation: bluetooth-disabled 160ms cubic-bezier(0.4, 0.0, 0.2, 1);
- -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-symbolic");
-}
-
-@keyframes bluetooth-disabled {
- 0% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-active-symbolic"); }
- 10% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-10-symbolic"); }
- 20% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-20-symbolic"); }
- 30% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-30-symbolic"); }
- 40% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-40-symbolic"); }
- 50% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-50-symbolic"); }
- 60% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-60-symbolic"); }
- 70% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-70-symbolic"); }
- 80% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-80-symbolic"); }
- 90% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-90-symbolic"); }
- 100% { -gtk-icon-source: -gtk-icontheme("indicator-bluetooth-disabled-symbolic"); }
-}
diff --git a/meson.build b/meson.build
index 837146f9..18b1da9a 100644
--- a/meson.build
+++ b/meson.build
@@ -47,13 +47,14 @@ shared_module(
'src/Widgets/Device.vala',
'src/Widgets/DisplayWidget.vala',
'src/Widgets/PopoverWidget.vala',
+ 'src' / 'Widgets' / 'Symbol.vala',
config_file,
gresource,
dependencies: [
dependency('glib-2.0'),
dependency('gobject-2.0'),
dependency('granite-7', version: '>=7.0.0'),
- dependency('gtk4'),
+ dependency('gtk4', version: '>=4.22'),
wingpanel_dep
],
install: true,
diff --git a/src/Widgets/DisplayWidget.vala b/src/Widgets/DisplayWidget.vala
index 21ad3156..a61f585a 100644
--- a/src/Widgets/DisplayWidget.vala
+++ b/src/Widgets/DisplayWidget.vala
@@ -6,28 +6,18 @@
public class BluetoothIndicator.Widgets.DisplayWidget : Granite.Bin {
public BluetoothIndicator.Services.ObjectManager object_manager { get; construct; }
- private Gtk.Spinner spinner;
+ private Granite.Symbol symbol;
public DisplayWidget (BluetoothIndicator.Services.ObjectManager object_manager) {
Object (object_manager: object_manager);
}
construct {
- spinner = new Gtk.Spinner ();
-
- child = spinner;
-
- // Prevent a race that skips automatic resource loading
- // https://github.com/elementary/panel-bluetooth/issues/203
- 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/bluetooth/indicator.css");
-
- Gtk.StyleContext.add_provider_for_display (Gdk.Display.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+ symbol = new Granite.Symbol ("/io/elementary/wingpanel/bluetooth/icons/bluetooth.svg") {
+ pixel_size = 24
+ };
- spinner.add_css_class ("bluetooth-icon");
- spinner.add_css_class ("disabled");
+ child = symbol;
object_manager.global_state_changed.connect ((state, connected) => {
set_icon ();
@@ -68,18 +58,16 @@ public class BluetoothIndicator.Widgets.DisplayWidget : Granite.Bin {
string context;
if (state) {
- spinner.remove_css_class ("disabled");
context = _("Middle-click to turn Bluetooth off");
if (connected) {
- spinner.add_css_class ("paired");
+ symbol.state = Granite.SymbolState.ACTIVE;
description = _("Bluetooth connected");
} else {
- spinner.remove_css_class ("paired");
+ symbol.state = Granite.SymbolState.NORMAL;
description = _("Bluetooth is on");
}
} else {
- spinner.remove_css_class ("paired");
- spinner.add_css_class ("disabled");
+ symbol.state = Granite.SymbolState.DISABLED;
description = _("Bluetooth is off");
context = _("Middle-click to turn Bluetooth on");
}
diff --git a/src/Widgets/Symbol.vala b/src/Widgets/Symbol.vala
new file mode 100644
index 00000000..dbc77b5e
--- /dev/null
+++ b/src/Widgets/Symbol.vala
@@ -0,0 +1,78 @@
+/*
+* SPDX-License-Identifier: LGPL-2.1-or-later
+* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
+*/
+
+namespace Granite.SymbolState {
+ // The default state
+ public const string NORMAL = "normal";
+ // Disabled state represented by a slash
+ public const string DISABLED = "disabled";
+ // e.g. paired, connected, needs attention
+ public const string ACTIVE = "active";
+}
+
+public class Granite.Symbol : Granite.Bin {
+ public string resource_path { get; construct; }
+
+ public int pixel_size {
+ get { return image.pixel_size; }
+ set { image.pixel_size = value; }
+ }
+
+ public uint state_index {
+ get { return svg.state; }
+ set {
+ uint length = -1;
+ svg.get_state_names (out length);
+
+ if (value > length - 1) {
+ warning ("Granite.Symbol set to undefined state. Ignoring.");
+ return;
+ }
+
+ svg.state = value;
+ notify_property ("state");
+ }
+ }
+
+ public string state {
+ get {
+ uint length = -1;
+ return svg.get_state_names (out length)[state_index];
+ }
+ set {
+ uint length = -1;
+ var names = svg.get_state_names (out length);
+
+ for (int i = 0; i < length; i++) {
+ if (names[i] == value) {
+ state_index = i;
+ return;
+ }
+ }
+
+ warning ("Granite.Symbol set to undefined state. Ignoring.");
+ }
+ }
+
+ private Gtk.Image image;
+ private Gtk.Svg svg;
+
+ public Symbol (string resource_path) {
+ Object (resource_path: resource_path);
+ }
+
+ construct {
+ svg = new Gtk.Svg.from_resource (resource_path);
+
+ image = new Gtk.Image.from_paintable (svg);
+
+ child = image;
+
+ child.realize.connect (() => {
+ svg.set_frame_clock (get_frame_clock ());
+ svg.play ();
+ });
+ }
+}