1use std::ops::Deref;
23use std::sync::atomic::{self, AtomicI32};
24
25use serde::{Deserialize, Serialize};
26use zbus::zvariant::{self, OwnedValue, Type, Value, as_value::optional};
27
28use zbus::{interface, object_server::SignalEmitter};
29
30pub mod event_types;
31
32pub use event_types::*;
33
34#[derive(Type, Debug, Serialize, Deserialize, Default, OwnedValue, Value, Clone)]
35#[zvariant(signature = "dict")]
37pub struct MenuProperty {
38 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
39 pub label: Option<String>,
40 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
41 #[zvariant(rename = "icon-name")]
42 pub icon_name: Option<String>,
43 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
44 pub enabled: Option<bool>,
45 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
46 #[zvariant(rename = "toggle-type")]
47 pub toggle_type: Option<ToggleType>,
48 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
49 #[zvariant(rename = "toggle-state")]
50 pub toggle_state: Option<ToggleState>,
51 #[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
52 #[zvariant(rename = "children-display")]
53 pub children_display: Option<String>,
54}
55
56impl MenuProperty {
57 pub const LABEL: &str = "label";
59 pub const ICON_NAME: &str = "icon-name";
60 pub const ENABLED: &str = "enabled";
61 pub const TOGGLE_TYPE: &str = "toggle-type";
62 pub const TOGGLE_STATE: &str = "toggle-state";
63 pub const CHILDREN_DISPLAY: &str = "children-display";
64}
65
66impl MenuProperty {
67 pub fn root() -> Self {
68 MenuProperty {
69 label: Some("root".to_owned()),
70 children_display: Some("submenu".to_owned()),
71 ..Default::default()
72 }
73 }
74 pub fn submenu(label: String) -> Self {
75 MenuProperty {
76 label: Some(label),
77 children_display: Some("submenu".to_owned()),
78 ..Default::default()
79 }
80 }
81}
82
83#[derive(
84 Type, Debug, Serialize, Deserialize, OwnedValue, Value, Clone, Copy, PartialEq, PartialOrd,
85)]
86pub struct Id(i32);
87
88static COUNT: AtomicI32 = AtomicI32::new(1);
89
90impl Id {
91 pub const MAIN: Self = Id(0);
92 pub fn unique() -> Id {
94 Id(COUNT.fetch_add(1, atomic::Ordering::Relaxed))
95 }
96}
97
98impl Deref for Id {
99 type Target = i32;
100 fn deref(&self) -> &Self::Target {
101 &self.0
102 }
103}
104
105#[allow(unused)]
106#[derive(Debug, Clone)]
107pub enum MenuType {
108 Root,
109 SubMenu,
110 Button,
111 RadioGroup,
112}
113
114#[derive(Debug, Clone)]
115pub enum MenuUnit<Message: Clone> {
116 StandardButton {
117 id: Id,
118 options: ButtonOptions,
119 message: Message,
120 },
121 Root {
122 sub_menus: Vec<MenuUnit<Message>>,
123 },
124 SubMenu {
125 id: Id,
126 label: String,
127 sub_menus: Vec<MenuUnit<Message>>,
128 },
129 RadioGroup {
130 selections: Vec<MenuUnit<Message>>,
131 },
132 RadioButton {
133 id: Id,
134 options: RadioOptions,
135 message: Message,
136 },
137}
138
139#[derive(Debug, Clone)]
140pub struct MenuTree<Message: Clone>(MenuUnit<Message>);
141
142impl<Message: Clone> Default for MenuTree<Message> {
143 fn default() -> Self {
144 Self::new()
145 }
146}
147
148impl<Message: Clone> MenuTree<Message> {
149 pub fn new() -> Self {
150 Self(MenuUnit::root())
151 }
152 pub fn push_sub_menu(mut self, menu: MenuUnit<Message>) -> Self {
153 self.0 = self.0.push_sub_menu(menu);
154 self
155 }
156 fn get_unit_mut(&mut self) -> &mut MenuUnit<Message> {
157 &mut self.0
158 }
159 pub fn get_unit(&self) -> &MenuUnit<Message> {
160 &self.0
161 }
162}
163
164impl<Message: Clone> From<&MenuTree<Message>> for MenuItem {
165 fn from(value: &MenuTree<Message>) -> Self {
166 (&value.0).into()
167 }
168}
169#[derive(Debug, Clone, Default)]
170pub struct ButtonOptions {
171 pub label: String,
172 pub enabled: bool,
173 pub icon_name: String,
174}
175
176#[derive(Debug, Clone, Default)]
177pub struct RadioOptions {
178 pub label: String,
179 pub enabled: bool,
180 pub icon_name: String,
181 pub toggle_type: ToggleType,
182 pub toggle_state: ToggleState,
183}
184
185#[derive(Debug, Clone)]
186pub struct RadioInitOption<Message: Clone> {
187 pub options: RadioOptions,
188 pub message: Message,
189}
190
191impl<Message: Clone> From<&MenuUnit<Message>> for MenuItem {
192 fn from(value: &MenuUnit<Message>) -> Self {
193 let IdOrGroup::Id(id) = value.id_or_ids() else {
194 panic!("RadioGroup should not be here");
195 };
196 let mut output = MenuItem {
197 id,
198 property: value.get_property().expect("RadioGroup should not be here"),
199 sub_menus: vec![],
200 };
201 let Some(sub_menus) = value.sub_menus() else {
202 return output;
203 };
204 for sub_menu in sub_menus {
205 if let MenuUnit::RadioGroup { selections, .. } = sub_menu {
206 for selection in selections {
207 output = output.push_sub_menu(selection.into());
208 }
209 } else {
210 output = output.push_sub_menu(sub_menu.into());
211 }
212 }
213 output
214 }
215}
216
217enum IdOrGroup {
218 Id(Id),
219 Groups(Vec<Id>),
220}
221
222impl IdOrGroup {
223 fn contains_id(&self, id: i32) -> bool {
224 let id = Id(id);
225 match self {
226 IdOrGroup::Id(oid) => *oid == id,
227 IdOrGroup::Groups(ids) => ids.contains(&id),
228 }
229 }
230}
231
232impl<Message: Clone> MenuUnit<Message> {
233 pub fn try_change_label(&mut self, new_label: String) {
234 match self {
235 Self::RadioButton {
236 options: RadioOptions { label, .. },
237 ..
238 }
239 | Self::SubMenu { label, .. }
240 | Self::StandardButton {
241 options: ButtonOptions { label, .. },
242 ..
243 } => {
244 *label = new_label;
245 }
246 _ => {}
247 }
248 }
249 fn message(&self, id: i32) -> Option<Message> {
250 let fid = Id(id);
251 match self {
252 Self::RadioGroup { selections } => {
253 for selection in selections {
254 let IdOrGroup::Id(oid) = selection.id_or_ids() else {
255 continue;
256 };
257 if oid == fid {
258 return selection.message(id);
259 }
260 }
261 }
262 Self::RadioButton { id, message, .. } | Self::StandardButton { id, message, .. } => {
263 if fid == *id {
264 return Some(message.clone());
265 }
266 }
267 _ => {}
268 }
269 None
270 }
271 fn get_property(&self) -> Option<MenuProperty> {
272 match self {
273 Self::Root { .. } => Some(MenuProperty::root()),
274 Self::SubMenu { label, .. } => Some(MenuProperty::submenu(label.clone())),
275 Self::StandardButton {
276 options:
277 ButtonOptions {
278 label,
279 enabled,
280 icon_name,
281 },
282 ..
283 } => Some(MenuProperty {
284 label: Some(label.clone()),
285 icon_name: Some(icon_name.clone()),
286 enabled: Some(*enabled),
287 ..Default::default()
288 }),
289 Self::RadioButton {
290 options:
291 RadioOptions {
292 label,
293 enabled,
294 icon_name,
295 toggle_state,
296 toggle_type,
297 },
298 ..
299 } => Some(MenuProperty {
300 label: Some(label.clone()),
301 icon_name: Some(icon_name.clone()),
302 enabled: Some(*enabled),
303 toggle_type: Some(*toggle_type),
304 toggle_state: Some(*toggle_state),
305 ..Default::default()
306 }),
307 Self::RadioGroup { .. } => None,
308 }
309 }
310 pub fn sub_menus(&self) -> Option<&Vec<Self>> {
311 match self {
312 Self::Root { sub_menus } | Self::SubMenu { sub_menus, .. } => Some(sub_menus),
313 _ => None,
314 }
315 }
316 pub fn sub_menus_mut(&mut self) -> Option<&mut Vec<Self>> {
317 match self {
318 Self::Root { sub_menus } | Self::SubMenu { sub_menus, .. } => Some(sub_menus),
319 _ => None,
320 }
321 }
322 fn id_or_ids(&self) -> IdOrGroup {
323 match self {
324 Self::Root { .. } => IdOrGroup::Id(Id::MAIN),
325 Self::SubMenu { id, .. }
326 | Self::StandardButton { id, .. }
327 | Self::RadioButton { id, .. } => IdOrGroup::Id(*id),
328 Self::RadioGroup { selections, .. } => {
329 let mut groups = vec![];
330 for selection in selections {
331 match selection.id_or_ids() {
332 IdOrGroup::Id(id) => groups.push(id),
333 IdOrGroup::Groups(mut ids) => {
334 groups.append(&mut ids);
335 }
336 }
337 }
338 IdOrGroup::Groups(groups)
339 }
340 }
341 }
342 pub fn button(options: ButtonOptions, message: Message) -> Self {
343 Self::StandardButton {
344 id: Id::unique(),
345 options,
346 message,
347 }
348 }
349 pub fn root() -> Self {
350 Self::Root { sub_menus: vec![] }
351 }
352 pub fn sub_menu(label: String) -> Self {
353 Self::SubMenu {
354 id: Id::unique(),
355 label,
356 sub_menus: vec![],
357 }
358 }
359
360 pub fn toggle_group(init_options: Vec<RadioInitOption<Message>>) -> Self {
361 let selections = init_options
362 .into_iter()
363 .map(
364 |RadioInitOption { options, message }| MenuUnit::RadioButton {
365 id: Id::unique(),
366 options,
367 message,
368 },
369 )
370 .collect();
371 Self::RadioGroup { selections }
372 }
373
374 pub fn push_sub_menu(mut self, menu: Self) -> Self {
375 let Some(sub_menus) = self.sub_menus_mut() else {
376 return self;
377 };
378 sub_menus.push(menu);
379 self
380 }
381
382 pub fn unit_type(&self) -> MenuType {
383 match self {
384 MenuUnit::Root { .. } => MenuType::Root,
385 MenuUnit::SubMenu { .. } => MenuType::SubMenu,
386 MenuUnit::RadioGroup { .. } => MenuType::RadioGroup,
387 MenuUnit::StandardButton { .. } | MenuUnit::RadioButton { .. } => MenuType::Button,
388 }
389 }
390
391 pub fn find_menu_by_id(&self, id: i32) -> Option<&Self> {
392 if self.id_or_ids().contains_id(id) {
393 return Some(self);
394 }
395 let sub_menus = self.sub_menus()?;
396 for menu in sub_menus {
397 if let Some(menu) = menu.find_menu_by_id(id) {
398 return Some(menu);
399 }
400 }
401 None
402 }
403 pub fn find_menu_by_id_mut(&mut self, id: i32) -> Option<&mut Self> {
404 if self.id_or_ids().contains_id(id) {
405 return Some(self);
406 }
407 let sub_menus = self.sub_menus_mut()?;
408 for menu in sub_menus {
409 if let Some(menu) = menu.find_menu_by_id_mut(id) {
410 return Some(menu);
411 }
412 }
413 None
414 }
415 fn find_menu_and_message_by_id_mut(&mut self, id: i32) -> Option<(&mut Self, Message)> {
416 if self.id_or_ids().contains_id(id) {
417 let message = self.message(id)?;
418 return Some((self, message));
419 }
420 let sub_menus = self.sub_menus_mut()?;
421 for menu in sub_menus {
422 if let Some(menu) = menu.find_menu_and_message_by_id_mut(id) {
423 return Some(menu);
424 }
425 }
426 None
427 }
428}
429
430#[derive(Type, Debug, Serialize, Deserialize, OwnedValue, Value, Clone)]
431#[zvariant(signature = "(ia{sv}av)")]
432pub struct MenuItem {
433 pub id: Id,
434 pub property: MenuProperty,
435 pub sub_menus: Vec<zvariant::OwnedValue>,
436}
437
438#[derive(Clone, PartialEq, Type, Serialize, Deserialize, OwnedValue, Value, Debug, Default)]
439#[zvariant(signature = "s", rename_all = "lowercase")]
440pub enum MenuStatus {
441 #[default]
442 Normal,
443 Notice,
444 Disabled,
445}
446
447impl Default for MenuItem {
448 fn default() -> Self {
449 MenuItem {
450 id: Id::unique(),
451 property: MenuProperty::root(),
452 sub_menus: vec![],
453 }
454 }
455}
456
457impl MenuItem {
458 pub fn new(property: MenuProperty) -> Self {
459 MenuItem {
460 id: Id::unique(),
461 property,
462 sub_menus: vec![],
463 }
464 }
465
466 #[allow(unused)]
467 #[allow(clippy::only_used_in_recursion)]
468 pub fn get_filiter(
469 &self,
470 parent_id: i32,
471 recursion_depth: i32,
472 property_names: &[&str],
473 ) -> Option<MenuItem> {
474 if *self.id == parent_id {
475 let mut new_menu = MenuItem {
476 id: self.id,
477 property: self.property.clone(),
478 sub_menus: vec![],
479 };
480
481 let next_reversion_depth = recursion_depth - 1;
482 if next_reversion_depth != 0 {
483 for menu in self.sub_menus.as_slice() {
484 let menu: MenuItem = menu.clone().try_into().unwrap();
485 let next_menu = menu.filiter(next_reversion_depth, property_names);
486 new_menu = new_menu.push_sub_menu(next_menu);
487 }
488 }
489 return Some(new_menu);
490 }
491 None
492 }
493
494 #[allow(unused)]
495 #[allow(clippy::only_used_in_recursion)]
496 fn filiter(&self, recursion_depth: i32, property_names: &[&str]) -> MenuItem {
497 let mut new_menu = MenuItem {
498 id: self.id,
499 property: self.property.clone(),
500 sub_menus: vec![],
501 };
502
503 let next_reversion_depth = recursion_depth - 1;
504 for menu in self.sub_menus.as_slice() {
505 let menu: MenuItem = menu.clone().try_into().unwrap();
506 let next_menu = menu.filiter(next_reversion_depth, property_names);
507 new_menu = new_menu.push_sub_menu(next_menu);
508 }
509
510 new_menu
511 }
512
513 pub fn push_sub_menu(mut self, menu: MenuItem) -> Self {
514 self.sub_menus.push(OwnedValue::try_from(menu).unwrap());
515 self
516 }
517
518 pub fn get_property(&self, id: i32, name: String) -> Option<PropertyItem> {
519 if *self.id == id {
520 return Some(PropertyItem {
521 id,
522 item: self.property.clone(),
523 });
524 }
525 let sub_menus: Vec<MenuItem> = self
526 .sub_menus
527 .iter()
528 .map(|submenu| submenu.clone().try_into().unwrap())
529 .collect();
530
531 for sub_menu in sub_menus {
532 let property = sub_menu.get_property(id, name.clone());
533 if property.is_some() {
534 return property;
535 }
536 }
537
538 None
539 }
540
541 pub fn get_property_groups(
542 &self,
543 ids: Vec<i32>,
544 _property_names: Vec<String>,
545 ) -> Vec<PropertyItem> {
546 let mut output = vec![];
547 for id in ids {
548 if let Some(property) = self.get_property(id, "".to_string()) {
549 output.push(property);
550 }
551 }
552
553 output
554 }
555}
556
557#[derive(Type, Debug, Default, Serialize, Deserialize)]
558pub struct PropertyItem {
559 pub id: i32,
560 pub item: MenuProperty,
561}
562
563pub trait DBusMenuItem {
564 type State;
565 type Message: Clone;
566
567 fn boot(&self) -> Self::State;
568
569 fn menu(&self) -> MenuTree<Self::Message>;
570
571 fn revision(&self, state: &Self::State) -> u32;
572
573 #[allow(unused)]
574 fn about_to_show(&self, state: &mut Self::State, id: i32) -> zbus::fdo::Result<bool> {
575 Err(zbus::fdo::Error::Failed("Unimplemented".to_string()))
576 }
577
578 #[allow(unused)]
580 fn about_to_show_group(
581 &self,
582 state: &mut Self::State,
583 ids: Vec<i32>,
584 ) -> zbus::fdo::Result<(Vec<i32>, Vec<i32>)> {
585 Err(zbus::fdo::Error::Failed("Unimplemented".to_string()))
586 }
587
588 fn status(&self, _state: &Self::State) -> zbus::fdo::Result<MenuStatus> {
589 Ok(MenuStatus::Normal)
590 }
591
592 #[allow(unused)]
593 fn on_clicked(
594 &self,
595 state: &mut Self::State,
596 button: &mut MenuUnit<Self::Message>,
597 message: Self::Message,
598 timestamp: u32,
599 ) -> EventUpdate {
600 EventUpdate::None
601 }
602
603 #[allow(unused)]
604 fn text_direction(&self, state: &Self::State) -> TextDirection {
605 TextDirection::Inherit
606 }
607
608 #[allow(unused)]
609 fn icon_theme_path(&self, state: &Self::State) -> Vec<String> {
610 vec![]
611 }
612}
613
614pub struct DBusMenuInstance<State, Message>
615where
616 Message: Clone,
617{
618 pub(crate) program: Box<dyn DBusMenuItem<State = State, Message = Message> + Send + Sync>,
619 pub(crate) state: State,
620 pub(crate) menu_tree: MenuTree<Message>,
621}
622
623pub trait DBusMenuBootFn<State> {
624 fn boot(&self) -> State;
625}
626
627impl<T, State> DBusMenuBootFn<State> for T
628where
629 T: Fn() -> State,
630{
631 fn boot(&self) -> State {
632 self()
633 }
634}
635
636pub trait MenuBootFn<Message: Clone> {
637 fn menu(&self) -> MenuTree<Message>;
638}
639
640impl<Message> MenuBootFn<Message> for MenuTree<Message>
641where
642 Message: Clone,
643{
644 fn menu(&self) -> MenuTree<Message> {
645 self.clone()
646 }
647}
648
649impl<T, Message> MenuBootFn<Message> for T
650where
651 Message: Clone,
652 T: Fn() -> MenuTree<Message>,
653{
654 fn menu(&self) -> MenuTree<Message> {
655 self()
656 }
657}
658
659pub trait MenuStatusFn<State> {
660 fn status(&self, state: &State) -> MenuStatus;
661}
662
663impl<T, State> MenuStatusFn<State> for T
664where
665 T: Fn(&State) -> MenuStatus,
666{
667 fn status(&self, state: &State) -> MenuStatus {
668 self(state)
669 }
670}
671pub trait AboutToShowFn<State> {
672 fn about_to_show(&self, state: &mut State, id: i32) -> bool;
673}
674
675impl<T, State> AboutToShowFn<State> for T
676where
677 T: Fn(&mut State, i32) -> bool,
678{
679 fn about_to_show(&self, state: &mut State, id: i32) -> bool {
680 self(state, id)
681 }
682}
683
684#[derive(Debug)]
685pub enum EventUpdate {
686 None,
687 UpdateCurrent,
688 UpdateAll,
689}
690
691pub trait RevisionFn<State> {
692 fn revision(&self, state: &State) -> u32;
693}
694
695impl<State, F> RevisionFn<State> for F
696where
697 F: Fn(&State) -> u32,
698{
699 fn revision(&self, state: &State) -> u32 {
700 self(state)
701 }
702}
703
704impl<State> RevisionFn<State> for u32 {
705 fn revision(&self, _state: &State) -> u32 {
706 *self
707 }
708}
709
710pub trait OnClickedFn<State, Message: Clone> {
711 fn on_clicked(
712 &self,
713 state: &mut State,
714 button: &mut MenuUnit<Message>,
715 message: Message,
716 timestamp: u32,
717 ) -> EventUpdate;
718}
719
720impl<T, State, Message> OnClickedFn<State, Message> for T
721where
722 T: Fn(&mut State, &mut MenuUnit<Message>, Message, u32) -> EventUpdate,
723 Message: Clone,
724{
725 fn on_clicked(
726 &self,
727 state: &mut State,
728 button: &mut MenuUnit<Message>,
729 message: Message,
730 timestamp: u32,
731 ) -> EventUpdate {
732 self(state, button, message, timestamp)
733 }
734}
735
736pub trait TextDirectionFn<State> {
737 fn text_direction(&self, state: &State) -> TextDirection;
738}
739
740impl<State> TextDirectionFn<State> for TextDirection {
741 fn text_direction(&self, _state: &State) -> TextDirection {
742 *self
743 }
744}
745
746impl<T, State> TextDirectionFn<State> for T
747where
748 T: Fn(&State) -> TextDirection,
749{
750 fn text_direction(&self, state: &State) -> TextDirection {
751 self(state)
752 }
753}
754
755pub trait IconThemePathFn<State> {
756 fn icon_theme_path(&self, state: &State) -> Vec<String>;
757}
758
759impl<State> IconThemePathFn<State> for Vec<String> {
760 fn icon_theme_path(&self, _state: &State) -> Vec<String> {
761 self.clone()
762 }
763}
764
765impl<T, State> IconThemePathFn<State> for T
766where
767 T: Fn(&State) -> Vec<String>,
768{
769 fn icon_theme_path(&self, state: &State) -> Vec<String> {
770 self(state)
771 }
772}
773
774pub trait AboutToShowGroupFn<State> {
775 fn about_to_show_group(
776 &self,
777 state: &mut State,
778 ids: Vec<i32>,
779 ) -> zbus::fdo::Result<(Vec<i32>, Vec<i32>)>;
780}
781
782impl<T, State> AboutToShowGroupFn<State> for T
783where
784 T: Fn(&mut State, Vec<i32>) -> zbus::fdo::Result<(Vec<i32>, Vec<i32>)>,
785{
786 fn about_to_show_group(
787 &self,
788 state: &mut State,
789 ids: Vec<i32>,
790 ) -> zbus::fdo::Result<(Vec<i32>, Vec<i32>)> {
791 self(state, ids)
792 }
793}
794
795#[interface(name = "com.canonical.dbusmenu")]
796impl<State, Message> DBusMenuInstance<State, Message>
797where
798 State: 'static + Send + Sync,
799 Message: 'static + Send + Sync + Clone,
800{
801 fn about_to_show(&mut self, id: i32) -> zbus::fdo::Result<bool> {
802 self.program.about_to_show(&mut self.state, id)
803 }
804
805 fn about_to_show_group(&mut self, ids: Vec<i32>) -> zbus::fdo::Result<(Vec<i32>, Vec<i32>)> {
807 self.program.about_to_show_group(&mut self.state, ids)
808 }
809
810 fn get_layout(
813 &mut self,
814 parent_id: i32,
815 recursion_depth: i32,
816 property_names: Vec<String>,
817 ) -> zbus::fdo::Result<(u32, MenuItem)> {
818 let property_names: Vec<&str> = property_names.iter().map(|name| name.as_str()).collect();
819 let menuitem: MenuItem = (&self.menu_tree).into();
820 Ok((
821 self.program.revision(&self.state),
822 menuitem
823 .get_filiter(parent_id, recursion_depth, &property_names)
824 .ok_or(zbus::fdo::Error::Failed("UnFounded".to_string()))?,
825 ))
826 }
827
828 fn get_group_properties(
829 &mut self,
830 ids: Vec<i32>,
831 property_names: Vec<String>,
832 ) -> zbus::fdo::Result<Vec<PropertyItem>> {
833 let menuitem: MenuItem = (&self.menu_tree).into();
834 Ok(menuitem.get_property_groups(ids, property_names))
835 }
836
837 fn get_property(&mut self, id: i32, name: String) -> zbus::fdo::Result<PropertyItem> {
838 let menuitem: MenuItem = (&self.menu_tree).into();
839 menuitem
840 .get_property(id, name)
841 .ok_or(zbus::fdo::Error::Failed("Unfounded".to_string()))
842 }
843
844 #[zbus(property)]
846 fn version(&self) -> u32 {
847 2
848 }
849
850 #[zbus(property)]
852 fn status(&self) -> zbus::fdo::Result<MenuStatus> {
853 self.program.status(&self.state)
854 }
855
856 async fn event(
858 &mut self,
859 id: i32,
860 event_id: String,
861 _data: zbus::zvariant::OwnedValue,
862 timestamp: u32,
863 #[zbus(signal_emitter)] cxts: SignalEmitter<'_>,
864 ) -> zbus::fdo::Result<()> {
865 let menu = self.menu_tree.get_unit_mut();
866
867 let Some((button, message)) = menu.find_menu_and_message_by_id_mut(id) else {
868 return Ok(());
869 };
870 let need_update = match event_id.as_str() {
871 "clicked" => {
872 if !matches!(button.unit_type(), MenuType::Button | MenuType::RadioGroup) {
873 return Ok(());
874 }
875 self.program
876 .on_clicked(&mut self.state, button, message, timestamp)
877 }
878 _ => EventUpdate::None,
879 };
880
881 let revision = self.program.revision(&self.state);
882 match need_update {
883 EventUpdate::UpdateCurrent => {
884 let _ =
885 DBusMenuInstance::<State, Message>::layout_updated(&cxts, revision, id).await;
886 }
887 EventUpdate::UpdateAll => {
888 let _ =
889 DBusMenuInstance::<State, Message>::layout_updated(&cxts, revision, *Id::MAIN)
890 .await;
891 }
892 _ => {}
893 }
894
895 Ok(())
896 }
897
898 async fn event_group(
900 &mut self,
901 events: Vec<(i32, String, zbus::zvariant::OwnedValue, u32)>,
902 #[zbus(signal_emitter)] cxts: SignalEmitter<'_>,
903 ) -> zbus::fdo::Result<Vec<i32>> {
904 let mut output = vec![];
905 let mut update_all = false;
906 let mut update_parents: Vec<i32> = vec![];
907 for (id, event_id, _data, timestamp) in events {
908 let menu = self.menu_tree.get_unit_mut();
909 let Some((button, message)) = menu.find_menu_and_message_by_id_mut(id) else {
910 continue;
911 };
912
913 let need_update = match event_id.as_str() {
914 "clicked" => {
915 if !matches!(button.unit_type(), MenuType::Button | MenuType::RadioGroup) {
916 continue;
917 }
918 self.program
919 .on_clicked(&mut self.state, button, message, timestamp)
920 }
921 _ => {
922 continue;
923 }
924 };
925 match need_update {
926 EventUpdate::None => {
927 continue;
928 }
929 EventUpdate::UpdateAll => {
930 update_all = true;
931 }
932 EventUpdate::UpdateCurrent => {
933 if let IdOrGroup::Id(id) = menu.id_or_ids() {
934 update_parents.push(*id);
935 }
936 }
937 };
938 output.push(id);
939 }
940 let revision = self.program.revision(&self.state);
941 if update_all {
942 let _ = DBusMenuInstance::<State, Message>::layout_updated(&cxts, revision, *Id::MAIN)
943 .await;
944 } else {
945 for id in update_parents {
946 let _ =
947 DBusMenuInstance::<State, Message>::layout_updated(&cxts, revision, id).await;
948 }
949 }
950 Ok(output)
951 }
952
953 #[zbus(property)]
955 fn text_direction(&self) -> TextDirection {
956 self.program.text_direction(&self.state)
957 }
958
959 #[zbus(property)]
960 fn icon_theme_path(&self) -> Vec<String> {
961 self.program.icon_theme_path(&self.state)
962 }
963
964 #[zbus(signal)]
966 pub async fn item_activation_requested(
967 ctxt: &SignalEmitter<'_>,
968 id: i32,
969 timestamp: u32,
970 ) -> zbus::Result<()>;
971
972 #[zbus(signal)]
974 pub async fn items_properties_updated(
975 ctxt: &SignalEmitter<'_>,
976 updated_props: Vec<(i32, MenuProperty)>,
977 removed_props: Vec<(i32, Vec<&str>)>,
978 ) -> zbus::Result<()>;
979
980 #[zbus(signal)]
982 pub async fn layout_updated(
983 ctxt: &SignalEmitter<'_>,
984 revision: u32,
985 parent: i32,
986 ) -> zbus::Result<()>;
987}