From b026db6f7c51a71b3e3cdf8c591ef4f3c85a04e1 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 6 Nov 2025 09:32:54 -0500 Subject: [PATCH] fix(status-area): better handle apps with no menus --- .../src/components/app.rs | 6 +++- .../src/components/status_menu.rs | 24 ++++++++++--- .../src/subscriptions/status_notifier_item.rs | 36 +++++++++++++++---- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/cosmic-applet-status-area/src/components/app.rs b/cosmic-applet-status-area/src/components/app.rs index 4240f2ae..e957971d 100644 --- a/cosmic-applet-status-area/src/components/app.rs +++ b/cosmic-applet-status-area/src/components/app.rs @@ -460,7 +460,11 @@ impl cosmic::Application for App { .icon_button_from_handle(icon.clone().symbolic(true)), _ => self.core.applet.icon_button(menu.icon_name()), } - .on_press_down(Msg::TogglePopup(*id)), + .on_press_down(if menu.item.menu_proxy().is_some() { + Msg::TogglePopup(*id) + } else { + Msg::StatusMenu((*id, status_menu::Msg::Click(0, true))) + }), ) .on_enter(Msg::Hovered(*id)) .into() diff --git a/cosmic-applet-status-area/src/components/status_menu.rs b/cosmic-applet-status-area/src/components/status_menu.rs index 37cb102f..5836510a 100644 --- a/cosmic-applet-status-area/src/components/status_menu.rs +++ b/cosmic-applet-status-area/src/components/status_menu.rs @@ -20,7 +20,7 @@ pub enum Msg { } pub struct State { - item: StatusNotifierItem, + pub item: StatusNotifierItem, layout: Option, expanded: Option, icon_name: String, @@ -101,9 +101,19 @@ impl State { let Some((id, is_submenu)) = self.click_event else { return iced::Task::none(); }; - - let menu_proxy = self.item.menu_proxy().clone(); let item_proxy = self.item.item_proxy().clone(); + + let Some(menu_proxy) = self.item.menu_proxy().cloned() else { + tokio::spawn(async move { + let _ = item_proxy.provide_xdg_activation_token(token).await; + if let Err(err) = item_proxy.activate(0, 0).await { + tracing::error!( + "Error activating status notifier item without menu proxy: {err:?}" + ); + } + }); + return iced::Task::none(); + }; tokio::spawn(async move { let _ = item_proxy.provide_xdg_activation_token(token).await; let _ = menu_proxy.event(id, "clicked", &0.into(), 0).await; @@ -150,7 +160,9 @@ impl State { } pub fn opened(&self) { - let menu_proxy = self.item.menu_proxy().clone(); + let Some(menu_proxy) = self.item.menu_proxy().cloned() else { + return; + }; tokio::spawn(async move { let _ = menu_proxy.event(0, "opened", &0i32.into(), 0).await; let _ = menu_proxy.about_to_show(0).await; @@ -158,7 +170,9 @@ impl State { } pub fn closed(&self) { - let menu_proxy = self.item.menu_proxy().clone(); + let Some(menu_proxy) = self.item.menu_proxy().cloned() else { + return; + }; tokio::spawn(async move { let _ = menu_proxy.event(0, "closed", &0i32.into(), 0).await; }); diff --git a/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs b/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs index c9e17218..88d21c29 100644 --- a/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs +++ b/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs @@ -10,7 +10,7 @@ use zbus::zvariant::{self, OwnedValue}; pub struct StatusNotifierItem { name: String, item_proxy: StatusNotifierItemProxy<'static>, - menu_proxy: DBusMenuProxy<'static>, + menu_proxy: Option>, } #[derive(Clone, Debug, zvariant::Value)] @@ -42,7 +42,22 @@ impl StatusNotifierItem { .build() .await?; - let menu_path = item_proxy.menu().await?; + // XX: some items will not implement this but have a menu anyway + let is_menu = item_proxy.item_is_menu().await; + + let menu_path = item_proxy.menu().await; + + // Why would an item say it has no menu but provide a menu path? Slack does this. + let mut is_menu = menu_path.is_ok() || is_menu.unwrap_or(false); + + if !is_menu { + return Ok(Self { + name, + item_proxy, + menu_proxy: None, + }); + } + let menu_path = menu_path?; let menu_proxy = DBusMenuProxy::builder(connection) .destination(dest.to_string())? .path(menu_path)? @@ -52,7 +67,7 @@ impl StatusNotifierItem { Ok(Self { name, item_proxy, - menu_proxy, + menu_proxy: Some(menu_proxy), }) } @@ -62,7 +77,9 @@ impl StatusNotifierItem { // TODO: Only fetch changed part of layout, if that's any faster pub fn layout_subscription(&self) -> iced::Subscription> { - let menu_proxy = self.menu_proxy.clone(); + let Some(menu_proxy) = self.menu_proxy.clone() else { + return Subscription::none(); + }; Subscription::run_with_id( format!("status-notifier-item-layout-{}", &self.name), async move { @@ -107,8 +124,8 @@ impl StatusNotifierItem { ) } - pub fn menu_proxy(&self) -> &DBusMenuProxy<'static> { - &self.menu_proxy + pub fn menu_proxy(&self) -> Option<&DBusMenuProxy<'static>> { + self.menu_proxy.as_ref() } pub fn item_proxy(&self) -> &StatusNotifierItemProxy<'static> { @@ -135,10 +152,17 @@ pub trait StatusNotifierItem { #[zbus(property)] fn menu(&self) -> zbus::Result; + #[zbus(property)] + fn item_is_menu(&self) -> zbus::Result; + #[zbus(signal)] fn new_icon(&self) -> zbus::Result<()>; fn provide_xdg_activation_token(&self, token: String) -> zbus::Result<()>; + + fn activate(&self, x: i32, y: i32) -> zbus::Result<()>; + + fn secondary_activate(&self, x: i32, y: i32) -> zbus::Result<()>; } #[derive(Clone, Debug)]