status-area: Handle changes to icon properties

It seems status icons, at least some, don't send property change
notifications. So we can't rely on that, and have to disable caching.
And handle the `NewIcon` signal defined in
https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem

I'm not sure whether or not there's a *good* reason it works this way,
but regardless I see `nm-applet` and `ibus` update their icons as
they should after these changes.
This commit is contained in:
Ian Douglas Scott 2025-03-17 13:51:56 -07:00 committed by Ian Douglas Scott
parent 7fdaf839e0
commit 2a939e5a11
2 changed files with 83 additions and 38 deletions

View file

@ -7,11 +7,12 @@ use cosmic::{
widget::icon,
};
use crate::subscriptions::status_notifier_item::{Layout, StatusNotifierItem};
use crate::subscriptions::status_notifier_item::{IconUpdate, Layout, StatusNotifierItem};
#[derive(Clone, Debug)]
pub enum Msg {
Layout(Result<Layout, String>),
Icon(IconUpdate),
Click(i32, bool),
}
@ -19,6 +20,9 @@ pub struct State {
item: StatusNotifierItem,
layout: Option<Layout>,
expanded: Option<i32>,
icon_name: String,
// TODO handle icon with multiple sizes?
icon_pixmap: Option<icon::Handle>,
}
impl State {
@ -28,6 +32,8 @@ impl State {
item,
layout: None,
expanded: None,
icon_name: String::new(),
icon_pixmap: None,
},
iced::Task::none(),
)
@ -44,6 +50,27 @@ impl State {
}
iced::Task::none()
}
Msg::Icon(update) => {
match update {
IconUpdate::Name(name) => {
self.icon_name = name;
}
IconUpdate::Pixmap(icons) => {
self.icon_pixmap = icons
.into_iter()
.max_by_key(|i| (i.width, i.height))
.map(|mut i| {
// Convert ARGB to RGBA
for pixel in i.bytes.chunks_exact_mut(4) {
pixel.rotate_left(1);
}
icon::from_raster_pixels(i.width as u32, i.height as u32, i.bytes)
});
}
}
iced::Task::none()
}
Msg::Click(id, is_submenu) => {
let menu_proxy = self.item.menu_proxy().clone();
tokio::spawn(async move {
@ -68,11 +95,11 @@ impl State {
}
pub fn icon_name(&self) -> &str {
self.item.icon_name()
&self.icon_name
}
pub fn icon_pixmap(&self) -> Option<&icon::Handle> {
self.item.icon_pixmap()
self.icon_pixmap.as_ref()
}
pub fn popup_view(&self) -> cosmic::Element<Msg> {
@ -84,7 +111,10 @@ impl State {
}
pub fn subscription(&self) -> iced::Subscription<Msg> {
self.item.layout_subscription().map(Msg::Layout)
iced::Subscription::batch([
self.item.layout_subscription().map(Msg::Layout),
self.item.icon_subscription().map(Msg::Icon),
])
}
pub fn opened(&self) {