From 3fd6c4f6bced58ddc8822a9b3c16d7b22763d515 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 9 Jun 2025 10:38:33 -0400 Subject: [PATCH] popup refactor --- examples/application/Cargo.toml | 2 +- examples/application/src/main.rs | 26 +- src/widget/menu/menu_bar.rs | 111 +++---- src/widget/menu/menu_inner.rs | 519 +++++++----------------------- src/widget/menu/menu_tree.rs | 5 +- src/widget/responsive_menu_bar.rs | 2 +- 6 files changed, 180 insertions(+), 485 deletions(-) diff --git a/examples/application/Cargo.toml b/examples/application/Cargo.toml index 18cf3b61..e5ae2f30 100644 --- a/examples/application/Cargo.toml +++ b/examples/application/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [features] -default = [] +default = ["wayland"] wayland = ["libcosmic/wayland"] [dependencies] diff --git a/examples/application/src/main.rs b/examples/application/src/main.rs index b37e263f..c1aba67f 100644 --- a/examples/application/src/main.rs +++ b/examples/application/src/main.rs @@ -257,16 +257,16 @@ impl cosmic::Application for App { menu::Item::Button("hi 21", None, Action::Hi), menu::Item::Button("hi 22", None, Action::Hi2), menu::Item::Folder( - "nest 3 2".into(), + "nest 3 2 >".into(), vec![ - menu::Item::Button("31", None, Action::Hi), - menu::Item::Button("342", None, Action::Hi2), - menu::Item::Button("3443", None, Action::Hi3), + menu::Item::Button("21", None, Action::Hi), + menu::Item::Button("242", None, Action::Hi2), + menu::Item::Button("2443", None, Action::Hi3), menu::Item::Folder( - "nest 4 2".into(), + "nest 4 2 >".into(), vec![ - menu::Item::Button("343", None, Action::Hi2), - menu::Item::Button("3444", None, Action::Hi), + menu::Item::Button("243", None, Action::Hi2), + menu::Item::Button("2444", None, Action::Hi), ], ), ], @@ -276,9 +276,12 @@ impl cosmic::Application for App { ( "hi 3".into(), vec![ - menu::Item::Button("hi 33", None, Action::Hi), - menu::Item::Button("hi 333", None, Action::Hi2), + menu::Item::Button("hi 31", None, Action::Hi), + menu::Item::Button("hi 332", None, Action::Hi2), menu::Item::Button("hi 3333", None, Action::Hi3), + menu::Item::Button("hi 33334", None, Action::Hi3), + menu::Item::Button("hi 333335", None, Action::Hi3), + menu::Item::Button("hi 3333336", None, Action::Hi3), ], ), ( @@ -288,13 +291,12 @@ impl cosmic::Application for App { menu::Item::Button("hi 44", None, Action::Hi2), menu::Item::Button("hi 444", None, Action::Hi3), menu::Item::Folder( - "nest 4".into(), + "nest 4 >".into(), vec![ menu::Item::Button("hi 41", None, Action::Hi), menu::Item::Button("hi 442", None, Action::Hi2), - menu::Item::Button("hi 4443", None, Action::Hi3), menu::Item::Folder( - "nest 2 4".into(), + "nest 3 4 >".into(), vec![ menu::Item::Button("hi 443", None, Action::Hi2), menu::Item::Button("hi 4444", None, Action::Hi), diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index 6c4bcea3..8658f29a 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -51,25 +51,23 @@ pub(crate) struct MenuBarStateInner { pub(crate) bar_pressed: bool, pub(crate) view_cursor: Cursor, pub(crate) open: bool, - pub(crate) active_root: Vec>, + pub(crate) active_root: Vec, pub(crate) horizontal_direction: Direction, pub(crate) vertical_direction: Direction, /// List of all menu states - pub(crate) menu_states: Vec>, + 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) - .into_iter() - .flat_map(|v| v.iter()) + .iter() + .skip(index) .take_while(|ms| ms.index.is_some()) .map(|ms| ms.index.expect("No indices were found in the menu state.")) } pub(super) fn reset(&mut self) { - dbg!("reset"); self.open = false; self.active_root = Vec::new(); self.menu_states.clear(); @@ -205,13 +203,7 @@ where pub fn new(menu_roots: Vec>) -> Self { let mut menu_roots = menu_roots; menu_roots.iter_mut().for_each(MenuTree::set_index); - // println!("======================================================"); - // for menu_root in &menu_roots { - // dbg!(menu_root.index); - // for inner_root in &menu_root.children { - // dbg!(inner_root.index); - // } - // } + Self { width: Length::Shrink, height: Length::Shrink, @@ -326,7 +318,6 @@ where } #[cfg(all(feature = "wayland", feature = "winit"))] - pub fn with_positioner( mut self, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, @@ -335,11 +326,13 @@ where self } + #[must_use] pub fn window_id(mut self, id: window::Id) -> Self { self.window_id = id; self } + #[must_use] pub fn window_id_maybe(mut self, id: Option) -> Self { if let Some(id) = id { self.window_id = id; @@ -347,6 +340,7 @@ where self } + #[must_use] pub fn on_surface_action( mut self, handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static, @@ -355,6 +349,8 @@ where self } + #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] + #[allow(clippy::too_many_lines)] fn create_popup( &mut self, layout: Layout<'_>, @@ -363,11 +359,7 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, my_state: &mut MenuBarState, - ) -> event::Status { - let mut status = event::Status::Ignored; - - #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] - // 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::{ @@ -375,22 +367,20 @@ where }; 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]) - }); + let old_active_root = my_state + .inner + .with_data(|state| state.active_root.get(0).copied()); // 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())); - if old_active_root == hovered_root { - return status; + if old_active_root + .zip(hovered_root) + .is_some_and(|r| r.0 == r.1) + { + return; } let (id, root_list) = my_state.inner.with_data_mut(|state| { if let Some(id) = state.popup_id.get(&self.window_id).copied() { @@ -441,7 +431,8 @@ where ); let anchor_rect = my_state.inner.with_data_mut(|state| { state.popup_id.insert(self.window_id, id); - state.menu_states[0] + state + .menu_states .iter() .find(|s| s.index.is_none()) .map(|s| s.menu_bounds.parent_bounds) @@ -491,7 +482,6 @@ where }), ))); } - status } } impl Widget for MenuBar @@ -588,8 +578,6 @@ where let open = my_state.inner.with_data_mut(|state| { if reset { if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { - dbg!("reset destroy"); - if let Some(handler) = self.on_surface_action.as_ref() { shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id))); state.reset(); @@ -607,17 +595,22 @@ where state.view_cursor = view_cursor; state.open = true; create_popup = true; - } else if let Some(id) = state.popup_id.remove(&self.window_id) { + } else if let Some(_id) = state.popup_id.remove(&self.window_id) { state.menu_states.clear(); state.active_root.clear(); - let surface_action = self.on_surface_action.as_ref().unwrap(); state.open = false; #[cfg(all( feature = "wayland", feature = "winit", feature = "surface-message" ))] - shell.publish(surface_action(crate::surface::action::destroy_popup(id))); + { + let surface_action = self.on_surface_action.as_ref().unwrap(); + + shell.publish(surface_action(crate::surface::action::destroy_popup( + _id, + ))); + } state.view_cursor = view_cursor; } create_popup @@ -626,38 +619,15 @@ where if !create_popup { return event::Status::Ignored; } - - return root_status.merge(self.create_popup( - layout, - view_cursor, - renderer, - shell, - viewport, - my_state, - )); + #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] + self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state); } Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered) if open && view_cursor.is_over(layout.bounds()) => { - return root_status.merge(self.create_popup( - layout, - view_cursor, - renderer, - shell, - viewport, - my_state, - )); + #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] + self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state); } - // Window(Focused) => { - // my_state.inner.with_data_mut(|state| { - // if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { - // if let Some(handler) = self.on_surface_action.as_ref() { - // shell.publish((handler)(destroy_popup(popup_id))); - // state.reset(); - // } - // } - // }); - // } _ => (), } @@ -686,11 +656,7 @@ where // draw path highlight if self.path_highlight.is_some() { let styling = theme.appearance(&self.style); - if let Some(active) = state - .active_root - .get(0) - .and_then(|active_root| active_root.get(0)) - { + if let Some(active) = state.active_root.get(0) { let active_bounds = layout .children() .nth(*active) @@ -734,16 +700,13 @@ where _renderer: &Renderer, translation: Vector, ) -> Option> { - #[cfg(all(feature = "wayland", feature = "winit"))] + #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] return None; let state = tree.state.downcast_ref::(); - if state - .inner - .with_data(|state| !state.open || state.active_root.is_empty()) - { + if state.inner.with_data(|state| !state.open) { return None; - }; + } Some( Menu { diff --git a/src/widget/menu/menu_inner.rs b/src/widget/menu/menu_inner.rs index ec5300c7..3b783af0 100644 --- a/src/widget/menu/menu_inner.rs +++ b/src/widget/menu/menu_inner.rs @@ -230,7 +230,7 @@ pub(super) struct MenuSlice { pub(super) upper_bound_rel: f32, } -#[derive(Clone)] +#[derive(Debug, Clone)] /// Menu bounds in overlay space pub struct MenuBounds { child_positions: Vec, @@ -322,9 +322,6 @@ impl MenuState { // self.menu_bounds.child_positions.len() // ); - // for mt in &menu_tree.children[start_index..=end_index] { - // dbg!(mt.index); - // } // viewport space children bounds let children_bounds = self.menu_bounds.children_bounds + overlay_offset; @@ -336,7 +333,6 @@ impl MenuState { .map(|(i, ((cp, size), mt))| { let mut position = *cp; let mut size = *size; - // dbg!(position, size); if position < lower_bound_rel && (position + size.height) > lower_bound_rel { size.height = position + size.height - lower_bound_rel; @@ -345,13 +341,9 @@ impl MenuState { { size.height = upper_bound_rel - position; } - // dbg!(size); let limits = Limits::new(size, size); - // dbg!(i, mt.index, tree.len()); - // for child in &mt.children { - // dbg!(child.index); - // } + mt.item .layout(&mut tree[mt.index], renderer, &limits) @@ -359,11 +351,8 @@ impl MenuState { }) .collect::>(); - // dbg!(children_bounds.size()); - let node = Node::with_children(children_bounds.size(), child_nodes) - .move_to(children_bounds.position()); - // dbg!(&node); - node + Node::with_children(children_bounds.size(), child_nodes) + .move_to(children_bounds.position()) } fn layout_single( @@ -479,29 +468,30 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { let empty = Vec::new(); self.tree.inner.with_data_mut(|data| { + if data.active_root.len() < self.depth + 1 || data.menu_states.len() < self.depth + 1 { + return Node::new(Size::ZERO); + } let overlay_offset = Point::ORIGIN - position; let tree_children = &mut data.tree.children; - let children = data - .active_root - .get(self.depth) - .cloned() + let children = (if self.is_overlay {0} else {self.depth}..=self.depth) .map(|active_root| { - if active_root.is_empty() || self.menu_roots.is_empty() { + if self.menu_roots.is_empty() { return (&empty, vec![]); } - let (active_tree, roots) = active_root + let (active_tree, roots) = data.active_root[..=active_root] .iter() - .skip(if self.is_overlay { 0 } else { 1 }) + .skip(1) .fold( ( - &mut tree_children[active_root[0]].children, - &self.menu_roots[active_root[0]].children, + &mut tree_children[data.active_root[0]].children, + &self.menu_roots[data.active_root[0]].children, ), - |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), + |(tree, mt), next_active_root| { + (tree, &mt[*next_active_root].children) + }, ); - if let Some(ms) = data.menu_states.get(self.depth) { - ms.iter() + data.menu_states[if self.is_overlay {0} else {self.depth}..=self.depth].iter() .enumerate() .filter(|ms| self.is_overlay || ms.0 < 1) .fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| { @@ -528,23 +518,19 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { nodes, ) }) - } else { - (&empty, vec![]) - } - }) - .map(|(_, l)| l) - .unwrap_or_default(); - // dbg!(intrinsic_size); + + }).map(|(_, l)| l).next().unwrap_or_default(); + // overlay space viewport rectangle - let node = Node::with_children( + Node::with_children( limits.resolve(Length::Shrink, Length::Shrink, intrinsic_size), children, ) - .translate(Point::ORIGIN - position); - node + .translate(Point::ORIGIN - position) }) } + #[allow(clippy::too_many_lines)] fn on_event( &mut self, event: event::Event, @@ -618,10 +604,9 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => { let view_cursor = Cursor::Available(position); let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; - if !(self.is_overlay || view_cursor.is_over(viewport)) { + if !self.is_overlay && !view_cursor.is_over(viewport) { return (None, menu_status); } - // dbg!(view_cursor, viewport, self.window_id); let (new_root, status) = process_overlay_events( self, @@ -631,10 +616,8 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { view_cursor, overlay_cursor, self.cross_offset as f32, - self.is_overlay, shell, ); - dbg!(new_root.as_ref().map(|new_root| new_root.0)); return (new_root, status.merge(menu_status)); } @@ -651,7 +634,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .distance(view_cursor.position().unwrap_or_default()) < 2.0 { - let is_inside = state.menu_states[self.depth] + let is_inside = state.menu_states[..=self.depth] .iter() .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); let mut needs_reset = false; @@ -665,7 +648,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { needs_reset |= self.close_condition.click_outside && !is_inside; if needs_reset { - dbg!("reset"); if let Some(handler) = self.on_surface_action.as_ref() { let mut root = self.window_id; let mut depth = self.depth; @@ -679,7 +661,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { root = parent.0.clone(); depth = depth.saturating_sub(1); } - dbg!(root); shell .publish((handler)(crate::surface::Action::DestroyPopup(root))); } @@ -704,7 +685,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { (None, ret) } - #[allow(unused_results)] + #[allow(unused_results, clippy::too_many_lines)] fn draw( &self, renderer: &mut crate::Renderer, @@ -717,9 +698,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { if !state.open || state.active_root.len() <= self.depth { return; } - let Some(active_root) = state.active_root.get(self.depth) else { - return; - }; + let active_root = &state.active_root[..=self.depth]; let viewport = layout.bounds(); let viewport_size = viewport.size(); let overlay_offset = Point::ORIGIN - viewport.position(); @@ -733,7 +712,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { let styling = theme.appearance(&self.style); let (active_tree, roots) = active_root .iter() - .skip(if self.is_overlay { 0 } else { 1 }) + .skip(1) .fold( ( &state.tree.children[active_root[0]].children, @@ -742,8 +721,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), ); let indices = state.get_trimmed_indices(self.depth).collect::>(); - - state.menu_states[self.depth] + state.menu_states[if self.is_overlay {0} else {self.depth}..=self.depth] .iter() .zip(layout.children()) .enumerate() @@ -812,7 +790,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { } } if start_index < menu_roots.len() { - // dbg!(start_index, end_index, menu_roots.len()); // draw item menu_roots[start_index..=end_index] .iter() @@ -838,7 +815,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .map_or(menu_roots, |active| &menu_roots[active].children) }, ); - }) + }); } } impl overlay::Overlay @@ -897,7 +874,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget iced_core::layout::Node { - // dbg!("layout", self.window_id, limits); Menu::layout(self, renderer, *limits) } @@ -928,7 +904,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget event::Status { let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); - // dbg!(new_root.as_ref().map(|r| r.0)); #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] if let Some((new_root, new_ms)) = new_root { use iced_runtime::platform_specific::wayland::popup::{ @@ -948,11 +923,9 @@ impl<'a, Message: std::clone::Clone + 'static> Widget Widget>()); let mut popup_menu = Menu { tree: self.tree.clone(), menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())), @@ -997,24 +962,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget Widget Widget Widget( if !(state .menu_states .get(menu.depth) - .is_none_or(|s| s.is_empty()) + .is_none() && (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) || menu.depth > 0 || !state.open { - // dbg!("exiting from root menu init early...", menu.depth); return; } - dbg!( - menu.depth, - menu.window_id, - &menu.root_bounds_list, - state.menu_states.get(menu.depth).is_none() - ); - dbg!(menu.menu_roots.len()); - dbg!( - state - .menu_states - .get(menu.depth) - .is_none_or(|s| s.is_empty()) - ); - dbg!( - menu.menu_roots - .as_slice() - .iter() - .map(|r| r.index) - .collect::>() - ); - - dbg!( - state - .active_root - .get(menu.depth) - .into_iter() - .flatten() - .count() - .saturating_sub(1) - ); - // let root = state - // .active_root - // .get(menu.depth) - // .iter() - // .map(|l| l.into_iter()) - // .flatten() - // .take( - // state - // .active_root - // .get(menu.depth) - // .into_iter() - // .flatten() - // .count() - // .saturating_sub(1), - // ) - // .fold(menu.menu_roots.as_slice(), |m, i| { - // dbg!(m.len()); - // m[*i].children.as_slice() - // }); - // dbg!(root.len()); - dbg!(menu.depth); - let mut set = false; for (i, (&root_bounds, mt)) in menu .root_bounds_list @@ -1227,14 +1093,10 @@ pub(super) fn init_root_menu( .enumerate() { if mt.children.is_empty() { - // dbg!("skipping menu with no children"); continue; } - dbg!(mt.children.len()); - dbg!(i, root_bounds.contains(overlay_cursor)); + if root_bounds.contains(overlay_cursor) { - dbg!(root_bounds); - // dbg!(i, root_bounds, mt.width, mt.height); let view_center = viewport_size.width * 0.5; let rb_center = root_bounds.center_x(); @@ -1268,22 +1130,14 @@ pub(super) fn init_root_menu( menu.is_overlay, ); set = true; - dbg!(i); - dbg!("inserting root menu state"); - dbg!(&menu_bounds.children_bounds); - dbg!((&menu_bounds.child_positions)); - dbg!(&state.active_root); - state.active_root.insert(menu.depth, vec![i]); - // do we need to insert the rest now too? + state.active_root.insert(menu.depth, i); let ms = MenuState { index: None, scroll_offset: 0.0, menu_bounds, }; - dbg!("pushing to menu states..."); - let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth); - v.push(ms); + state.menu_states.push(ms); // Hack to ensure menu opens properly shell.invalidate_layout(); @@ -1292,12 +1146,12 @@ pub(super) fn init_root_menu( } } if !set { - dbg!(overlay_cursor); panic!("huh"); } }); } +#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] pub(super) fn init_root_popup_menu( menu: &mut Menu<'_, Message>, renderer: &crate::Renderer, @@ -1310,61 +1164,29 @@ pub(super) fn init_root_popup_menu( ) where Message: std::clone::Clone, { - dbg!("init"); menu.tree.inner.with_data_mut(|state| { if !(state .menu_states .get(menu.depth) - .is_none_or(|s| s.is_empty()) + .is_none() && (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) { - dbg!("exiting init early", menu.depth); return; } - println!( - "init root menu {} {:?}", - menu.window_id, menu.root_bounds_list - ); - // dbg!(state - // .menu_states - // .get(&menu.window_id) - // .is_none_or(|s| s.is_empty())); - // dbg!(menu - // .menu_roots - // .as_slice() - // .iter() - // .map(|r| r.index) - // .collect::>()); - - // dbg!(state - // .active_root - // .get(&menu.window_id) - // .into_iter() - // .flatten() - // .count()); - let active_roots = state - .active_root - .get(menu.depth) - .cloned() - .unwrap_or_default(); - dbg!(&active_roots); - dbg!(menu.menu_roots.len(), menu.root_bounds_list.len()); + let active_roots = &state + .active_root[..=menu.depth]; let mut set = false; - let mt = active_roots - .iter() - .skip(if menu.is_overlay { 0 } else { 1 }) + let mt = active_roots.iter() + .skip(1) .fold(&menu.menu_roots[active_roots[0]], |mt, next_active_root| { &mt.children[*next_active_root] }); let i = active_roots.last().unwrap(); let root_bounds = menu.root_bounds_list[*i]; - if mt.children.is_empty() { - panic!("skipping menu with no children"); - } - dbg!(root_bounds, overlay_cursor); + assert!(!mt.children.is_empty(), "skipping menu with no children"); let aod = Aod { horizontal: true, vertical: true, @@ -1389,8 +1211,7 @@ pub(super) fn init_root_popup_menu( &mut state.tree.children[0].children, menu.is_overlay, ); - dbg!(mt.children.len(), root_bounds, overlay_cursor, i,); - dbg!(i, root_bounds, mt.width, mt.height); + let view_center = viewport_size.width * 0.5; let rb_center = root_bounds.center_x(); @@ -1399,12 +1220,6 @@ pub(super) fn init_root_popup_menu( } else { Direction::Positive }; - - // dbg!( - // state.tree.children.len(), - // state.tree.children[0].children.len(), - // ); - set = true; let ms = MenuState { @@ -1412,23 +1227,18 @@ pub(super) fn init_root_popup_menu( scroll_offset: 0.0, menu_bounds, }; - let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth); - v.push(ms); - dbg!(v.len(), menu.depth); - dbg!(i); + state.menu_states.push(ms); // Hack to ensure menu opens properly shell.invalidate_layout(); - - if !set { - panic!("oops"); - } - }) + // non tree buttons arent active? + assert!(set, "oops"); + }); } #[allow(clippy::too_many_arguments)] -fn process_menu_events<'b, Message: std::clone::Clone>( - menu: &'b mut Menu, +fn process_menu_events( + menu: &mut Menu, event: event::Event, view_cursor: Cursor, renderer: &crate::Renderer, @@ -1438,70 +1248,44 @@ fn process_menu_events<'b, Message: std::clone::Clone>( ) -> event::Status { use event::Status; - let window_id = menu.window_id; - let is_overlay = menu.is_overlay; let my_state = &mut menu.tree; let menu_roots = match &mut menu.menu_roots { Cow::Borrowed(_) => panic!(), Cow::Owned(o) => o.as_mut_slice(), }; my_state.inner.with_data_mut(|state| { - let Some(active_root) = state.active_root.get(menu.depth).cloned() else { + if state.active_root.len() <= menu.depth { + return event::Status::Ignored; + } + + let Some(hover) = state.menu_states.last_mut() else { return Status::Ignored; }; - let indices = state.get_trimmed_indices(menu.depth); - - let indices = indices.collect::>(); - // if is_overlay { - // indices.collect::>() - // } else { - // indices.take(1).collect::>() - // }; - - if indices.is_empty() { + let Some(hover_index) = hover.index else { return Status::Ignored; - } + }; - // get active item - // let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]); - // 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| { - // dbg!(mt.children.len(), next_active_root); - // (tree, &mut mt.children[*next_active_root]) - // }, - // ); - let (tree, mt) = active_root + let (tree, mt) = state.active_root .iter() - .skip(if menu.is_overlay { 0 } else { 1 }) + .skip(1) .fold( + // then use menu states for each open menu ( - &mut state.tree.children[active_root[0]].children, - &mut menu_roots[active_root[0]], + &mut state.tree.children[state.active_root[0]].children, + &mut menu_roots[state.active_root[0]], ), |(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), ); - // 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 mt = &mut mt.children[hover_index]; let tree = &mut tree[mt.index]; // get layout - let last_ms = &state.menu_states[menu.depth][indices.len() - 1]; - let child_node = last_ms.layout_single( + let child_node = hover.layout_single( overlay_offset, - last_ms.index.expect("missing index within menu state."), + hover.index.expect("missing index within menu state."), renderer, mt, tree, @@ -1531,8 +1315,7 @@ fn process_overlay_events( view_cursor: Cursor, overlay_cursor: Point, cross_offset: f32, - is_overlay: bool, - shell: &mut Shell<'_, Message>, + _shell: &mut Shell<'_, Message>, ) -> (Option<(usize, MenuState)>, event::Status) where Message: std::clone::Clone, @@ -1550,12 +1333,7 @@ where let mut new_menu_root = None; menu.tree.inner.with_data_mut(|state| { - let Some(active_root) = state.active_root.get(menu.depth).clone() else { - if is_overlay && !menu.bar_bounds.contains(overlay_cursor) { - state.reset(); - } - return (new_menu_root, Ignored); - }; + let active_root = &state.active_root[..=menu.depth]; if state.pressed { return (new_menu_root, Ignored); @@ -1566,28 +1344,24 @@ where state.view_cursor = view_cursor; // * remove invalid menus - dbg!( - &state - .menu_states - .iter() - .map(|s| s.iter().map(|s| s.index).collect::>()) - .collect::>() - ); + let mut prev_bounds = std::iter::once(menu.bar_bounds) .chain( - (state.menu_states[..menu.depth]) - .iter() - .map(|s| s[0].menu_bounds.children_bounds), + if menu.is_overlay { + state.menu_states[..state.menu_states.len().saturating_sub(1)].iter() + } else { + state.menu_states[..menu.depth].iter() + } + .map(|s| s.menu_bounds.children_bounds), ) .collect::>(); - let menu_states = state.menu_states.get_mut(menu.depth).unwrap(); - - if menu.close_condition.leave { - for i in (0..menu_states.len()).rev() { - let mb = &menu_states[i].menu_bounds; + + if menu.is_overlay && menu.close_condition.leave { + for i in (0..state.menu_states.len()).rev() { + let mb = &state.menu_states[i].menu_bounds; if mb.parent_bounds.contains(overlay_cursor) - || is_overlay && mb.children_bounds.contains(overlay_cursor) + || menu.is_overlay && mb.children_bounds.contains(overlay_cursor) || mb.offset_bounds.contains(overlay_cursor) || (mb.check_bounds.contains(overlay_cursor) && prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))) @@ -1595,31 +1369,18 @@ where break; } prev_bounds.pop(); - menu_states.pop(); - } - } else { - for i in (0..menu_states.len()).rev() { - let mb = &menu_states[i].menu_bounds; - if mb.parent_bounds.contains(overlay_cursor) - || mb.children_bounds.contains(overlay_cursor) - || prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)) - { - break; - } - prev_bounds.pop(); - menu_states.pop(); + state.menu_states.pop(); } } - // get indices - let indices = menu_states.iter().map(|ms| ms.index).collect::>(); - let should_add = is_overlay || menu_states.len() < 2; - dbg!(&indices); - - // dbg!(menu_states.iter().map(|m| m.index).collect::>()); // * update active item - let Some(last_menu_state) = menu_states.get_mut(0) else { + let menu_states_len = state.menu_states.len(); + let Some(last_menu_state) = state.menu_states.get_mut(if menu.is_overlay { + menu_states_len - 1 + } else { + menu.depth + }) else { if menu.is_overlay { // no menus left // TODO do we want to avoid this for popups? @@ -1633,7 +1394,6 @@ where } } - dbg!("no last menu state", menu.depth, menu_states.len()); return (new_menu_root, Captured); }; @@ -1641,26 +1401,27 @@ where let last_parent_bounds = last_menu_bounds.parent_bounds; let last_children_bounds = last_menu_bounds.children_bounds; - if (is_overlay && !menu.menu_overlays_parent && last_parent_bounds.contains(overlay_cursor)) + if (menu.is_overlay && !menu.menu_overlays_parent && last_parent_bounds.contains(overlay_cursor)) // cursor is in the parent part - || is_overlay && !last_children_bounds.contains(overlay_cursor) + || menu.is_overlay && !last_children_bounds.contains(overlay_cursor) // cursor is outside { last_menu_state.index = None; - dbg!("cursor is outside..."); return (new_menu_root, Captured); } // cursor is in the children part + // TODO set active root here even when not a tree. + // ensure that the + // calc new index let height_diff = (overlay_cursor.y - (last_children_bounds.y + last_menu_state.scroll_offset)) .clamp(0.0, last_children_bounds.height - 0.001); - // dbg!(height_diff); let (active_tree, roots) = active_root .iter() - .skip(if menu.is_overlay { 0 } else { 1 }) + .skip(1) .fold( ( &mut state.tree.children[active_root[0]].children, @@ -1668,22 +1429,8 @@ where ), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), ); - if is_overlay { - panic!(); - } - let active_menu = if is_overlay { - indices[0..indices.len().saturating_sub(1)].iter().fold( - roots, - |mt: &Vec>, i| { - &mt[i.expect("missing active child index in menu")].children - }, - ) - } else { - // popup does one layer deep - roots - }; - // dbg!(active_menu.children.len()); - // dbg!(menu.window_id); + + let active_menu = roots; let new_index = match menu.item_height { ItemHeight::Uniform(u) => (height_diff / f32::from(u)).floor() as usize, ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { @@ -1698,41 +1445,32 @@ where ) } }; - let remove = last_menu_state - .index - .as_ref() - .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); + + + let remove = !menu.is_overlay + && last_menu_state + .index + .as_ref() + .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] - if remove { - if let Some(id) = state.popup_id.remove(&menu.window_id) { - state.active_root.pop(); - shell.publish((menu.on_surface_action.as_ref().unwrap())({ - crate::surface::action::destroy_popup(id) - })) - }; + { + if remove { + if let Some(id) = state.popup_id.remove(&menu.window_id) { + state.active_root.truncate(menu.depth + 1); + _shell.publish((menu.on_surface_action.as_ref().unwrap())({ + crate::surface::action::destroy_popup(id) + })); + } + } } - let item = &active_menu[new_index]; - // dbg!(new_index); - // dbg!(item.index); - // if the index changes, get a new root - // if !last_menu_state.index.is_some_and(|old| old == new_index) && !item.children.is_empty() { - // dbg!(&indices); - // dbg!(new_index); - // dbg!(item.children.len()); - // dbg!(menu.window_id); - // new_menu_root = Some(new_index); - // } // set new index let old_index = last_menu_state.index.replace(new_index); - // dbg!(should_add); - dbg!(old_index, new_index); // get new active item // * add new menu if the new item is a menu - if !item.children.is_empty() { - // dbg!("its a menu"); + if !item.children.is_empty() && old_index.is_none_or(|i| i != new_index) { let item_position = Point::new( 0.0, last_menu_bounds.child_positions[new_index] + last_menu_state.scroll_offset, @@ -1770,19 +1508,14 @@ where menu.is_overlay, ), }; - if !old_index.is_some_and(|old| old == new_index) { - // dbg!("adding new menu"); - new_menu_root = Some((new_index, ms.clone())); - } - if should_add { - let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth); - v.push(ms); - } + + new_menu_root = Some((new_index, ms.clone())); + state.menu_states.truncate(menu.depth + 1); + state.menu_states.push(ms); + } else if remove { + state.menu_states.truncate(menu.depth + 1); } - if remove { - state.menu_states.pop(); - } - + (new_menu_root, Captured) }) } @@ -1801,7 +1534,6 @@ where use mouse::ScrollDelta; menu.tree.inner.with_data_mut(|state| { - dbg!(state.active_root.len()); let delta_y = match delta { ScrollDelta::Lines { y, .. } => y * 60.0, @@ -1817,13 +1549,12 @@ where (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); (max_offset, min_offset) }; - let menu_states = state.menu_states.get_mut(0).unwrap(); // update - if menu_states.is_empty() { + if state.menu_states.is_empty() { return Ignored; - } else if menu_states.len() == 1 { - let last_ms = &mut menu_states[0]; + } else if state.menu_states.len() == 1 { + let last_ms = &mut state.menu_states[0]; if last_ms.index.is_none() { return Captured; @@ -1833,8 +1564,8 @@ where last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset); } else { // >= 2 - let max_index = menu_states.len() - 1; - let last_two = &mut menu_states[max_index - 1..=max_index]; + let max_index = state.menu_states.len() - 1; + let last_two = &mut state.menu_states[max_index - 1..=max_index]; if last_two[1].index.is_some() { // scroll the last one @@ -1927,7 +1658,7 @@ fn get_children_layout( .collect(), }; - let max_index = menu_tree.children.len() - 1; + let max_index = menu_tree.children.len().saturating_sub(1); let child_positions: Vec = std::iter::once(0.0) .chain(child_sizes[0..max_index].iter().scan(0.0, |acc, x| { *acc += x.height; diff --git a/src/widget/menu/menu_tree.rs b/src/widget/menu/menu_tree.rs index 82ed6db8..3a875fa1 100644 --- a/src/widget/menu/menu_tree.rs +++ b/src/widget/menu/menu_tree.rs @@ -252,7 +252,7 @@ pub fn menu_items< } // dbg!("button with action...", action.message()); - let menu_button = menu_button(items).on_press(action.message()).description(l); + let menu_button = menu_button(items).on_press(action.message()); trees.push(MenuTree::::from(Element::from(menu_button))); } @@ -272,7 +272,7 @@ pub fn menu_items< items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); } - let menu_button = menu_button(items).description((l.clone())); + let menu_button = menu_button(items); trees.push(MenuTree::::from(Element::from(menu_button))); } @@ -321,7 +321,6 @@ pub fn menu_items< .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 af9d7f76..41b4e00a 100644 --- a/src/widget/responsive_menu_bar.rs +++ b/src/widget/responsive_menu_bar.rs @@ -86,7 +86,7 @@ impl ResponsiveMenuBar { crate::widget::RcElementWrapper::new(Element::from( menu::root(mt.0), )), - menu::items(key_binds, mt.1.into()), + menu::items(key_binds, mt.1), ) }) .collect(),