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:
parent
7fdaf839e0
commit
2a939e5a11
2 changed files with 83 additions and 38 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -11,18 +11,21 @@ use zbus::zvariant::{self, OwnedValue};
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct StatusNotifierItem {
|
||||
name: String,
|
||||
icon_name: String,
|
||||
// TODO Handle icon with multiple sizes?
|
||||
icon_pixmap: Option<icon::Handle>,
|
||||
_item_proxy: StatusNotifierItemProxy<'static>,
|
||||
item_proxy: StatusNotifierItemProxy<'static>,
|
||||
menu_proxy: DBusMenuProxy<'static>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, zvariant::Value)]
|
||||
pub struct Icon {
|
||||
width: i32,
|
||||
height: i32,
|
||||
bytes: Vec<u8>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum IconUpdate {
|
||||
Name(String),
|
||||
Pixmap(Vec<Icon>),
|
||||
}
|
||||
|
||||
impl StatusNotifierItem {
|
||||
|
|
@ -34,26 +37,13 @@ impl StatusNotifierItem {
|
|||
};
|
||||
|
||||
let item_proxy = StatusNotifierItemProxy::builder(connection)
|
||||
// Status icons don't seem to report property changes the normal way...
|
||||
.cache_properties(zbus::proxy::CacheProperties::No)
|
||||
.destination(dest.to_string())?
|
||||
.path(path.to_string())?
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let icon_name = item_proxy.icon_name().await.unwrap_or_default();
|
||||
let icon_pixmap = item_proxy
|
||||
.icon_pixmap()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.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)
|
||||
});
|
||||
|
||||
let menu_path = item_proxy.menu().await?;
|
||||
let menu_proxy = DBusMenuProxy::builder(connection)
|
||||
.destination(dest.to_string())?
|
||||
|
|
@ -63,9 +53,7 @@ impl StatusNotifierItem {
|
|||
|
||||
Ok(Self {
|
||||
name,
|
||||
icon_name,
|
||||
icon_pixmap,
|
||||
_item_proxy: item_proxy,
|
||||
item_proxy,
|
||||
menu_proxy,
|
||||
})
|
||||
}
|
||||
|
|
@ -74,19 +62,11 @@ impl StatusNotifierItem {
|
|||
&self.name
|
||||
}
|
||||
|
||||
pub fn icon_name(&self) -> &str {
|
||||
&self.icon_name
|
||||
}
|
||||
|
||||
pub fn icon_pixmap(&self) -> Option<&icon::Handle> {
|
||||
self.icon_pixmap.as_ref()
|
||||
}
|
||||
|
||||
// TODO: Only fetch changed part of layout, if that's any faster
|
||||
pub fn layout_subscription(&self) -> iced::Subscription<Result<Layout, String>> {
|
||||
let menu_proxy = self.menu_proxy.clone();
|
||||
Subscription::run_with_id(
|
||||
format!("status-notifier-item-{}", &self.name),
|
||||
format!("status-notifier-item-layout-{}", &self.name),
|
||||
async move {
|
||||
let initial = futures::stream::once(get_layout(menu_proxy.clone()));
|
||||
let layout_updated_stream = menu_proxy.receive_layout_updated().await.unwrap();
|
||||
|
|
@ -97,6 +77,38 @@ impl StatusNotifierItem {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn icon_subscription(&self) -> iced::Subscription<IconUpdate> {
|
||||
fn icon_events<'a>(
|
||||
item_proxy: StatusNotifierItemProxy<'static>,
|
||||
) -> impl futures::Stream<Item = IconUpdate> + 'static {
|
||||
async move {
|
||||
let icon_name = item_proxy.icon_name().await;
|
||||
let icon_pixmap = item_proxy.icon_pixmap().await;
|
||||
futures::stream::iter(
|
||||
[
|
||||
icon_name.map(IconUpdate::Name),
|
||||
icon_pixmap.map(IconUpdate::Pixmap),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(Result::ok),
|
||||
)
|
||||
}
|
||||
.flatten_stream()
|
||||
}
|
||||
|
||||
let item_proxy = self.item_proxy.clone();
|
||||
Subscription::run_with_id(
|
||||
format!("status-notifier-item-icon-{}", &self.name),
|
||||
async move {
|
||||
let new_icon_stream = item_proxy.receive_new_icon().await.unwrap();
|
||||
futures::stream::once(async { () })
|
||||
.chain(new_icon_stream.map(|_| ()))
|
||||
.flat_map(move |()| icon_events(item_proxy.clone()))
|
||||
}
|
||||
.flatten_stream(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn menu_proxy(&self) -> &DBusMenuProxy<'static> {
|
||||
&self.menu_proxy
|
||||
}
|
||||
|
|
@ -120,6 +132,9 @@ trait StatusNotifierItem {
|
|||
|
||||
#[zbus(property)]
|
||||
fn menu(&self) -> zbus::Result<zvariant::OwnedObjectPath>;
|
||||
|
||||
#[zbus(signal)]
|
||||
fn new_icon(&self) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue