From a372ce800f4f29f4d3630c150f980dbb78bfcec3 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 28 May 2025 17:01:24 -0400 Subject: [PATCH] wip: popup --- examples/application/src/main.rs | 175 ++++++++------------- src/surface/action.rs | 2 +- src/theme/style/menu_bar.rs | 2 +- src/widget/dropdown/widget.rs | 10 +- src/widget/menu/menu_bar.rs | 172 +++++++++++++++++++-- src/widget/menu/menu_inner.rs | 246 +++++++++++++++++++++++++++--- src/widget/menu/menu_tree.rs | 19 ++- src/widget/responsive_menu_bar.rs | 2 + 8 files changed, 469 insertions(+), 159 deletions(-) diff --git a/examples/application/src/main.rs b/examples/application/src/main.rs index d7501a61..3908cabd 100644 --- a/examples/application/src/main.rs +++ b/examples/application/src/main.rs @@ -46,13 +46,19 @@ impl Page { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Action { Hi, + Hi2, + Hi3, } impl MenuAction for Action { type Message = Message; fn message(&self) -> Message { - Message::Hi + match self { + Action::Hi => Message::Hi, + Action::Hi2 => Message::Hi2, + Action::Hi3 => Message::Hi3, + } } } @@ -86,6 +92,8 @@ pub enum Message { ToggleHide, Surface(cosmic::surface::Action), Hi, + Hi2, + Hi3, } /// The [`App`] stores application-specific state. @@ -176,6 +184,12 @@ impl cosmic::Application for App { Message::Hi => { dbg!("hi"); } + Message::Hi2 => { + dbg!("hi 2"); + } + Message::Hi3 => { + dbg!("hi 3"); + } } Task::none() } @@ -222,123 +236,60 @@ impl cosmic::Application for App { fn header_start(&self) -> Vec> { use cosmic::widget::menu::Tree; - #[cfg(not(feature = "wayland"))] - { + + vec![cosmic::widget::responsive_menu_bar( + self.core(), + &self.keybinds, + MENU_ID.clone(), + Message::Surface, vec![ - cosmic::widget::menu::bar(vec![ - Tree::with_children( - menu::root("hiiiiiiiiiiiiiiiiiii 1"), - menu::items( - &self.keybinds, - vec![menu::Item::Button("hi", None, Action::Hi)], - ), - ), - Tree::with_children( - menu::root("hiiiiiiiiiiiiiiiiii 2"), - menu::items( - &self.keybinds, - vec![menu::Item::Button("hi 2", None, Action::Hi)], - ), - ), - Tree::with_children( - menu::root("hiiiiiiiiiiiiiiiiiiiii 3"), - menu::items( - &self.keybinds, - vec![ - menu::Item::Button("hi 3", None, Action::Hi), - menu::Item::Button("hi 3 #2", None, Action::Hi), - ], - ), - ), - Tree::with_children( - menu::root("hi 3"), - menu::items( - &self.keybinds, - vec![ - menu::Item::Button("hi 3", None, Action::Hi), - menu::Item::Button("hi 3 #2", None, Action::Hi), - menu::Item::Button("hi 3 #3", None, Action::Hi), - ], - ), - ), - Tree::with_children( - menu::root("hi 4"), - menu::items( - &self.keybinds, + ( + "hi 1".into(), + vec![ + menu::Item::Button("hi 12".into(), None, Action::Hi), + menu::Item::Button("hi 13".into(), None, Action::Hi2), + ], + ), + ( + "hi 2".into(), + vec![ + menu::Item::Button("hi 21".into(), None, Action::Hi), + menu::Item::Button("hi 22".into(), None, Action::Hi2), + ], + ), + ( + "hi 3".into(), + vec![ + menu::Item::Button("hi 33".into(), None, Action::Hi), + menu::Item::Button("hi 333".into(), None, Action::Hi2), + menu::Item::Button("hi 3333".into(), None, Action::Hi3), + ], + ), + ( + "hiiiiiiiiiiiiiiiiiii 4".into(), + vec![ + menu::Item::Button("hi 4".into(), None, Action::Hi), + menu::Item::Button("hi 44".into(), None, Action::Hi2), + menu::Item::Button("hi 444".into(), None, Action::Hi3), + menu::Item::Folder( + "nest 4".into(), vec![ + menu::Item::Button("hi 41".into(), None, Action::Hi), + menu::Item::Button("hi 442".into(), None, Action::Hi2), + menu::Item::Button("hi 4443".into(), None, Action::Hi3), menu::Item::Folder( - "hi 41 extra root", - vec![menu::Item::Button("hi 3", None, Action::Hi)], + "nest 2 4".into(), + vec![ + menu::Item::Button("hi 443".into(), None, Action::Hi2), + menu::Item::Button("hi 4444".into(), None, Action::Hi), + ], ), - menu::Item::Button("hi 42", None, Action::Hi), - menu::Item::Button("hi 43", None, Action::Hi), - menu::Item::Button("hi 44", None, Action::Hi), - menu::Item::Button("hi 45", None, Action::Hi), - menu::Item::Button("hi 46", None, Action::Hi), ], ), - ), - ]) - .into(), - ] - } - #[cfg(feature = "wayland")] - { - vec![cosmic::widget::responsive_menu_bar().into_element( - self.core(), - &self.keybinds, - MENU_ID.clone(), - Message::Surface, - vec![ - ( - "hi 1".into(), - vec![ - menu::Item::Button("hi 1".into(), None, Action::Hi), - menu::Item::Button("hi 1 2".into(), None, Action::Hi), - ], - ), - ( - "hi 2".into(), - vec![ - menu::Item::Button("hi 2", None, Action::Hi), - menu::Item::Button("hi 22", None, Action::Hi), - ], - ), - ( - "hi 3".into(), - vec![ - menu::Item::Button("hi 3", None, Action::Hi), - menu::Item::Button("hi 33", None, Action::Hi), - menu::Item::Button("hi 333", None, Action::Hi), - ], - ), - ( - "hiiiiiiiiiiiiiiiiiii 4".into(), - vec![ - menu::Item::Button("hi 4", None, Action::Hi), - menu::Item::Button("hi 44", None, Action::Hi), - menu::Item::Button("hi 444", None, Action::Hi), - menu::Item::Folder( - "nest 4".into(), - vec![ - menu::Item::Button("hi 4", None, Action::Hi), - menu::Item::Button("hi 44", None, Action::Hi), - menu::Item::Button("hi 444", None, Action::Hi), - menu::Item::Folder( - "nest 2 4".into(), - vec![ - menu::Item::Button("hi 4", None, Action::Hi), - menu::Item::Button("hi 44", None, Action::Hi), - menu::Item::Button("hi 444", None, Action::Hi), - ], - ), - ], - ), - ], - ), - ], - )] - } + ], + ), + ], + )] } } diff --git a/src/surface/action.rs b/src/surface/action.rs index e27815eb..fdf2680e 100644 --- a/src/surface/action.rs +++ b/src/surface/action.rs @@ -85,7 +85,7 @@ pub fn simple_subsurface( /// Used to create a popup message from within a widget. #[cfg(all(feature = "wayland", feature = "winit"))] #[must_use] -pub fn simple_popup( +pub fn simple_popup( settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync diff --git a/src/theme/style/menu_bar.rs b/src/theme/style/menu_bar.rs index 7f99a1a5..ed0e657a 100644 --- a/src/theme/style/menu_bar.rs +++ b/src/theme/style/menu_bar.rs @@ -42,7 +42,7 @@ pub enum MenuBarStyle { #[default] Default, /// A [`Theme`] that uses a `Custom` palette. - Custom(Arc>), + Custom(Arc + Send + Sync>), } impl From Appearance> for MenuBarStyle { diff --git a/src/widget/dropdown/widget.rs b/src/widget/dropdown/widget.rs index 76ee7b05..d196215d 100644 --- a/src/widget/dropdown/widget.rs +++ b/src/widget/dropdown/widget.rs @@ -536,15 +536,7 @@ pub fn update< let on_close = surface::action::destroy_popup(id); let on_surface_action_clone = on_surface_action.clone(); let translation = layout.virtual_offset(); - let get_popup_action = surface::action::simple_popup::< - AppMessage, - Box< - dyn Fn() -> Element<'static, crate::Action> - + Send - + Sync - + 'static, - >, - >( + let get_popup_action = surface::action::simple_popup::( move || { SctkPopupSettings { parent, diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index dd998e81..5cdbf2d2 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -15,6 +15,7 @@ use crate::{ widget::{ RcWrapper, dropdown::menu::{self, State}, + menu::menu_inner::init_root_menu, }, }; @@ -57,6 +58,7 @@ pub(crate) struct MenuBarStateInner { pub(crate) menu_states: Vec>, } impl MenuBarStateInner { + /// get the list of indices hovered for the menu pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator + '_ { self.menu_states .get(index) @@ -188,7 +190,7 @@ pub struct MenuBar { menu_roots: Vec>, style: ::Style, window_id: window::Id, - #[cfg(feature = "wayland")] + #[cfg(all(feature = "wayland", feature = "winit"))] positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, pub(crate) on_surface_action: Option Message>>, } @@ -228,7 +230,7 @@ where menu_roots, style: ::Style::default(), window_id: window::Id::NONE, - #[cfg(feature = "wayland")] + #[cfg(all(feature = "wayland", feature = "winit"))] positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(), on_surface_action: None, } @@ -322,7 +324,8 @@ where self } - #[cfg(feature = "wayland")] + #[cfg(all(feature = "wayland", feature = "winit"))] + pub fn with_positioner( mut self, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, @@ -405,6 +408,7 @@ where ) } + #[allow(clippy::too_many_lines)] fn on_event( &mut self, tree: &mut Tree, @@ -441,28 +445,170 @@ where if reset { if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { dbg!("reset destroy"); - // TODO emit message if let Some(handler) = self.on_surface_action.as_ref() { shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id))); state.reset(); } } } + }); - let tree = &mut state.tree; + // let tree: &mut _ = &mut state.tree; - match event { - Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { + match event { + Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { + my_state.inner.with_data_mut(|state| { if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { state.view_cursor = view_cursor; state.open = true; - // #[cfg(feature = "wayland")] - // TODO emit Message to open menu } + }); + #[cfg(all(feature = "wayland", feature = "winit"))] + + dbg!( + self.window_id != window::Id::NONE, + self.on_surface_action.is_some() + ); + // TODO emit Message to open menu + if self.window_id != window::Id::NONE && self.on_surface_action.is_some() { + use crate::surface::action::destroy_popup; + use iced_runtime::platform_specific::wayland::popup::{ + SctkPopupSettings, SctkPositioner, + }; + + let surface_action = self.on_surface_action.as_ref().unwrap(); + let old_active_root = my_state.inner.with_data(|state| { + state + .active_root + .get(0) + .filter(|r| r.len() == 1) + .map(|r| r[0]) + }); + + // if position is not on menu bar button skip. + let position = view_cursor.position(); + let hovered_root = layout + .children() + .position(|lo| view_cursor.is_over(lo.bounds())); + dbg!(old_active_root); + // TODO why exit here? + // if hovered_root + // .zip(old_active_root.as_ref()) + // .is_none_or(|r| r.0 != *r.1) + // { + // panic!(); + // } + + let (id, root_list) = my_state.inner.with_data_mut(|state| { + if let Some(id) = state.popup_id.get(&self.window_id).copied() { + // close existing popups + state.menu_states.clear(); + state.active_root.clear(); + shell.publish(surface_action(destroy_popup(id))); + state.view_cursor = view_cursor; + (id, layout.children().map(|lo| lo.bounds()).collect()) + } else { + ( + window::Id::unique(), + layout.children().map(|lo| lo.bounds()).collect(), + ) + } + }); + + let mut popup_menu: Menu<'static, _> = Menu { + tree: my_state.clone(), + menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()), + bounds_expand: self.bounds_expand, + menu_overlays_parent: false, + close_condition: self.close_condition, + item_width: self.item_width, + item_height: self.item_height, + bar_bounds: layout.bounds(), + main_offset: self.main_offset, + cross_offset: self.cross_offset, + root_bounds_list: root_list, + path_highlight: self.path_highlight, + style: std::borrow::Cow::Owned(self.style.clone()), + position: Point::new(0., 0.), + is_overlay: false, + window_id: id, + depth: 0, + }; + + init_root_menu( + &mut popup_menu, + renderer, + shell, + view_cursor.position().unwrap(), + viewport.size(), + Vector::new(0., 0.), + layout.bounds(), + self.main_offset as f32, + ); + let anchor_rect = my_state.inner.with_data(|state| { + state.menu_states[0] + .iter() + .find(|s| s.index.is_none()) + .map(|s| s.menu_bounds.parent_bounds) + .map_or_else( + || { + let bounds = layout.bounds(); + Rectangle { + x: bounds.x as i32, + y: bounds.y as i32, + width: bounds.width as i32, + height: bounds.height as i32, + } + }, + |r| Rectangle { + x: r.x as i32, + y: r.y as i32, + width: r.width as i32, + height: r.height as i32, + }, + ) + }); + + let menu_node = + popup_menu.layout(renderer, Limits::NONE.min_width(1.).min_height(1.)); + let popup_size = menu_node.size(); + let positioner = SctkPositioner { + size: Some((popup_size.width.ceil() as u32 + 2, popup_size.height.ceil() as u32 + 2)), + anchor_rect, + anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft, + gravity:cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight, + reactive: true, + ..Default::default() + }; + let parent = self.window_id; + shell.publish((surface_action)(crate::surface::action::simple_popup( + move || SctkPopupSettings { + parent, + id, + positioner: positioner.clone(), + parent_size: None, + grab: true, + close_with_children: true, + input_zone: None, + }, + Some(move || { + Element::from( + crate::widget::container(popup_menu.clone()).center(Length::Fill), + ) + .map(crate::action::app) + }), + ))); } - _ => (), } - }); + // Window(Focused) => { + // if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { + // dbg!("window focused"); + // shell.publish(Message::from(SurfaceMessage::DestroyPopup(popup_id))); + // } + // state.reset(); + // } + _ => (), + } root_status } @@ -533,8 +679,8 @@ where _renderer: &Renderer, translation: Vector, ) -> Option> { - //#[cfg(feature = "wayland")] - //return None; + #[cfg(all(feature = "wayland", feature = "winit"))] + return None; let state = tree.state.downcast_ref::(); if state diff --git a/src/widget/menu/menu_inner.rs b/src/widget/menu/menu_inner.rs index e9c2d233..5ff73053 100644 --- a/src/widget/menu/menu_inner.rs +++ b/src/widget/menu/menu_inner.rs @@ -232,11 +232,11 @@ pub(super) struct MenuSlice { #[derive(Clone)] /// Menu bounds in overlay space -struct MenuBounds { +pub struct MenuBounds { child_positions: Vec, child_sizes: Vec, children_bounds: Rectangle, - parent_bounds: Rectangle, + pub parent_bounds: Rectangle, check_bounds: Rectangle, offset_bounds: Rectangle, } @@ -299,7 +299,7 @@ pub(crate) struct MenuState { /// The index of the active menu item pub(super) index: Option, scroll_offset: f32, - menu_bounds: MenuBounds, + pub menu_bounds: MenuBounds, } impl MenuState { pub(super) fn layout( @@ -444,6 +444,7 @@ impl MenuState { } } +#[derive(Clone)] pub(crate) struct Menu<'b, Message: std::clone::Clone> { pub(crate) tree: MenuBarState, // Flattened menu tree @@ -471,7 +472,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { overlay::Element::new(Box::new(self)) } - fn layout(&mut self, renderer: &crate::Renderer, limits: Limits) -> Node { + pub(crate) fn layout(&self, renderer: &crate::Renderer, limits: Limits) -> Node { // layout children; let position = self.position; let mut intrinsic_size = Size::ZERO; @@ -889,6 +890,207 @@ impl overlay::Overlay Widget + for Menu<'a, Message> +{ + fn size(&self) -> Size { + Size { + width: Length::Shrink, + height: Length::Shrink, + } + } + + fn layout( + &self, + _tree: &mut Tree, + renderer: &crate::Renderer, + limits: &iced_core::layout::Limits, + ) -> iced_core::layout::Node { + // dbg!(self.window_id, limits); + Menu::layout(self, renderer, *limits) + } + + fn draw( + &self, + _tree: &Tree, + renderer: &mut crate::Renderer, + theme: &crate::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + Menu::draw(self, renderer, theme, style, layout, cursor); + } + + fn on_event( + &mut self, + _tree: &mut Tree, + event: iced::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &crate::Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) -> event::Status { + let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); + // #[cfg(feature = "wayland")] + // if let Some((new_root, new_ms)) = new_root { + // use iced_runtime::platform_specific::wayland::popup::{ + // SctkPopupSettings, SctkPositioner, + // }; + // let mut guard = self.tree.inner.lock().unwrap(); + // let popup_id = *guard + // .popup_id + // .entry(self.window_id) + // .or_insert_with(window::Id::unique); + // let active_roots = &guard.active_root[&self.window_id]; + // // dbg!(active_roots); + // let root_bounds_list = active_roots + // .into_iter() + // .fold(layout, |l, active_root| { + // // dbg!(active_root); + // l.children().nth(*active_root).unwrap() + // }) + // .children() + // .map(|c| c.bounds()) + // .collect(); + // drop(guard); + + // // dbg!(&root_bounds_list); + // // dbg!(self.menu_roots.clone().len()); + // // dbg!(self + // // .menu_roots + // // .clone() + // // .iter() + // // .map(|r| r.index) + // // .collect::>()); + // let mut popup_menu = Menu { + // tree: self.tree.clone(), + // menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())), + // bounds_expand: self.bounds_expand, + // menu_overlays_parent: false, + // close_condition: self.close_condition, + // item_width: self.item_width, + // item_height: self.item_height, + // bar_bounds: layout.bounds(), + // main_offset: self.main_offset, + // cross_offset: self.cross_offset, + // root_bounds_list, + // path_highlight: self.path_highlight, + // style: Cow::Owned(Cow::into_owned(self.style.clone())), + // position: Point::new(0., 0.), + // is_overlay: false, + // window_id: popup_id, + // depth: self.depth + 1, + // }; + // let mut guard = self.tree.inner.lock().unwrap(); + // // dbg!(guard.menu_states.keys()); + // let Some(parent_root) = guard.active_root.get(&self.window_id) else { + // // TODO log warning + // return status; + // }; + // let mut roots = parent_root.clone(); + // roots.push(new_root); + // // dbg!(&roots); + // guard.active_root.insert(popup_id, roots); + // _ = guard.menu_states.remove(&popup_id); + // drop(guard); + // init_root_popup_menu( + // &mut popup_menu, + // renderer, + // shell, + // cursor.position().unwrap(), + // layout.bounds().size(), + // Vector::new(0., 0.), + // layout.bounds(), + // self.main_offset as f32, + // ); + // let mut guard = self.tree.inner.lock().unwrap(); + + // guard + // .menu_states + // .get_mut(&self.window_id) + // .unwrap() + // .push(new_ms); + + // let anchor_rect = guard.menu_states[&self.window_id] + // .iter() + // .find(|s| s.index.is_none()) + // .map(|s| s.menu_bounds.parent_bounds) + // .map_or_else( + // || { + // let bounds = layout.bounds(); + // Rectangle { + // x: bounds.x as i32, + // y: bounds.y as i32, + // width: bounds.width as i32, + // height: bounds.height as i32, + // } + // }, + // |r| Rectangle { + // x: r.x as i32, + // y: r.y as i32, + // width: r.width as i32, + // height: r.height as i32, + // }, + // ); + // // dbg!(&anchor_rect); + + // drop(guard); + // let menu_node = Widget::layout( + // &popup_menu, + // &mut Tree::empty(), + // renderer, + // &Limits::NONE.min_width(1.).min_height(1.), + // ); + // // dbg!(menu_node.size()); + + // // dbg!(&menu_node); + + // let popup_size = menu_node.size(); + // let positioner = SctkPositioner { + // size: Some((popup_size.width.ceil() as u32 + 2, popup_size.height.ceil() as u32 + 2)), + // anchor_rect, + // anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::TopRight, + // gravity:cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight, + // reactive: true, + // ..Default::default() + // }; + // let parent = self.window_id; + // // dbg!(&positioner); + // shell.publish(crate::app::message::simple_popup( + // move || SctkPopupSettings { + // parent, + // id: popup_id, + // positioner: positioner.clone(), + // parent_size: None, + // grab: true, + // close_with_children: true, + // }, + // Some(move || { + // crate::Element::from( + // crate::widget::container(popup_menu.clone()).center(Length::Fill), + // ) + // .map(crate::app::Message::App) + // }), + // )); + // } + status + } +} + +impl<'a, Message> From> + for iced::Element<'a, Message, crate::Theme, crate::Renderer> +where + Message: std::clone::Clone + 'static, +{ + fn from(value: Menu<'a, Message>) -> Self { + Self::new(value) + } +} + fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { Rectangle { x: rect.x - padding.left, @@ -1194,34 +1396,48 @@ fn process_menu_events<'b, Message: std::clone::Clone>( Cow::Owned(o) => o.as_mut_slice(), }; my_state.inner.with_data_mut(|state| { + dbg!(&state.active_root); let Some(active_root) = state.active_root.get(menu.depth).cloned() else { return Status::Ignored; }; + dbg!("got the active root"); let indices = state.get_trimmed_indices(menu.depth); - let indices = if is_overlay { - indices.collect::>() - } else { - indices.take(1).collect::>() - }; + let indices = indices.collect::>(); + // if is_overlay { + // indices.collect::>() + // } else { + // indices.take(1).collect::>() + // }; if indices.is_empty() { return Status::Ignored; } + dbg!(&active_root, &indices); // get active item // let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]); - let (tree, mt) = active_root.iter().take(menu.depth).fold( + let (tree, mt) = indices.iter().take(indices.len()).fold( ( &mut state.tree.children[active_root[0]].children, &mut menu_roots[active_root[0]], ), |(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), ); + dbg!(mt.children.len(), mt.index,); + // let Some(i) = state.menu_states[menu.depth].iter().position(|ms| { + // ms.menu_bounds + // .check_bounds + // .contains(view_cursor.position().unwrap_or(Point { x: -1., y: -1. })) + // }) else { + // return Status::Ignored; + // }; + // let mt = &mut mt.children[indices[indices.len() - 1]]; // widget tree let tree = &mut tree[mt.index]; + dbg!(tree.children.len()); // get layout let last_ms = &state.menu_states[menu.depth][indices.len() - 1]; @@ -1234,7 +1450,9 @@ fn process_menu_events<'b, Message: std::clone::Clone>( ); let child_layout = Layout::new(&child_node); + dbg!("item on event handler..."); // process only the last widget + dbg!(&event); mt.item.on_event( tree, event, @@ -1369,13 +1587,7 @@ where - (last_children_bounds.y + last_menu_state.scroll_offset)) .clamp(0.0, last_children_bounds.height - 0.001); // dbg!(height_diff); - // let (tree, active_menu_root) = active_root.iter().skip(1).fold( - // ( - // &mut state.tree.children[active_root[0]].children, - // &menu.menu_roots[active_root[0]], - // ), - // |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]), - // ); + let (active_tree, roots) = active_root.iter().take(menu.depth).fold( ( &mut state.tree.children[active_root[0]].children, diff --git a/src/widget/menu/menu_tree.rs b/src/widget/menu/menu_tree.rs index 71f93231..82ed6db8 100644 --- a/src/widget/menu/menu_tree.rs +++ b/src/widget/menu/menu_tree.rs @@ -213,7 +213,7 @@ where pub fn menu_items< A: MenuAction, L: Into> + 'static, - Message: 'static + std::clone::Clone, + Message: 'static + std::clone::Clone + std::fmt::Debug, >( key_binds: &HashMap, children: Vec>, @@ -238,9 +238,10 @@ pub fn menu_items< match item { MenuItem::Button(label, icon, action) => { + let l: Cow<'static, str> = label.into(); let key = find_key(&action, key_binds); let mut items = vec![ - widget::text(label).into(), + widget::text(l.clone()).into(), widget::horizontal_space().into(), widget::text(key).into(), ]; @@ -250,15 +251,18 @@ pub fn menu_items< items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); } - let menu_button = menu_button(items).on_press(action.message()); + // dbg!("button with action...", action.message()); + let menu_button = menu_button(items).on_press(action.message()).description(l); trees.push(MenuTree::::from(Element::from(menu_button))); } MenuItem::ButtonDisabled(label, icon, action) => { + let l: Cow<'static, str> = label.into(); + let key = find_key(&action, key_binds); let mut items = vec![ - widget::text(label).into(), + widget::text(l.clone()).into(), widget::horizontal_space().into(), widget::text(key).into(), ]; @@ -268,7 +272,7 @@ pub fn menu_items< items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); } - let menu_button = menu_button(items); + let menu_button = menu_button(items).description((l.clone())); trees.push(MenuTree::::from(Element::from(menu_button))); } @@ -305,16 +309,19 @@ pub fn menu_items< ))); } MenuItem::Folder(label, children) => { + let l: Cow<'static, str> = label.into(); + trees.push(MenuTree::::with_children( RcElementWrapper::new(crate::Element::from( menu_button::<'static, _>(vec![ - widget::text(label).into(), + widget::text(l.clone()).into(), widget::horizontal_space().into(), widget::icon::from_name("pan-end-symbolic") .size(16) .icon() .into(), ]) + .description(l.clone()) .class( // Menu folders have no on_press so they take on the disabled style by default if children.is_empty() { diff --git a/src/widget/responsive_menu_bar.rs b/src/widget/responsive_menu_bar.rs index b08e86ea..e5416151 100644 --- a/src/widget/responsive_menu_bar.rs +++ b/src/widget/responsive_menu_bar.rs @@ -97,6 +97,7 @@ impl ResponsiveMenuBar { .item_width(self.item_width) .item_height(self.item_height) .spacing(self.spacing) + .on_surface_action(action_message.clone()) .window_id_maybe(core.main_window_id()), crate::widget::Id::new(format!("menu_bar_expanded_{id}")), ), @@ -131,6 +132,7 @@ impl ResponsiveMenuBar { .item_height(self.item_height) .item_width(self.collapsed_item_width) .spacing(self.spacing) + .on_surface_action(action_message.clone()) .window_id_maybe(core.main_window_id()), crate::widget::Id::new(format!("menu_bar_collapsed_{id}")), ),