wip: on hover switch and fix for nesting

This commit is contained in:
Ashley Wulber 2025-06-05 12:10:07 -04:00
parent ff41ff0532
commit 51c391a656
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
3 changed files with 364 additions and 281 deletions

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[features] [features]
default = ["wayland"] default = []
wayland = ["libcosmic/wayland"] wayland = ["libcosmic/wayland"]
[dependencies] [dependencies]
@ -25,4 +25,5 @@ features = [
"wgpu", "wgpu",
"single-instance", "single-instance",
"multi-window", "multi-window",
"surface-message",
] ]

View file

@ -12,7 +12,6 @@ use super::{
use crate::{ use crate::{
Renderer, Renderer,
style::menu_bar::StyleSheet, style::menu_bar::StyleSheet,
surface::action::destroy_popup,
widget::{ widget::{
RcWrapper, RcWrapper,
dropdown::menu::{self, State}, dropdown::menu::{self, State},
@ -64,8 +63,7 @@ impl MenuBarStateInner {
self.menu_states self.menu_states
.get(index) .get(index)
.into_iter() .into_iter()
.map(|v| v.iter()) .flat_map(|v| v.iter())
.flatten()
.take_while(|ms| ms.index.is_some()) .take_while(|ms| ms.index.is_some())
.map(|ms| ms.index.expect("No indices were found in the menu state.")) .map(|ms| ms.index.expect("No indices were found in the menu state."))
} }
@ -356,6 +354,145 @@ where
self.on_surface_action = Some(Arc::new(handler)); self.on_surface_action = Some(Arc::new(handler));
self self
} }
fn create_popup(
&mut self,
layout: Layout<'_>,
view_cursor: Cursor,
renderer: &Renderer,
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::{
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()));
if old_active_root == hovered_root {
return status;
}
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,
on_surface_action: self.on_surface_action.clone(),
};
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_mut(|state| {
state.popup_id.insert(self.window_id, id);
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: false,
input_zone: None,
},
Some(move || {
Element::from(crate::widget::container(popup_menu.clone()).center(Length::Fill))
.map(crate::action::app)
}),
)));
}
status
}
} }
impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message> impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message>
where where
@ -448,7 +585,7 @@ where
.inner .inner
.with_data(|d| !d.open && !d.active_root.is_empty()); .with_data(|d| !d.open && !d.active_root.is_empty());
my_state.inner.with_data_mut(|state| { let open = my_state.inner.with_data_mut(|state| {
if reset { if reset {
if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
dbg!("reset destroy"); dbg!("reset destroy");
@ -459,6 +596,7 @@ where
} }
} }
} }
state.open
}); });
match event { match event {
@ -467,149 +605,48 @@ where
let mut create_popup = false; let mut create_popup = false;
if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
state.view_cursor = view_cursor; state.view_cursor = view_cursor;
dbg!(view_cursor.is_over(layout.bounds()));
state.open = true; state.open = true;
create_popup = 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) {
dbg!("destroy popup...");
state.menu_states.clear(); state.menu_states.clear();
state.active_root.clear(); state.active_root.clear();
let surface_action = self.on_surface_action.as_ref().unwrap(); let surface_action = self.on_surface_action.as_ref().unwrap();
state.open = false; state.open = false;
shell.publish(surface_action(destroy_popup(id))); #[cfg(all(
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
shell.publish(surface_action(crate::surface::action::destroy_popup(id)));
state.view_cursor = view_cursor; state.view_cursor = view_cursor;
} }
create_popup create_popup
}); });
if !create_popup { if !create_popup {
return root_status; return 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::{
SctkPopupSettings, SctkPositioner,
};
let surface_action = self.on_surface_action.as_ref().unwrap(); return root_status.merge(self.create_popup(
let old_active_root = my_state.inner.with_data(|state| { layout,
state view_cursor,
.active_root renderer,
.get(0) shell,
.filter(|r| r.len() == 1) viewport,
.map(|r| r[0]) my_state,
}); ));
}
// if position is not on menu bar button skip. Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
let position = view_cursor.position(); if open && view_cursor.is_over(layout.bounds()) =>
let hovered_root = layout {
.children() return root_status.merge(self.create_popup(
.position(|lo| view_cursor.is_over(lo.bounds())); layout,
view_cursor,
let (id, root_list) = my_state.inner.with_data_mut(|state| { renderer,
if let Some(id) = state.popup_id.get(&self.window_id).copied() { shell,
// close existing popups viewport,
state.menu_states.clear(); my_state,
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,
on_surface_action: self.on_surface_action.clone(),
};
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_mut(|state| {
state.popup_id.insert(self.window_id, id);
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: false,
input_zone: None,
},
Some(move || {
Element::from(
crate::widget::container(popup_menu.clone()).center(Length::Fill),
)
.map(crate::action::app)
}),
)));
}
} }
// Window(Focused) => { // Window(Focused) => {
// my_state.inner.with_data_mut(|state| { // my_state.inner.with_data_mut(|state| {

View file

@ -503,7 +503,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
if let Some(ms) = data.menu_states.get(self.depth) { if let Some(ms) = data.menu_states.get(self.depth) {
ms.iter() ms.iter()
.enumerate() .enumerate()
.filter(|ms| self.is_overlay || ms.0 < active_root.len()) .filter(|ms| self.is_overlay || ms.0 < 1)
.fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| { .fold((roots, Vec::new()), |(menu_root, mut nodes), (_i, ms)| {
let slice = let slice =
ms.slice(limits.max(), overlay_offset, self.item_height); ms.slice(limits.max(), overlay_offset, self.item_height);
@ -564,7 +564,11 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
}; };
use touch::Event::{FingerLifted, FingerMoved, FingerPressed}; use touch::Event::{FingerLifted, FingerMoved, FingerPressed};
if !self.tree.inner.with_data(|data| data.open) { if !self
.tree
.inner
.with_data(|data| data.open || data.active_root.len() <= self.depth)
{
return (None, Ignored); return (None, Ignored);
}; };
@ -612,11 +616,9 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} }
Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => { Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => {
dbg!("moved", self.window_id);
let view_cursor = Cursor::Available(position); let view_cursor = Cursor::Available(position);
let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; 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)) {
dbg!("exit early", view_cursor, viewport);
return (None, menu_status); return (None, menu_status);
} }
// dbg!(view_cursor, viewport, self.window_id); // dbg!(view_cursor, viewport, self.window_id);
@ -665,9 +667,21 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
if needs_reset { if needs_reset {
dbg!("reset"); dbg!("reset");
if let Some(handler) = self.on_surface_action.as_ref() { if let Some(handler) = self.on_surface_action.as_ref() {
shell.publish((handler)(crate::surface::Action::DestroyPopup( let mut root = self.window_id;
self.window_id, let mut depth = self.depth;
))); while let Some(parent) =
state.popup_id.iter().find(|(_, v)| **v == root)
{
// parent of root popup is the window, so we stop.
if depth == 0 {
break;
}
root = parent.0.clone();
depth = depth.saturating_sub(1);
}
dbg!(root);
shell
.publish((handler)(crate::surface::Action::DestroyPopup(root)));
} }
state.reset(); state.reset();
@ -700,13 +714,12 @@ 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| {
if !state.open { if !state.open || state.active_root.len() <= self.depth {
return; return;
} }
let Some(active_root) = state.active_root.get(self.depth) else { let Some(active_root) = state.active_root.get(self.depth) else {
return; return;
}; };
dbg!(self.depth, &active_root);
let viewport = layout.bounds(); let viewport = layout.bounds();
let viewport_size = viewport.size(); let viewport_size = viewport.size();
let overlay_offset = Point::ORIGIN - viewport.position(); let overlay_offset = Point::ORIGIN - viewport.position();
@ -728,104 +741,103 @@ 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),
); );
let indices = state.get_trimmed_indices(self.depth).collect::<Vec<_>>(); let indices = state.get_trimmed_indices(self.depth).collect::<Vec<_>>();
state.menu_states[self.depth] state.menu_states[self.depth]
.iter() .iter()
.zip(layout.children()) .zip(layout.children())
.enumerate() .enumerate()
.filter(|ms: &(usize, (&MenuState, Layout<'_>))| { .filter(|ms: &(usize, (&MenuState, Layout<'_>))| self.is_overlay || ms.0 < 1)
self.is_overlay || ms.0 < active_root.len() .fold(
}) roots,
.fold(roots, |menu_roots, (i, (ms, children_layout))| { |menu_roots: &Vec<MenuTree<Message>>, (i, (ms, children_layout))| {
let draw_path = self.path_highlight.as_ref().map_or(false, |ph| match ph { let draw_path = self.path_highlight.as_ref().map_or(false, |ph| match ph {
PathHighlight::Full => true, PathHighlight::Full => true,
PathHighlight::OmitActive => !indices.is_empty() && i < indices.len() - 1, PathHighlight::OmitActive => {
PathHighlight::MenuActive => self.depth == state.active_root.len() - 1, !indices.is_empty() && i < indices.len() - 1
});
// react only to the last menu
if self.depth == state.active_root.len() - 1 {
view_cursor
} else {
Cursor::Available([-1.0; 2].into())
};
dbg!(view_cursor);
let draw_menu = |r: &mut crate::Renderer| {
// calc slice
let slice = ms.slice(viewport_size, overlay_offset, self.item_height);
let start_index = slice.start_index;
let end_index = slice.end_index;
let children_bounds = children_layout.bounds();
// draw menu background
// let bounds = pad_rectangle(children_bounds, styling.background_expand.into());
// println!("cursor: {:?}", view_cursor);
// println!("bg_bounds: {:?}", bounds);
// println!("color: {:?}\n", styling.background);
let menu_quad = renderer::Quad {
bounds: pad_rectangle(
children_bounds,
styling.background_expand.into(),
),
border: Border {
radius: styling.menu_border_radius.into(),
width: styling.border_width,
color: styling.border_color,
},
shadow: Shadow::default(),
};
let menu_color = styling.background;
r.fill_quad(menu_quad, menu_color);
dbg!(ms.index, children_layout.children().count(), start_index);
// draw path hightlight
if let (true, Some(active)) = (draw_path, ms.index) {
if let Some(active_layout) = children_layout
.children()
.nth(active.saturating_sub(start_index))
{
let path_quad = renderer::Quad {
bounds: active_layout.bounds(),
border: Border {
radius: styling.menu_border_radius.into(),
..Default::default()
},
shadow: Shadow::default(),
};
r.fill_quad(path_quad, styling.path);
} }
} PathHighlight::MenuActive => self.depth == state.active_root.len() - 1,
if start_index < menu_roots.len() { });
// dbg!(start_index, end_index, menu_roots.len());
// draw item
menu_roots[start_index..=end_index]
.iter()
.zip(children_layout.children())
.for_each(|(mt, clo)| {
dbg!(self.depth, view_cursor, clo.bounds());
mt.item.draw( // react only to the last menu
&active_tree[mt.index], if self.depth == state.active_root.len() - 1 {
r, view_cursor
theme, } else {
style, Cursor::Available([-1.0; 2].into())
clo, };
view_cursor,
&children_layout.bounds(),
);
});
}
};
renderer.with_layer(render_bounds, draw_menu); let draw_menu = |r: &mut crate::Renderer| {
// calc slice
let slice = ms.slice(viewport_size, overlay_offset, self.item_height);
let start_index = slice.start_index;
let end_index = slice.end_index;
// only the last menu can have a None active index let children_bounds = children_layout.bounds();
ms.index
.map_or(menu_roots, |active| &menu_roots[active].children) // draw menu background
}); // let bounds = pad_rectangle(children_bounds, styling.background_expand.into());
// println!("cursor: {:?}", view_cursor);
// println!("bg_bounds: {:?}", bounds);
// println!("color: {:?}\n", styling.background);
let menu_quad = renderer::Quad {
bounds: pad_rectangle(
children_bounds,
styling.background_expand.into(),
),
border: Border {
radius: styling.menu_border_radius.into(),
width: styling.border_width,
color: styling.border_color,
},
shadow: Shadow::default(),
};
let menu_color = styling.background;
r.fill_quad(menu_quad, menu_color);
// draw path hightlight
if let (true, Some(active)) = (draw_path, ms.index) {
if let Some(active_layout) = children_layout
.children()
.nth(active.saturating_sub(start_index))
{
let path_quad = renderer::Quad {
bounds: active_layout.bounds(),
border: Border {
radius: styling.menu_border_radius.into(),
..Default::default()
},
shadow: Shadow::default(),
};
r.fill_quad(path_quad, styling.path);
}
}
if start_index < menu_roots.len() {
// dbg!(start_index, end_index, menu_roots.len());
// draw item
menu_roots[start_index..=end_index]
.iter()
.zip(children_layout.children())
.for_each(|(mt, clo)| {
mt.item.draw(
&active_tree[mt.index],
r,
theme,
style,
clo,
view_cursor,
&children_layout.bounds(),
);
});
}
};
renderer.with_layer(render_bounds, draw_menu);
// only the last menu can have a None active index
ms.index
.map_or(menu_roots, |active| &menu_roots[active].children)
},
);
}) })
} }
} }
@ -914,13 +926,8 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
let prev_hover = self.is_overlay.then(|| {
self.tree.inner.with_data(|d| {
let menu_states = d.menu_states.get(self.depth).unwrap();
menu_states.get(0).and_then(|ms| ms.index)
})
});
let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell);
// dbg!(new_root.as_ref().map(|r| r.0)); // dbg!(new_root.as_ref().map(|r| r.0));
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
if let Some((new_root, new_ms)) = new_root { if let Some((new_root, new_ms)) = new_root {
@ -928,7 +935,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
SctkPopupSettings, SctkPositioner, SctkPopupSettings, SctkPositioner,
}; };
let overlay_offset = Point::ORIGIN - viewport.position(); let overlay_offset = Point::ORIGIN - viewport.position();
dbg!(overlay_offset);
let overlay_cursor = cursor.position().unwrap_or_default() - overlay_offset; let overlay_cursor = cursor.position().unwrap_or_default() - overlay_offset;
@ -941,7 +947,8 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
.active_root .active_root
.get(self.depth) .get(self.depth)
.cloned() .cloned()
.unwrap_or_default(); // dbg!(active_roots); .unwrap_or_default();
dbg!(self.window_id, popup_id, active_roots);
// let root_bounds_list = active_roots // let root_bounds_list = active_roots
// .into_iter() // .into_iter()
// .fold(layout, |l, active_root| { // .fold(layout, |l, active_root| {
@ -989,6 +996,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
depth: self.depth + 1, depth: self.depth + 1,
on_surface_action: self.on_surface_action.clone(), on_surface_action: self.on_surface_action.clone(),
}; };
// let mut state = self.tree.inner.lock().unwrap(); // let mut state = self.tree.inner.lock().unwrap();
// dbg!(state.menu_states.keys()); // dbg!(state.menu_states.keys());
// let Some(parent_root) = state.active_root.get(&self.window_id) else { // let Some(parent_root) = state.active_root.get(&self.window_id) else {
@ -999,12 +1007,15 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
dbg!("NO ROOT?"); dbg!("NO ROOT?");
return None; return None;
}; };
let mut roots = parent_root.clone(); let mut roots = parent_root.clone();
dbg!(&roots);
roots.push(new_root); roots.push(new_root);
// dbg!(&roots); dbg!(&roots);
state.active_root.push(roots); state.active_root.push(roots);
// _ = state.menu_states.remove(&popup_id); dbg!(&state.active_root);
// drop(state);
Some((popup_menu, popup_id)) Some((popup_menu, popup_id))
}) else { }) else {
return status; return status;
@ -1022,7 +1033,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
); );
let anchor_rect = self.tree.inner.with_data_mut(|state| { let anchor_rect = self.tree.inner.with_data_mut(|state| {
// let mut state = self.tree.inner.lock().unwrap(); // let mut state = self.tree.inner.lock().unwrap();
state.menu_states.get_mut(self.depth).unwrap().push(new_ms); // state.menu_states.get_mut(self.depth).unwrap().push(new_ms);
state.menu_states[self.depth] state.menu_states[self.depth]
.iter() .iter()
@ -1047,18 +1058,12 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
) )
}); });
// dbg!(&anchor_rect);
// drop(state);
let menu_node = Widget::layout( let menu_node = Widget::layout(
&menu, &menu,
&mut Tree::empty(), &mut Tree::empty(),
renderer, renderer,
&Limits::NONE.min_width(1.).min_height(1.), &Limits::NONE.min_width(1.).min_height(1.),
); );
// dbg!(menu_node.size());
// dbg!(&menu_node);
let popup_size = menu_node.size(); let popup_size = menu_node.size();
let positioner = SctkPositioner { let positioner = SctkPositioner {
@ -1070,8 +1075,19 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
..Default::default() ..Default::default()
}; };
let parent = self.window_id; let parent = self.window_id;
dbg!(&positioner); dbg!(
self.depth + 1,
&positioner,
parent,
popup_id,
&menu_node,
anchor_rect
);
let ar_len = self
.tree
.inner
.with_data_mut(|state| state.active_root.len());
// if self.depth < 1 {
shell.publish((self.on_surface_action.as_ref().unwrap())( shell.publish((self.on_surface_action.as_ref().unwrap())(
crate::surface::action::simple_popup( crate::surface::action::simple_popup(
move || SctkPopupSettings { move || SctkPopupSettings {
@ -1091,6 +1107,17 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
}), }),
), ),
)); ));
// } else {
// dbg!(SctkPopupSettings {
// parent,
// id: popup_id,
// positioner: positioner.clone(),
// parent_size: None,
// grab: true,
// close_with_children: false,
// input_zone: None,
// });
// }
return status; return status;
} }
@ -1133,6 +1160,8 @@ pub(super) fn init_root_menu<Message: Clone>(
.get(menu.depth) .get(menu.depth)
.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)))
|| menu.depth > 0
|| !state.open
{ {
// dbg!("exiting from root menu init early...", menu.depth); // dbg!("exiting from root menu init early...", menu.depth);
return; return;
@ -1436,13 +1465,26 @@ 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) = indices.iter().take(indices.len()).fold( // let (tree, mt) = indices.iter().take(indices.len()).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]],
), // ),
|(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), // |(tree, mt), next_active_root| {
); // dbg!(mt.children.len(), next_active_root);
// (tree, &mut mt.children[*next_active_root])
// },
// );
let (tree, mt) = active_root
.iter()
.skip(if menu.is_overlay { 0 } else { 1 })
.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]),
);
// let Some(i) = state.menu_states[menu.depth].iter().position(|ms| { // let Some(i) = state.menu_states[menu.depth].iter().position(|ms| {
// ms.menu_bounds // ms.menu_bounds
// .check_bounds // .check_bounds
@ -1509,7 +1551,6 @@ where
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
let Some(active_root) = state.active_root.get(menu.depth).clone() else { let Some(active_root) = state.active_root.get(menu.depth).clone() else {
panic!();
if is_overlay && !menu.bar_bounds.contains(overlay_cursor) { if is_overlay && !menu.bar_bounds.contains(overlay_cursor) {
state.reset(); state.reset();
} }
@ -1525,11 +1566,18 @@ where
state.view_cursor = view_cursor; state.view_cursor = view_cursor;
// * remove invalid menus // * remove invalid menus
dbg!(
&state
.menu_states
.iter()
.map(|s| s.iter().map(|s| s.index).collect::<Vec<_>>())
.collect::<Vec<_>>()
);
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.depth][..state.menu_states.len().saturating_sub(1).min(1)] (state.menu_states[..menu.depth])
.iter() .iter()
.map(|ms| ms.menu_bounds.children_bounds), .map(|s| s[0].menu_bounds.children_bounds),
) )
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let menu_states = state.menu_states.get_mut(menu.depth).unwrap(); let menu_states = state.menu_states.get_mut(menu.depth).unwrap();
@ -1569,7 +1617,6 @@ where
let should_add = is_overlay || menu_states.len() < 2; let should_add = is_overlay || menu_states.len() < 2;
dbg!(&indices); dbg!(&indices);
let menu_state_len = menu_states.len();
// dbg!(menu_states.iter().map(|m| m.index).collect::<Vec<_>>()); // dbg!(menu_states.iter().map(|m| m.index).collect::<Vec<_>>());
// * update active item // * update active item
let Some(last_menu_state) = menu_states.get_mut(0) else { let Some(last_menu_state) = menu_states.get_mut(0) else {
@ -1621,6 +1668,9 @@ where
), ),
|(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children),
); );
if is_overlay {
panic!();
}
let active_menu = if is_overlay { let active_menu = if is_overlay {
indices[0..indices.len().saturating_sub(1)].iter().fold( indices[0..indices.len().saturating_sub(1)].iter().fold(
roots, roots,
@ -1648,27 +1698,19 @@ where
) )
} }
}; };
// if last_menu_state let remove = last_menu_state
// .index
// .as_ref()
// .is_some_and(|old_index| *old_index == new_index)
// {
// dbg!("skipping duplicate", last_menu_state.index);
// return (None, Captured);
// }
if last_menu_state
.index .index
.as_ref() .as_ref()
.is_some_and(|i| *i != new_index) .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty());
{
shell.publish((menu.on_surface_action.as_ref().unwrap())( #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
crate::surface::action::destroy_popup( if remove {
*state if let Some(id) = state.popup_id.remove(&menu.window_id) {
.popup_id state.active_root.pop();
.entry(menu.window_id) shell.publish((menu.on_surface_action.as_ref().unwrap())({
.or_insert_with(window::Id::unique), crate::surface::action::destroy_popup(id)
), }))
)); };
} }
let item = &active_menu[new_index]; let item = &active_menu[new_index];
@ -1737,6 +1779,9 @@ where
v.push(ms); v.push(ms);
} }
} }
if remove {
state.menu_states.pop();
}
(new_menu_root, Captured) (new_menu_root, Captured)
}) })