This commit is contained in:
Ashley Wulber 2025-04-29 13:16:02 -04:00
parent 9e9449d302
commit 2cfef8814e
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
2 changed files with 147 additions and 115 deletions

View file

@ -50,15 +50,15 @@ pub(crate) struct MenuBarStateInner {
pub(crate) bar_pressed: bool, pub(crate) bar_pressed: bool,
pub(crate) view_cursor: Cursor, pub(crate) view_cursor: Cursor,
pub(crate) open: bool, pub(crate) open: bool,
pub(crate) active_root: HashMap<window::Id, Vec<usize>>, pub(crate) active_root: Vec<Vec<usize>>,
pub(crate) horizontal_direction: Direction, pub(crate) horizontal_direction: Direction,
pub(crate) vertical_direction: Direction, pub(crate) vertical_direction: Direction,
pub(crate) menu_states: HashMap<window::Id, Vec<MenuState>>, pub(crate) menu_states: Vec<Vec<MenuState>>,
} }
impl MenuBarStateInner { impl MenuBarStateInner {
pub(super) fn get_trimmed_indices(&self, id: &window::Id) -> impl Iterator<Item = usize> + '_ { pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ {
self.menu_states self.menu_states
.get(id) .get(index)
.into_iter() .into_iter()
.map(|v| v.iter()) .map(|v| v.iter())
.flatten() .flatten()
@ -68,7 +68,7 @@ impl MenuBarStateInner {
pub(super) fn reset(&mut self) { pub(super) fn reset(&mut self) {
self.open = false; self.open = false;
self.active_root = HashMap::new(); self.active_root = Vec::new();
self.menu_states.clear(); self.menu_states.clear();
} }
} }
@ -79,10 +79,10 @@ impl Default for MenuBarStateInner {
pressed: false, pressed: false,
view_cursor: Cursor::Available([-0.5, -0.5].into()), view_cursor: Cursor::Available([-0.5, -0.5].into()),
open: false, open: false,
active_root: HashMap::new(), active_root: Vec::new(),
horizontal_direction: Direction::Positive, horizontal_direction: Direction::Positive,
vertical_direction: Direction::Positive, vertical_direction: Direction::Positive,
menu_states: HashMap::new(), menu_states: Vec::new(),
popup_id: HashMap::new(), popup_id: HashMap::new(),
bar_pressed: false, bar_pressed: false,
} }
@ -161,6 +161,15 @@ where
} }
} }
pub fn get_mut_or_default<T: Default>(vec: &mut Vec<T>, index: usize) -> &mut T {
if index < vec.len() {
&mut vec[index]
} else {
vec.resize_with(index + 1, T::default);
&mut vec[index]
}
}
/// A `MenuBar` collects `MenuTree`s and handles all the layout, event processing, and drawing. /// A `MenuBar` collects `MenuTree`s and handles all the layout, event processing, and drawing.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct MenuBar<Message> { pub struct MenuBar<Message> {
@ -479,11 +488,7 @@ where
// draw path highlight // draw path highlight
if self.path_highlight.is_some() { if self.path_highlight.is_some() {
let styling = theme.appearance(&self.style); let styling = theme.appearance(&self.style);
if let Some(active) = state if let Some(active) = get_mut_or_default(&mut state.active_root, 0).get(0) {
.active_root
.get(&self.window_id)
.and_then(|active| active.get(0))
{
let active_bounds = layout let active_bounds = layout
.children() .children()
.nth(*active) .nth(*active)
@ -527,11 +532,14 @@ where
_renderer: &Renderer, _renderer: &Renderer,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
// #[cfg(feature = "wayland")] //#[cfg(feature = "wayland")]
// return None; //return None;
let state = tree.state.downcast_ref::<MenuBarState>(); let state = tree.state.downcast_ref::<MenuBarState>();
if state.inner.with_data_mut(|state| !state.open) { if state
.inner
.with_data_mut(|state| !state.open || state.active_root.is_empty())
{
return None; return None;
}; };

View file

@ -329,7 +329,7 @@ impl MenuState {
// for mt in &menu_tree.children[start_index..=end_index] { // for mt in &menu_tree.children[start_index..=end_index] {
// dbg!(mt.index); // dbg!(mt.index);
// } // }
dbg!((start_index, end_index)); dbg!((start_index, end_index, menu_tree.len()));
// viewport space children bounds // viewport space children bounds
let children_bounds = self.menu_bounds.children_bounds + overlay_offset; let children_bounds = self.menu_bounds.children_bounds + overlay_offset;
@ -360,7 +360,7 @@ impl MenuState {
} }
mt.item mt.item
.layout(&mut tree[mt.index], renderer, &limits) .layout(&mut tree[i], renderer, &limits)
.move_to(Point::new(0.0, position + self.scroll_offset)) .move_to(Point::new(0.0, position + self.scroll_offset))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -469,21 +469,28 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
pub(crate) fn overlay(self) -> overlay::Element<'b, Message, crate::Theme, crate::Renderer> { pub(crate) fn overlay(self) -> overlay::Element<'b, Message, crate::Theme, crate::Renderer> {
overlay::Element::new(Box::new(self)) overlay::Element::new(Box::new(self))
} }
fn layout(&mut self, renderer: &crate::Renderer, limits: Limits) -> Node { fn layout(&mut self, renderer: &crate::Renderer, limits: Limits) -> Node {
// layout children // layout children;
let position = self.position; let position = self.position;
let mut intrinsic_size = Size::ZERO; let mut intrinsic_size = Size::ZERO;
dbg!(self.depth);
let empty = Vec::new();
self.tree.inner.with_data_mut(|data| { self.tree.inner.with_data_mut(|data| {
let overlay_offset = Point::ORIGIN - position; let overlay_offset = Point::ORIGIN - position;
let tree_children = &mut data.tree.children; let tree_children = &mut data.tree.children;
dbg!(&data.active_root,);
let children = data let children = data
.active_root .active_root
.get(&self.window_id) .get(self.depth)
.cloned() .cloned()
.map(|active_root| { .map(|active_root| {
if active_root.is_empty() || self.menu_roots.is_empty() {
return (&empty, vec![]);
}
let (active_tree, roots) = if self.depth > 0 { let (active_tree, roots) = if self.depth > 0 {
active_root.iter().skip(1).take(self.depth).fold( active_root.iter().take(self.depth).fold(
( (
&mut tree_children[active_root[0]].children, &mut tree_children[active_root[0]].children,
&self.menu_roots[active_root[0]].children, &self.menu_roots[active_root[0]].children,
@ -491,7 +498,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children),
) )
} else { } else {
(tree_children, self.menu_roots.as_ref()) (tree_children, &self.menu_roots[active_root[0]].children)
}; };
dbg!(roots.len()); dbg!(roots.len());
@ -512,32 +519,41 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// } // }
// } // }
// } // }
data.menu_states[&self.window_id] if let Some(ms) = data.menu_states.get(self.depth + 1) {
.iter() ms.iter()
.enumerate() .enumerate()
.filter(|ms| self.is_overlay || ms.0 < active_root.len()) .filter(|ms| self.is_overlay || ms.0 < active_root.len())
.fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| { .fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| {
dbg!(ms.index); dbg!(ms.index);
let slice = ms.slice(limits.max(), overlay_offset, self.item_height); let slice =
let _start_index = slice.start_index; ms.slice(limits.max(), overlay_offset, self.item_height);
let _end_index = slice.end_index; let _start_index = slice.start_index;
dbg!(&self.window_id, menu_root.len()); let _end_index = slice.end_index;
let children_node = dbg!(&self.depth, &self.window_id, menu_root.len());
ms.layout(overlay_offset, slice, renderer, menu_root, active_tree); let children_node = ms.layout(
let node_size = children_node.size(); overlay_offset,
// dbg!(node_size.height); slice,
intrinsic_size.height += node_size.height; renderer,
intrinsic_size.width = intrinsic_size.width.max(node_size.width); menu_root,
nodes.push(children_node); active_tree,
// if popup just use len 1? );
// only the last menu can have a None active index let node_size = children_node.size();
// dbg!(ms.index); // dbg!(node_size.height);
( intrinsic_size.height += node_size.height;
ms.index intrinsic_size.width = intrinsic_size.width.max(node_size.width);
.map_or(menu_root, |active| &menu_root[active].children), nodes.push(children_node);
nodes, // if popup just use len 1?
) // only the last menu can have a None active index
}) // dbg!(ms.index);
(
ms.index
.map_or(menu_root, |active| &menu_root[active].children),
nodes,
)
})
} else {
(&empty, vec![])
}
}) })
.map(|(_, l)| l) .map(|(_, l)| l)
.unwrap_or_default(); .unwrap_or_default();
@ -592,6 +608,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
overlay_offset, overlay_offset,
); );
dbg!("init_root_menu");
init_root_menu( init_root_menu(
self, self,
renderer, renderer,
@ -650,7 +667,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
.distance(view_cursor.position().unwrap_or_default()) .distance(view_cursor.position().unwrap_or_default())
< 2.0 < 2.0
{ {
let is_inside = state.menu_states[&self.window_id] let is_inside = state.menu_states[self.depth + 1]
.iter() .iter()
.any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor));
@ -696,7 +713,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
view_cursor: Cursor, view_cursor: Cursor,
) { ) {
self.tree.inner.with_data(|state| { self.tree.inner.with_data(|state| {
let Some(active_root) = state.active_root.get(&self.window_id) else { let Some(active_root) = state.active_root.get(self.depth) else {
// dbg!(self.window_id); // dbg!(self.window_id);
return; return;
}; };
@ -713,7 +730,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
let styling = theme.appearance(&self.style); let styling = theme.appearance(&self.style);
let (active_tree, roots) = if self.depth > 0 { let (active_tree, roots) = if self.depth > 0 {
active_root.iter().skip(1).take(self.depth).fold( active_root.iter().take(self.depth).fold(
( (
&state.tree.children[active_root[0]].children, &state.tree.children[active_root[0]].children,
&self.menu_roots[active_root[0]].children, &self.menu_roots[active_root[0]].children,
@ -726,9 +743,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// let tree = &state.tree.children[active_root].children; // let tree = &state.tree.children[active_root].children;
// let root = &self.menu_roots[active_root]; // let root = &self.menu_roots[active_root];
let indices = state let indices = state.get_trimmed_indices(self.depth).collect::<Vec<_>>();
.get_trimmed_indices(&self.window_id)
.collect::<Vec<_>>();
// dbg!( // dbg!(
// self.window_id, // self.window_id,
// state.menu_states[&self.window_id].len(), // state.menu_states[&self.window_id].len(),
@ -736,7 +751,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
// root.children.len(), // root.children.len(),
// layout.bounds() // layout.bounds()
// ); // );
state.menu_states[&self.window_id] state.menu_states[self.depth + 1]
.iter() .iter()
.zip(layout.children()) .zip(layout.children())
.enumerate() .enumerate()
@ -893,7 +908,7 @@ pub(super) fn init_root_menu<Message: Clone>(
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
if !(state if !(state
.menu_states .menu_states
.get(&menu.window_id) .get(menu.depth + 1)
.is_none_or(|s| s.is_empty()) .is_none_or(|s| s.is_empty())
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
{ {
@ -901,48 +916,53 @@ pub(super) fn init_root_menu<Message: Clone>(
} }
println!( println!(
"init root menu {} {:?}", "init root menu {} {} {:?}",
menu.window_id, menu.root_bounds_list menu.depth, menu.window_id, &menu.root_bounds_list
); );
dbg!(menu.menu_roots.len()); dbg!(menu.menu_roots.len());
// dbg!(state dbg!(
// .menu_states state
// .get(&menu.window_id) .menu_states
// .is_none_or(|s| s.is_empty())); .get(menu.depth + 1)
// dbg!(menu .is_none_or(|s| s.is_empty())
// .menu_roots );
// .as_slice() dbg!(
// .iter() menu.menu_roots
// .map(|r| r.index) .as_slice()
// .collect::<Vec<_>>()); .iter()
.map(|r| r.index)
.collect::<Vec<_>>()
);
// dbg!(state dbg!(
// .active_root state
// .get(&menu.window_id) .active_root
// .into_iter() .get(menu.depth)
// .flatten() .into_iter()
// .count() .flatten()
// .saturating_sub(1)); .count()
// let root = state .saturating_sub(1)
// .active_root );
// .get(&menu.window_id) let root = state
// .iter() .active_root
// .map(|l| l.into_iter()) .get(menu.depth)
// .flatten() .iter()
// .take( .map(|l| l.into_iter())
// state .flatten()
// .active_root .take(
// .get(&menu.window_id) state
// .into_iter() .active_root
// .flatten() .get(menu.depth)
// .count() .into_iter()
// .saturating_sub(1), .flatten()
// ) .count()
// .fold(menu.menu_roots.as_slice(), |m, i| { .saturating_sub(1),
// dbg!(m.len()); )
// m[*i].children.as_slice() .fold(menu.menu_roots.as_slice(), |m, i| {
// }); dbg!(m.len());
// dbg!(root.len()); m[*i].children.as_slice()
});
dbg!(root.len());
let mut set = false; let mut set = false;
for (i, (&root_bounds, mt)) in menu for (i, (&root_bounds, mt)) in menu
@ -955,7 +975,8 @@ pub(super) fn init_root_menu<Message: Clone>(
// dbg!("skipping menu with no children"); // dbg!("skipping menu with no children");
continue; continue;
} }
// dbg!(i, root_bounds.contains(overlay_cursor)); dbg!(mt.children.len());
dbg!(i, root_bounds.contains(overlay_cursor));
if root_bounds.contains(overlay_cursor) { if root_bounds.contains(overlay_cursor) {
dbg!(root_bounds); dbg!(root_bounds);
// dbg!(i, root_bounds, mt.width, mt.height); // dbg!(i, root_bounds, mt.width, mt.height);
@ -992,18 +1013,20 @@ pub(super) fn init_root_menu<Message: Clone>(
menu.is_overlay, menu.is_overlay,
); );
set = true; set = true;
// dbg!(i); dbg!(i);
// dbg!("inserting root menu state");
// dbg!(&menu_bounds.children_bounds); dbg!("inserting root menu state");
// dbg!((&menu_bounds.child_positions)); dbg!(&menu_bounds.children_bounds);
state.active_root.insert(menu.window_id, vec![i]); dbg!((&menu_bounds.child_positions));
state.active_root.insert(menu.depth, vec![i]);
// do we need to insert the rest now too? // do we need to insert the rest now too?
let ms = MenuState { let ms = MenuState {
index: None, index: None,
scroll_offset: 0.0, scroll_offset: 0.0,
menu_bounds, menu_bounds,
}; };
let v = state.menu_states.entry(menu.window_id).or_default(); dbg!("pushing to menu states...");
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth + 1);
v.push(ms); v.push(ms);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
@ -1014,6 +1037,7 @@ pub(super) fn init_root_menu<Message: Clone>(
} }
if !set { if !set {
dbg!(overlay_cursor); dbg!(overlay_cursor);
panic!("huh");
} }
}); });
} }
@ -1033,7 +1057,7 @@ pub(super) fn init_root_popup_menu<Message>(
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
if !(state if !(state
.menu_states .menu_states
.get(&menu.window_id) .get(menu.depth + 1)
.is_none_or(|s| s.is_empty()) .is_none_or(|s| s.is_empty())
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
{ {
@ -1063,7 +1087,7 @@ pub(super) fn init_root_popup_menu<Message>(
// .count()); // .count());
let active_roots = state let active_roots = state
.active_root .active_root
.get(&menu.window_id) .get(menu.depth)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
dbg!(&active_roots); dbg!(&active_roots);
@ -1132,7 +1156,7 @@ pub(super) fn init_root_popup_menu<Message>(
scroll_offset: 0.0, scroll_offset: 0.0,
menu_bounds, menu_bounds,
}; };
let v = state.menu_states.entry(menu.window_id).or_default(); let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth + 1);
v.push(ms); v.push(ms);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
@ -1167,11 +1191,11 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
Cow::Owned(o) => o.as_mut_slice(), Cow::Owned(o) => o.as_mut_slice(),
}; };
my_state.inner.with_data_mut(|state| { my_state.inner.with_data_mut(|state| {
let Some(active_root) = state.active_root.get(&window_id).cloned() else { let Some(active_root) = state.active_root.get(menu.depth).cloned() else {
return Status::Ignored; return Status::Ignored;
}; };
let indices = state.get_trimmed_indices(&window_id); let indices = state.get_trimmed_indices(menu.depth);
let indices = if is_overlay { let indices = if is_overlay {
indices.collect::<Vec<_>>() indices.collect::<Vec<_>>()
@ -1185,7 +1209,7 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
// get active item // get active item
// let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]); // let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]);
let (tree, mt) = active_root.iter().take(menu.depth).skip(1).fold( let (tree, mt) = active_root.iter().take(menu.depth).fold(
( (
&mut state.tree.children[active_root[0]].children, &mut state.tree.children[active_root[0]].children,
&mut menu_roots[active_root[0]], &mut menu_roots[active_root[0]],
@ -1197,7 +1221,7 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
let tree = &mut tree[mt.index]; let tree = &mut tree[mt.index];
// get layout // get layout
let last_ms = &state.menu_states[&window_id][indices.len() - 1]; let last_ms = &state.menu_states[menu.depth + 1][indices.len() - 1];
let child_node = last_ms.layout_single( let child_node = last_ms.layout_single(
overlay_offset, overlay_offset,
last_ms.index.expect("missing index within menu state."), last_ms.index.expect("missing index within menu state."),
@ -1248,7 +1272,7 @@ where
let mut new_menu_root = None; let mut new_menu_root = None;
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
let Some(active_root) = state.active_root.get(&menu.window_id).clone() else { let Some(active_root) = state.active_root.get(menu.depth).clone() else {
if is_overlay && !menu.bar_bounds.contains(overlay_cursor) { if is_overlay && !menu.bar_bounds.contains(overlay_cursor) {
state.reset(); state.reset();
} }
@ -1266,13 +1290,13 @@ where
// * remove invalid menus // * remove invalid menus
let mut prev_bounds = std::iter::once(menu.bar_bounds) let mut prev_bounds = std::iter::once(menu.bar_bounds)
.chain( .chain(
state.menu_states[&menu.window_id] state.menu_states[menu.depth + 1]
[..state.menu_states.len().saturating_sub(1).min(1)] [..state.menu_states.len().saturating_sub(1).min(1)]
.iter() .iter()
.map(|ms| ms.menu_bounds.children_bounds), .map(|ms| ms.menu_bounds.children_bounds),
) )
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let menu_states = state.menu_states.get_mut(&menu.window_id).unwrap(); let menu_states = state.menu_states.get_mut(menu.depth + 1).unwrap();
if menu.close_condition.leave { if menu.close_condition.leave {
for i in (0..menu_states.len()).rev() { for i in (0..menu_states.len()).rev() {
@ -1313,7 +1337,7 @@ where
let Some(last_menu_state) = menu_states.last_mut() else { let Some(last_menu_state) = menu_states.last_mut() else {
// no menus left // no menus left
// TODO do we want to avoid this for popups? // TODO do we want to avoid this for popups?
state.active_root.remove(&menu.window_id); state.active_root.remove(menu.depth);
// keep state.open when the cursor is still inside the menu bar // keep state.open when the cursor is still inside the menu bar
// this allows the overlay to keep drawing when the cursor is // this allows the overlay to keep drawing when the cursor is
@ -1351,7 +1375,7 @@ where
// |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]), // |(tree, mr), next_active_root| (tree, &mr.children[*next_active_root]),
// ); // );
let (active_tree, roots) = if menu.depth > 0 { let (active_tree, roots) = if menu.depth > 0 {
active_root.iter().skip(1).take(menu.depth).fold( active_root.iter().take(menu.depth).fold(
( (
&mut state.tree.children[active_root[0]].children, &mut state.tree.children[active_root[0]].children,
&menu.menu_roots[active_root[0]].children, &menu.menu_roots[active_root[0]].children,
@ -1450,7 +1474,7 @@ where
new_menu_root = Some((new_index, ms.clone())); new_menu_root = Some((new_index, ms.clone()));
} }
if should_add { if should_add {
let v = state.menu_states.entry(menu.window_id).or_default(); let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth);
v.push(ms); v.push(ms);
} }
} }
@ -1487,7 +1511,7 @@ where
(viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0);
(max_offset, min_offset) (max_offset, min_offset)
}; };
let menu_states = state.menu_states.get_mut(&menu.window_id).unwrap(); let menu_states = state.menu_states.get_mut(menu.depth + 1).unwrap();
// update // update
if menu_states.is_empty() { if menu_states.is_empty() {