diff --git a/cosmic-applet-status-area/src/components/app.rs b/cosmic-applet-status-area/src/components/app.rs index 8b5f0d4f..3ba8ff33 100644 --- a/cosmic-applet-status-area/src/components/app.rs +++ b/cosmic-applet-status-area/src/components/app.rs @@ -4,6 +4,8 @@ use cosmic::{ app, applet::cosmic_panel_config::PanelAnchor, + applet::token::subscription::{activation_token_subscription, TokenRequest, TokenUpdate}, + cctk::sctk::reexports::calloop, iced::{ self, overlay::menu, @@ -15,6 +17,7 @@ use cosmic::{ Element, Task, }; use std::collections::BTreeMap; +use zbus::connection::socket::channel; use crate::{components::status_menu, subscriptions::status_notifier_watcher}; @@ -29,10 +32,11 @@ pub enum Msg { Surface(surface::Action), ToggleOverflow, HoveredOverflow, + Token(TokenUpdate), } #[derive(Default)] -struct App { +pub(crate) struct App { core: app::Core, connection: Option, menus: BTreeMap, @@ -40,6 +44,7 @@ struct App { max_menu_id: usize, popup: Option, overflow_popup: Option, + token_tx: Option>, } impl App { @@ -167,7 +172,7 @@ impl cosmic::Application for App { } Msg::StatusMenu((id, msg)) => match self.menus.get_mut(&id) { Some(state) => state - .update(msg) + .update(msg, id, self.token_tx.as_ref()) .map(move |msg| cosmic::action::app(Msg::StatusMenu((id, msg)))), None => Task::none(), }, @@ -267,6 +272,32 @@ impl cosmic::Application for App { } Task::none() } + Msg::Token(u) => match u { + TokenUpdate::Init(tx) => { + self.token_tx = Some(tx); + return Task::none(); + } + TokenUpdate::Finished => { + self.token_tx = None; + return Task::none(); + } + TokenUpdate::ActivationToken { token, exec: id } => { + if let Some(((state, id), token)) = str::parse(&id) + .ok() + .and_then(|id: usize| self.menus.get_mut(&id).map(|m| (m, id))) + .zip(token) + { + return state + .update( + status_menu::Msg::ClickToken(token), + id, + self.token_tx.as_ref(), + ) + .map(move |msg| cosmic::action::app(Msg::StatusMenu((id, msg)))); + } + return Task::none(); + } + }, Msg::Hovered(id) => { let mut cmds = Vec::new(); if let Some(old_id) = self.open_menu.take() { @@ -417,6 +448,7 @@ impl cosmic::Application for App { for (id, menu) in self.menus.iter() { subscriptions.push(menu.subscription().with(*id).map(Msg::StatusMenu)); } + subscriptions.push(activation_token_subscription(0).map(Msg::Token)); iced::Subscription::batch(subscriptions) } diff --git a/cosmic-applet-status-area/src/components/status_menu.rs b/cosmic-applet-status-area/src/components/status_menu.rs index 2f2851ed..983ca368 100644 --- a/cosmic-applet-status-area/src/components/status_menu.rs +++ b/cosmic-applet-status-area/src/components/status_menu.rs @@ -2,9 +2,14 @@ // SPDX-License-Identifier: GPL-3.0-only use cosmic::{ - applet::menu_button, - iced::{self, Padding}, + applet::{ + menu_button, + token::{self, subscription::TokenRequest}, + }, + cctk::sctk::reexports::calloop, + iced, widget::icon, + Application, }; use crate::subscriptions::status_notifier_item::{IconUpdate, Layout, StatusNotifierItem}; @@ -14,6 +19,7 @@ pub enum Msg { Layout(Result), Icon(IconUpdate), Click(i32, bool), + ClickToken(String), } pub struct State { @@ -23,6 +29,7 @@ pub struct State { icon_name: String, // TODO handle icon with multiple sizes? icon_pixmap: Option, + click_event: Option<(i32, bool)>, } impl State { @@ -34,12 +41,18 @@ impl State { expanded: None, icon_name: String::new(), icon_pixmap: None, + click_event: None, }, iced::Task::none(), ) } - pub fn update(&mut self, message: Msg) -> iced::Task { + pub fn update( + &mut self, + message: Msg, + menu_id: usize, + token_tx: Option<&calloop::channel::Sender>, + ) -> iced::Task { match message { Msg::Layout(layout) => { match layout { @@ -78,8 +91,24 @@ impl State { iced::Task::none() } Msg::Click(id, is_submenu) => { + if let Some(token_tx) = token_tx { + _ = token_tx.send(TokenRequest { + app_id: super::app::App::APP_ID.to_string(), + exec: menu_id.to_string(), + }); + } + self.click_event = Some((id, is_submenu)); + iced::Task::none() + } + Msg::ClickToken(token) => { + 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(); tokio::spawn(async move { + let _ = item_proxy.provide_xdg_activation_token(token).await; let _ = menu_proxy.event(id, "clicked", &0.into(), 0).await; }); if is_submenu { 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 76a2eb5b..173b2ce8 100644 --- a/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs +++ b/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs @@ -113,6 +113,10 @@ impl StatusNotifierItem { pub fn menu_proxy(&self) -> &DBusMenuProxy<'static> { &self.menu_proxy } + + pub fn item_proxy(&self) -> &StatusNotifierItemProxy<'static> { + &self.item_proxy + } } async fn get_layout(menu_proxy: DBusMenuProxy<'static>) -> Result { @@ -136,6 +140,8 @@ pub trait StatusNotifierItem { #[zbus(signal)] fn new_icon(&self) -> zbus::Result<()>; + + fn provide_xdg_activation_token(&self, token: String) -> zbus::Result<()>; } #[derive(Clone, Debug)]