Skip to content

Commit

Permalink
Initial attempt at updating status area icons
Browse files Browse the repository at this point in the history
  • Loading branch information
filmor committed Nov 12, 2024
1 parent 09062c1 commit e952a29
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 42 deletions.
12 changes: 4 additions & 8 deletions cosmic-applet-status-area/src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,10 @@ impl cosmic::Application for App {
fn view(&self) -> cosmic::Element<'_, Msg> {
let children = self.menus.iter().map(|(id, menu)| {
mouse_area(
match menu.icon_pixmap() {
Some(icon) if menu.icon_name() == "" => self
.core
.applet
.icon_button_from_handle(icon.clone().symbolic(true)),
_ => self.core.applet.icon_button(menu.icon_name()),
}
.on_press_down(Msg::TogglePopup(*id)),
self.core
.applet
.icon_button_from_handle(menu.icon_handle().into())
.on_press_down(Msg::TogglePopup(*id)),
)
.on_enter(Msg::Hovered(*id))
.into()
Expand Down
27 changes: 19 additions & 8 deletions cosmic-applet-status-area/src/components/status_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@

use cosmic::{applet::menu_button, iced, widget::icon};

use crate::subscriptions::status_notifier_item::{Layout, StatusNotifierItem};
use crate::subscriptions::status_notifier_item::{IconNameOrPixmap, Layout, StatusNotifierItem};

#[derive(Clone, Debug)]
pub enum Msg {
Icon(Option<IconNameOrPixmap>),
Layout(Result<Layout, String>),
Click(i32, bool),
}

#[derive(Debug)]
pub struct State {
item: StatusNotifierItem,
layout: Option<Layout>,
icon: Option<IconNameOrPixmap>,
expanded: Option<i32>,
}

Expand All @@ -24,13 +27,18 @@ impl State {
item,
layout: None,
expanded: None,
icon: None,
},
iced::Task::none(),
)
}

pub fn update(&mut self, message: Msg) -> iced::Task<Msg> {
match message {
Msg::Icon(icon) => {
self.icon = icon;
iced::Command::none()
}
Msg::Layout(layout) => {
match layout {
Ok(layout) => {
Expand Down Expand Up @@ -63,12 +71,10 @@ impl State {
self.item.name()
}

pub fn icon_name(&self) -> &str {
self.item.icon_name()
}

pub fn icon_pixmap(&self) -> Option<&icon::Handle> {
self.item.icon_pixmap()
pub fn icon_handle(&self) -> icon::Handle {
self.icon.as_ref()
.map(|i| i.clone().into())
.unwrap_or_else(|| icon::from_raster_bytes(&[]))
}

pub fn popup_view(&self) -> cosmic::Element<Msg> {
Expand All @@ -80,7 +86,12 @@ impl State {
}

pub fn subscription(&self) -> iced::Subscription<Msg> {
self.item.layout_subscription().map(Msg::Layout)
let subs = vec![
self.item.icon_subscription().map(Msg::Icon),
self.item.layout_subscription().map(Msg::Layout),
];

iced::Subscription::batch(subs)
}

pub fn opened(&self) {
Expand Down
86 changes: 60 additions & 26 deletions cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ 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>,
// icon_name: String,
// icon_pixmap: Option<icon::Handle>,
_item_proxy: StatusNotifierItemProxy<'static>,
menu_proxy: DBusMenuProxy<'static>,
}
Expand All @@ -25,6 +24,28 @@ pub struct Icon {
bytes: Vec<u8>,
}

#[derive(Clone, Debug)]
pub enum IconNameOrPixmap {
Name(String),
Pixmap(Icon),
}

impl From<IconNameOrPixmap> for icon::Handle {
fn from(value: IconNameOrPixmap) -> Self {
match value {
IconNameOrPixmap::Name(name) => icon::from_name(name).symbolic(true).into(),
IconNameOrPixmap::Pixmap(i) => {
let mut i = i.clone();
// 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).symbolic(true)
}
}
}
}

impl StatusNotifierItem {
pub async fn new(connection: &zbus::Connection, name: String) -> zbus::Result<Self> {
let (dest, path) = if let Some(idx) = name.find('/') {
Expand All @@ -34,26 +55,12 @@ impl StatusNotifierItem {
};

let item_proxy = StatusNotifierItemProxy::builder(connection)
.cache_properties(zbus::proxy::CacheProperties::Yes)
.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())?
Expand All @@ -63,8 +70,6 @@ impl StatusNotifierItem {

Ok(Self {
name,
icon_name,
icon_pixmap,
_item_proxy: item_proxy,
menu_proxy,
})
Expand All @@ -74,12 +79,21 @@ 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()
pub fn icon_subscription(&self) -> iced::Subscription<Option<IconNameOrPixmap>> {
let item_proxy = self._item_proxy.clone();
iced::subscription::run_with_id(
format!("status-notifier-newicon-{}", &self.name),
async move {
let initial = futures::stream::once(get_icon(item_proxy.clone()));
let updates = item_proxy
.receive_new_icon()
.await
.unwrap()
.then(move |_| get_icon(item_proxy.clone()));
initial.chain(updates)
}
.flatten_stream(),
)
}

// TODO: Only fetch changed part of layout, if that's any faster
Expand Down Expand Up @@ -109,6 +123,23 @@ async fn get_layout(menu_proxy: DBusMenuProxy<'static>) -> Result<Layout, String
}
}

async fn get_icon(item_proxy: StatusNotifierItemProxy<'static>) -> Option<IconNameOrPixmap> {
if let Ok(pixmaps) = item_proxy.icon_pixmap().await {
// TODO Handle icon with multiple sizes?
return Some(IconNameOrPixmap::Pixmap(
pixmaps.into_iter().max_by_key(|i| (i.width, i.height))?,
));
}

if let Ok(icon_name) = item_proxy.icon_name().await {
if icon_name != "" {
return Some(IconNameOrPixmap::Name(icon_name));
}
}

None
}

#[zbus::proxy(interface = "org.kde.StatusNotifierItem")]
trait StatusNotifierItem {
#[zbus(property)]
Expand All @@ -120,6 +151,9 @@ trait StatusNotifierItem {

#[zbus(property)]
fn menu(&self) -> zbus::Result<zvariant::OwnedObjectPath>;

#[zbus(signal)]
fn new_icon(&self) -> zbus::Result<()>;
}

#[derive(Clone, Debug)]
Expand Down

0 comments on commit e952a29

Please sign in to comment.