Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions cosmic-applet-status-area/src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,7 @@ impl App {
let overflow_index = self.overflow_index().unwrap_or(0);
let children = self.menus.iter().skip(overflow_index).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)),
menu_icon_button(&self.core.applet, &menu).on_press_down(Msg::TogglePopup(*id)),
)
.on_enter(Msg::Hovered(*id))
.into()
Expand Down Expand Up @@ -453,14 +446,7 @@ impl cosmic::Application for App {
.take(overflow_index.unwrap_or(self.menus.len()))
.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)),
menu_icon_button(&self.core.applet, &menu).on_press_down(Msg::TogglePopup(*id)),
)
.on_enter(Msg::Hovered(*id))
.into()
Expand Down Expand Up @@ -530,6 +516,23 @@ impl cosmic::Application for App {
}
}

fn menu_icon_button<'a>(
applet: &'a cosmic::applet::Context,
menu: &'a status_menu::State,
) -> cosmic::widget::Button<'a, Msg> {
match (menu.icon_pixmap(), menu.icon_name(), menu.icon_theme_path()) {
(Some(icon), "", _) => applet.icon_button_from_handle(icon.clone().symbolic(true)),
(_, name, Some(theme_path)) if name != "" => {
let mut path = theme_path.to_owned();
// XXX right way to lookup icon in dir?
path.push(name.to_owned() + ".png");
let icon = cosmic::widget::icon::from_path(path);
applet.icon_button_from_handle(icon)
}
(_, name, _) => applet.icon_button(name),
}
}

pub fn main() -> iced::Result {
cosmic::applet::run::<App>(())
}
48 changes: 25 additions & 23 deletions cosmic-applet-status-area/src/components/status_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use cosmic::{
iced,
widget::icon,
};
use std::path::{Path, PathBuf};

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

Expand All @@ -26,6 +27,7 @@ pub struct State {
icon_name: String,
// TODO handle icon with multiple sizes?
icon_pixmap: Option<icon::Handle>,
icon_theme_path: Option<PathBuf>,
click_event: Option<(i32, bool)>,
}

Expand All @@ -38,6 +40,7 @@ impl State {
expanded: None,
icon_name: String::new(),
icon_pixmap: None,
icon_theme_path: None,
click_event: None,
},
iced::Task::none(),
Expand All @@ -61,29 +64,24 @@ 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| {
if i.width <= 0 || i.height <= 0 || i.bytes.is_empty() {
// App sent invalid icon data during initialization - show placeholder until NewIcon signal
eprintln!("Skipping invalid icon: {}x{} with {} bytes, app may still be initializing",
i.width, i.height, i.bytes.len());
return icon::from_name("dialog-question").symbolic(true).handle();
}
// 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)
});
}
}
self.icon_name = update.name.unwrap_or_default();
self.icon_pixmap = update.pixmap.and_then(|icons| icons
.into_iter()
.max_by_key(|i| (i.width, i.height))
.map(|mut i| {
if i.width <= 0 || i.height <= 0 || i.bytes.is_empty() {
// App sent invalid icon data during initialization - show placeholder until NewIcon signal
eprintln!("Skipping invalid icon: {}x{} with {} bytes, app may still be initializing",
i.width, i.height, i.bytes.len());
return icon::from_name("dialog-question").symbolic(true).handle();
}
// 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)
}));
self.icon_theme_path = update.theme_path;

iced::Task::none()
}
Expand Down Expand Up @@ -134,6 +132,10 @@ impl State {
self.icon_pixmap.as_ref()
}

pub fn icon_theme_path(&self) -> Option<&Path> {
self.icon_theme_path.as_deref()
}

pub fn popup_view(&self) -> cosmic::Element<'_, Msg> {
if let Some(layout) = self.layout.as_ref() {
layout_view(layout, self.expanded)
Expand Down
36 changes: 17 additions & 19 deletions cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use cosmic::iced::{self, Subscription};
use futures::{FutureExt, StreamExt};
use rustc_hash::FxHashMap;
use std::path::PathBuf;
use zbus::zvariant::{self, OwnedValue};

#[derive(Clone, Debug)]
Expand All @@ -21,9 +22,10 @@ pub struct Icon {
}

#[derive(Clone, Debug)]
pub enum IconUpdate {
Name(String),
Pixmap(Vec<Icon>),
pub struct IconUpdate {
pub name: Option<String>,
pub pixmap: Option<Vec<Icon>>,
pub theme_path: Option<PathBuf>,
}

impl StatusNotifierItem {
Expand Down Expand Up @@ -76,22 +78,15 @@ impl StatusNotifierItem {
}

pub fn icon_subscription(&self) -> iced::Subscription<IconUpdate> {
fn icon_events(
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),
)
async fn icon_events(item_proxy: StatusNotifierItemProxy<'static>) -> IconUpdate {
let icon_name = item_proxy.icon_name().await;
let icon_pixmap = item_proxy.icon_pixmap().await;
let icon_theme_path = item_proxy.icon_theme_path().await.map(PathBuf::from);
IconUpdate {
name: icon_name.ok(),
pixmap: icon_pixmap.ok(),
theme_path: icon_theme_path.ok(),
}
.flatten_stream()
}

let item_proxy = self.item_proxy.clone();
Expand All @@ -101,7 +96,7 @@ impl StatusNotifierItem {
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()))
.then(move |()| icon_events(item_proxy.clone()))
}
.flatten_stream(),
)
Expand All @@ -128,6 +123,9 @@ pub trait StatusNotifierItem {
#[zbus(property)]
fn icon_name(&self) -> zbus::Result<String>;

#[zbus(property)]
fn icon_theme_path(&self) -> zbus::Result<String>;

// https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/Icons
#[zbus(property)]
fn icon_pixmap(&self) -> zbus::Result<Vec<Icon>>;
Expand Down
Loading