diff --git a/src/status_menu.rs b/src/status_menu.rs index 82b14ec8..58174817 100644 --- a/src/status_menu.rs +++ b/src/status_menu.rs @@ -6,17 +6,22 @@ use gtk4::{ prelude::*, subclass::prelude::*, }; -use std::{borrow::Cow, collections::HashMap, fmt, io}; +use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt, io}; use crate::deref_cell::DerefCell; +struct Menu { + box_: gtk4::Box, + children: Vec, +} + #[derive(Default)] pub struct StatusMenuInner { menu_button: DerefCell, vbox: DerefCell, item: DerefCell, - layout: DerefCell, dbus_menu: DerefCell, + menus: RefCell>, } #[glib::object_subclass] @@ -72,12 +77,39 @@ impl StatusMenu { let menu = item.menu().unwrap(); // XXX unwrap? let layout = menu.get_layout(0, -1, &[]).await?.1; + menu.connect_layout_updated(clone!(@weak obj => move |_revision, parent| { + let mut menus = obj.inner().menus.borrow_mut(); + + if let Some(Menu { box_, children }) = menus.remove(&parent) { + let mut next_child = box_.first_child(); + while let Some(child) = next_child { + next_child = child.next_sibling(); + box_.remove(&child); + } + + fn remove_child_menus(menus: &mut HashMap, children: Vec) { + for i in children { + if let Some(menu) = menus.remove(&i) { + remove_child_menus(menus, menu.children); + } + } + } + remove_child_menus(&mut menus, children); + + glib::MainContext::default().spawn_local(clone!(@weak obj => async move { + match obj.inner().dbus_menu.get_layout(parent, -1, &[]).await { + Ok((_, layout)) => obj.populate_menu(&box_, &layout), + Err(err) => eprintln!("Failed to call 'GetLayout': {}", err), + } + })); + } + })); + obj.inner().item.set(item); - obj.inner().layout.set(layout); obj.inner().dbus_menu.set(menu); - println!("{:#?}", &*obj.inner().layout); - obj.populate_menu(&obj.inner().vbox, &obj.inner().layout); + println!("{:#?}", layout); + obj.populate_menu(&obj.inner().vbox, &layout); Ok(obj) } @@ -87,7 +119,11 @@ impl StatusMenu { } fn populate_menu(&self, box_: >k4::Box, layout: &Layout) { + let mut children = Vec::new(); + for i in layout.children() { + children.push(i.id()); + if i.type_().as_deref() == Some("separator") { let separator = gtk4::Separator::new(gtk4::Orientation::Horizontal); box_.append(&separator); @@ -151,6 +187,14 @@ impl StatusMenu { } } } + + self.inner().menus.borrow_mut().insert( + layout.id(), + Menu { + box_: box_.clone(), + children, + }, + ); } } @@ -293,6 +337,19 @@ impl DBusMenu { } }); } + + fn connect_layout_updated(&self, f: F) -> glib::SignalHandlerId { + self.0 + .connect_local("g-signal", false, move |args| { + if &args[2].get::().unwrap() == "LayoutUpdated" { + // XXX unwrap + let (revision, parent) = args[3].get::().unwrap().get().unwrap(); + f(revision, parent); + } + None + }) + .unwrap() + } } struct StatusNotifierItem(gio::DBusProxy, Option);