popup refactor

This commit is contained in:
Ashley Wulber 2025-06-09 10:38:33 -04:00
parent 51c391a656
commit 3fd6c4f6bc
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
6 changed files with 180 additions and 485 deletions

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[features] [features]
default = [] default = ["wayland"]
wayland = ["libcosmic/wayland"] wayland = ["libcosmic/wayland"]
[dependencies] [dependencies]

View file

@ -257,16 +257,16 @@ impl cosmic::Application for App {
menu::Item::Button("hi 21", None, Action::Hi), menu::Item::Button("hi 21", None, Action::Hi),
menu::Item::Button("hi 22", None, Action::Hi2), menu::Item::Button("hi 22", None, Action::Hi2),
menu::Item::Folder( menu::Item::Folder(
"nest 3 2".into(), "nest 3 2 >".into(),
vec![ vec![
menu::Item::Button("31", None, Action::Hi), menu::Item::Button("21", None, Action::Hi),
menu::Item::Button("342", None, Action::Hi2), menu::Item::Button("242", None, Action::Hi2),
menu::Item::Button("3443", None, Action::Hi3), menu::Item::Button("2443", None, Action::Hi3),
menu::Item::Folder( menu::Item::Folder(
"nest 4 2".into(), "nest 4 2 >".into(),
vec![ vec![
menu::Item::Button("343", None, Action::Hi2), menu::Item::Button("243", None, Action::Hi2),
menu::Item::Button("3444", None, Action::Hi), menu::Item::Button("2444", None, Action::Hi),
], ],
), ),
], ],
@ -276,9 +276,12 @@ impl cosmic::Application for App {
( (
"hi 3".into(), "hi 3".into(),
vec![ vec![
menu::Item::Button("hi 33", None, Action::Hi), menu::Item::Button("hi 31", None, Action::Hi),
menu::Item::Button("hi 333", None, Action::Hi2), menu::Item::Button("hi 332", None, Action::Hi2),
menu::Item::Button("hi 3333", None, Action::Hi3), 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 44", None, Action::Hi2),
menu::Item::Button("hi 444", None, Action::Hi3), menu::Item::Button("hi 444", None, Action::Hi3),
menu::Item::Folder( menu::Item::Folder(
"nest 4".into(), "nest 4 >".into(),
vec![ vec![
menu::Item::Button("hi 41", None, Action::Hi), menu::Item::Button("hi 41", None, Action::Hi),
menu::Item::Button("hi 442", None, Action::Hi2), menu::Item::Button("hi 442", None, Action::Hi2),
menu::Item::Button("hi 4443", None, Action::Hi3),
menu::Item::Folder( menu::Item::Folder(
"nest 2 4".into(), "nest 3 4 >".into(),
vec![ vec![
menu::Item::Button("hi 443", None, Action::Hi2), menu::Item::Button("hi 443", None, Action::Hi2),
menu::Item::Button("hi 4444", None, Action::Hi), menu::Item::Button("hi 4444", None, Action::Hi),

View file

@ -51,25 +51,23 @@ 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: Vec<Vec<usize>>, pub(crate) active_root: Vec<usize>,
pub(crate) horizontal_direction: Direction, pub(crate) horizontal_direction: Direction,
pub(crate) vertical_direction: Direction, pub(crate) vertical_direction: Direction,
/// List of all menu states /// List of all menu states
pub(crate) menu_states: Vec<Vec<MenuState>>, pub(crate) menu_states: Vec<MenuState>,
} }
impl MenuBarStateInner { impl MenuBarStateInner {
/// get the list of indices hovered for the menu /// get the list of indices hovered for the menu
pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ { pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ {
self.menu_states self.menu_states
.get(index) .iter()
.into_iter() .skip(index)
.flat_map(|v| v.iter())
.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."))
} }
pub(super) fn reset(&mut self) { pub(super) fn reset(&mut self) {
dbg!("reset");
self.open = false; self.open = false;
self.active_root = Vec::new(); self.active_root = Vec::new();
self.menu_states.clear(); self.menu_states.clear();
@ -205,13 +203,7 @@ where
pub fn new(menu_roots: Vec<MenuTree<Message>>) -> Self { pub fn new(menu_roots: Vec<MenuTree<Message>>) -> Self {
let mut menu_roots = menu_roots; let mut menu_roots = menu_roots;
menu_roots.iter_mut().for_each(MenuTree::set_index); 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 { Self {
width: Length::Shrink, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
@ -326,7 +318,6 @@ where
} }
#[cfg(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "wayland", feature = "winit"))]
pub fn with_positioner( pub fn with_positioner(
mut self, mut self,
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
@ -335,11 +326,13 @@ where
self self
} }
#[must_use]
pub fn window_id(mut self, id: window::Id) -> Self { pub fn window_id(mut self, id: window::Id) -> Self {
self.window_id = id; self.window_id = id;
self self
} }
#[must_use]
pub fn window_id_maybe(mut self, id: Option<window::Id>) -> Self { pub fn window_id_maybe(mut self, id: Option<window::Id>) -> Self {
if let Some(id) = id { if let Some(id) = id {
self.window_id = id; self.window_id = id;
@ -347,6 +340,7 @@ where
self self
} }
#[must_use]
pub fn on_surface_action( pub fn on_surface_action(
mut self, mut self,
handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static, handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static,
@ -355,6 +349,8 @@ where
self self
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
#[allow(clippy::too_many_lines)]
fn create_popup( fn create_popup(
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
@ -363,11 +359,7 @@ where
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
my_state: &mut MenuBarState, 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() { if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
use crate::surface::action::destroy_popup; use crate::surface::action::destroy_popup;
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
@ -375,22 +367,20 @@ where
}; };
let surface_action = self.on_surface_action.as_ref().unwrap(); let surface_action = self.on_surface_action.as_ref().unwrap();
let old_active_root = my_state.inner.with_data(|state| { let old_active_root = my_state
state .inner
.active_root .with_data(|state| state.active_root.get(0).copied());
.get(0)
.filter(|r| r.len() == 1)
.map(|r| r[0])
});
// if position is not on menu bar button skip. // if position is not on menu bar button skip.
let position = view_cursor.position();
let hovered_root = layout let hovered_root = layout
.children() .children()
.position(|lo| view_cursor.is_over(lo.bounds())); .position(|lo| view_cursor.is_over(lo.bounds()));
if old_active_root == hovered_root { if old_active_root
return status; .zip(hovered_root)
.is_some_and(|r| r.0 == r.1)
{
return;
} }
let (id, root_list) = my_state.inner.with_data_mut(|state| { let (id, root_list) = my_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.get(&self.window_id).copied() { 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| { let anchor_rect = my_state.inner.with_data_mut(|state| {
state.popup_id.insert(self.window_id, id); state.popup_id.insert(self.window_id, id);
state.menu_states[0] state
.menu_states
.iter() .iter()
.find(|s| s.index.is_none()) .find(|s| s.index.is_none())
.map(|s| s.menu_bounds.parent_bounds) .map(|s| s.menu_bounds.parent_bounds)
@ -491,7 +482,6 @@ where
}), }),
))); )));
} }
status
} }
} }
impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message> impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message>
@ -588,8 +578,6 @@ where
let open = 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");
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(popup_id))); shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
state.reset(); state.reset();
@ -607,17 +595,22 @@ where
state.view_cursor = view_cursor; state.view_cursor = view_cursor;
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) {
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();
state.open = false; state.open = false;
#[cfg(all( #[cfg(all(
feature = "wayland", feature = "wayland",
feature = "winit", feature = "winit",
feature = "surface-message" 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; state.view_cursor = view_cursor;
} }
create_popup create_popup
@ -626,38 +619,15 @@ where
if !create_popup { if !create_popup {
return event::Status::Ignored; return event::Status::Ignored;
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
return root_status.merge(self.create_popup( self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
layout,
view_cursor,
renderer,
shell,
viewport,
my_state,
));
} }
Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered) Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
if open && view_cursor.is_over(layout.bounds()) => if open && view_cursor.is_over(layout.bounds()) =>
{ {
return root_status.merge(self.create_popup( #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
layout, self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
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 // 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) = state.active_root.get(0) {
.active_root
.get(0)
.and_then(|active_root| active_root.get(0))
{
let active_bounds = layout let active_bounds = layout
.children() .children()
.nth(*active) .nth(*active)
@ -734,16 +700,13 @@ 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(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
return None; return None;
let state = tree.state.downcast_ref::<MenuBarState>(); let state = tree.state.downcast_ref::<MenuBarState>();
if state if state.inner.with_data(|state| !state.open) {
.inner
.with_data(|state| !state.open || state.active_root.is_empty())
{
return None; return None;
}; }
Some( Some(
Menu { Menu {

View file

@ -230,7 +230,7 @@ pub(super) struct MenuSlice {
pub(super) upper_bound_rel: f32, pub(super) upper_bound_rel: f32,
} }
#[derive(Clone)] #[derive(Debug, Clone)]
/// Menu bounds in overlay space /// Menu bounds in overlay space
pub struct MenuBounds { pub struct MenuBounds {
child_positions: Vec<f32>, child_positions: Vec<f32>,
@ -322,9 +322,6 @@ impl MenuState {
// self.menu_bounds.child_positions.len() // self.menu_bounds.child_positions.len()
// ); // );
// for mt in &menu_tree.children[start_index..=end_index] {
// dbg!(mt.index);
// }
// 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;
@ -336,7 +333,6 @@ impl MenuState {
.map(|(i, ((cp, size), mt))| { .map(|(i, ((cp, size), mt))| {
let mut position = *cp; let mut position = *cp;
let mut size = *size; let mut size = *size;
// dbg!(position, size);
if position < lower_bound_rel && (position + size.height) > lower_bound_rel { if position < lower_bound_rel && (position + size.height) > lower_bound_rel {
size.height = 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; size.height = upper_bound_rel - position;
} }
// dbg!(size);
let limits = Limits::new(size, size); let limits = Limits::new(size, size);
// dbg!(i, mt.index, tree.len());
// for child in &mt.children {
// dbg!(child.index);
// }
mt.item mt.item
.layout(&mut tree[mt.index], renderer, &limits) .layout(&mut tree[mt.index], renderer, &limits)
@ -359,11 +351,8 @@ impl MenuState {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// dbg!(children_bounds.size()); Node::with_children(children_bounds.size(), child_nodes)
let node = Node::with_children(children_bounds.size(), child_nodes) .move_to(children_bounds.position())
.move_to(children_bounds.position());
// dbg!(&node);
node
} }
fn layout_single<Message>( fn layout_single<Message>(
@ -479,29 +468,30 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
let empty = Vec::new(); let empty = Vec::new();
self.tree.inner.with_data_mut(|data| { 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 overlay_offset = Point::ORIGIN - position;
let tree_children = &mut data.tree.children; let tree_children = &mut data.tree.children;
let children = data let children = (if self.is_overlay {0} else {self.depth}..=self.depth)
.active_root
.get(self.depth)
.cloned()
.map(|active_root| { .map(|active_root| {
if active_root.is_empty() || self.menu_roots.is_empty() { if self.menu_roots.is_empty() {
return (&empty, vec![]); return (&empty, vec![]);
} }
let (active_tree, roots) = active_root let (active_tree, roots) = data.active_root[..=active_root]
.iter() .iter()
.skip(if self.is_overlay { 0 } else { 1 }) .skip(1)
.fold( .fold(
( (
&mut tree_children[active_root[0]].children, &mut tree_children[data.active_root[0]].children,
&self.menu_roots[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) { data.menu_states[if self.is_overlay {0} else {self.depth}..=self.depth].iter()
ms.iter()
.enumerate() .enumerate()
.filter(|ms| self.is_overlay || ms.0 < 1) .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)| {
@ -528,23 +518,19 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
nodes, nodes,
) )
}) })
} else {
(&empty, vec![]) }).map(|(_, l)| l).next().unwrap_or_default();
}
})
.map(|(_, l)| l)
.unwrap_or_default();
// dbg!(intrinsic_size);
// overlay space viewport rectangle // overlay space viewport rectangle
let node = Node::with_children( Node::with_children(
limits.resolve(Length::Shrink, Length::Shrink, intrinsic_size), limits.resolve(Length::Shrink, Length::Shrink, intrinsic_size),
children, children,
) )
.translate(Point::ORIGIN - position); .translate(Point::ORIGIN - position)
node
}) })
} }
#[allow(clippy::too_many_lines)]
fn on_event( fn on_event(
&mut self, &mut self,
event: event::Event, event: event::Event,
@ -618,10 +604,9 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => { Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => {
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) {
return (None, menu_status); return (None, menu_status);
} }
// dbg!(view_cursor, viewport, self.window_id);
let (new_root, status) = process_overlay_events( let (new_root, status) = process_overlay_events(
self, self,
@ -631,10 +616,8 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
view_cursor, view_cursor,
overlay_cursor, overlay_cursor,
self.cross_offset as f32, self.cross_offset as f32,
self.is_overlay,
shell, shell,
); );
dbg!(new_root.as_ref().map(|new_root| new_root.0));
return (new_root, status.merge(menu_status)); 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()) .distance(view_cursor.position().unwrap_or_default())
< 2.0 < 2.0
{ {
let is_inside = state.menu_states[self.depth] let is_inside = state.menu_states[..=self.depth]
.iter() .iter()
.any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor));
let mut needs_reset = false; 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; needs_reset |= self.close_condition.click_outside && !is_inside;
if needs_reset { if needs_reset {
dbg!("reset");
if let Some(handler) = self.on_surface_action.as_ref() { if let Some(handler) = self.on_surface_action.as_ref() {
let mut root = self.window_id; let mut root = self.window_id;
let mut depth = self.depth; let mut depth = self.depth;
@ -679,7 +661,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
root = parent.0.clone(); root = parent.0.clone();
depth = depth.saturating_sub(1); depth = depth.saturating_sub(1);
} }
dbg!(root);
shell shell
.publish((handler)(crate::surface::Action::DestroyPopup(root))); .publish((handler)(crate::surface::Action::DestroyPopup(root)));
} }
@ -704,7 +685,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
(None, ret) (None, ret)
} }
#[allow(unused_results)] #[allow(unused_results, clippy::too_many_lines)]
fn draw( fn draw(
&self, &self,
renderer: &mut crate::Renderer, 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 { if !state.open || state.active_root.len() <= self.depth {
return; return;
} }
let Some(active_root) = state.active_root.get(self.depth) else { let active_root = &state.active_root[..=self.depth];
return;
};
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();
@ -733,7 +712,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) = active_root let (active_tree, roots) = active_root
.iter() .iter()
.skip(if self.is_overlay { 0 } else { 1 }) .skip(1)
.fold( .fold(
( (
&state.tree.children[active_root[0]].children, &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), |(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[if self.is_overlay {0} else {self.depth}..=self.depth]
state.menu_states[self.depth]
.iter() .iter()
.zip(layout.children()) .zip(layout.children())
.enumerate() .enumerate()
@ -812,7 +790,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} }
} }
if start_index < menu_roots.len() { if start_index < menu_roots.len() {
// dbg!(start_index, end_index, menu_roots.len());
// draw item // draw item
menu_roots[start_index..=end_index] menu_roots[start_index..=end_index]
.iter() .iter()
@ -838,7 +815,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
.map_or(menu_roots, |active| &menu_roots[active].children) .map_or(menu_roots, |active| &menu_roots[active].children)
}, },
); );
}) });
} }
} }
impl<Message: Clone + 'static> overlay::Overlay<Message, crate::Theme, crate::Renderer> impl<Message: Clone + 'static> overlay::Overlay<Message, crate::Theme, crate::Renderer>
@ -897,7 +874,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &iced_core::layout::Limits, limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node { ) -> iced_core::layout::Node {
// dbg!("layout", self.window_id, limits);
Menu::layout(self, renderer, *limits) Menu::layout(self, renderer, *limits)
} }
@ -928,7 +904,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
) -> event::Status { ) -> event::Status {
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));
#[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 {
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
@ -948,11 +923,9 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
.get(self.depth) .get(self.depth)
.cloned() .cloned()
.unwrap_or_default(); .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| {
// dbg!(active_root, l.children().count());
// l.children().nth(active_root).unwrap() // l.children().nth(active_root).unwrap()
// }) // })
// .children() // .children()
@ -968,14 +941,6 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
.collect(); .collect();
// drop(state); // drop(state);
// dbg!(&root_bounds_list);
// dbg!(self.menu_roots.clone().len());
// dbg!(self
// .menu_roots
// .clone()
// .iter()
// .map(|r| r.index)
// .collect::<Vec<_>>());
let mut popup_menu = Menu { let mut popup_menu = Menu {
tree: self.tree.clone(), tree: self.tree.clone(),
menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())), menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())),
@ -997,24 +962,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
on_surface_action: self.on_surface_action.clone(), on_surface_action: self.on_surface_action.clone(),
}; };
// let mut state = self.tree.inner.lock().unwrap(); state.active_root.push(new_root);
// dbg!(state.menu_states.keys());
// let Some(parent_root) = state.active_root.get(&self.window_id) else {
// // TODO log warning
// return status;
// };
let Some(parent_root) = state.active_root.get(self.depth).cloned() else {
dbg!("NO ROOT?");
return None;
};
let mut roots = parent_root.clone();
dbg!(&roots);
roots.push(new_root);
dbg!(&roots);
state.active_root.push(roots);
dbg!(&state.active_root);
Some((popup_menu, popup_id)) Some((popup_menu, popup_id))
}) else { }) else {
@ -1032,12 +980,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, cra
self.main_offset as f32, self.main_offset as f32,
); );
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(); state.menu_states.get(self.depth + 1)
// state.menu_states.get_mut(self.depth).unwrap().push(new_ms);
state.menu_states[self.depth]
.iter()
.find(|s| s.index.is_none())
.map(|s| s.menu_bounds.parent_bounds) .map(|s| s.menu_bounds.parent_bounds)
.map_or_else( .map_or_else(
|| { || {
@ -1075,19 +1018,6 @@ 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!(
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 {
@ -1107,17 +1037,6 @@ 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;
} }
@ -1158,67 +1077,14 @@ pub(super) fn init_root_menu<Message: Clone>(
if !(state if !(state
.menu_states .menu_states
.get(menu.depth) .get(menu.depth)
.is_none_or(|s| s.is_empty()) .is_none()
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
|| menu.depth > 0 || menu.depth > 0
|| !state.open || !state.open
{ {
// dbg!("exiting from root menu init early...", menu.depth);
return; 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::<Vec<_>>()
);
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; let mut set = false;
for (i, (&root_bounds, mt)) in menu for (i, (&root_bounds, mt)) in menu
.root_bounds_list .root_bounds_list
@ -1227,14 +1093,10 @@ pub(super) fn init_root_menu<Message: Clone>(
.enumerate() .enumerate()
{ {
if mt.children.is_empty() { if mt.children.is_empty() {
// dbg!("skipping menu with no children");
continue; continue;
} }
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!(i, root_bounds, mt.width, mt.height);
let view_center = viewport_size.width * 0.5; let view_center = viewport_size.width * 0.5;
let rb_center = root_bounds.center_x(); let rb_center = root_bounds.center_x();
@ -1268,22 +1130,14 @@ pub(super) fn init_root_menu<Message: Clone>(
menu.is_overlay, menu.is_overlay,
); );
set = true; set = true;
dbg!(i);
dbg!("inserting root menu state"); state.active_root.insert(menu.depth, i);
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?
let ms = MenuState { let ms = MenuState {
index: None, index: None,
scroll_offset: 0.0, scroll_offset: 0.0,
menu_bounds, menu_bounds,
}; };
dbg!("pushing to menu states..."); state.menu_states.push(ms);
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth);
v.push(ms);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
shell.invalidate_layout(); shell.invalidate_layout();
@ -1292,12 +1146,12 @@ pub(super) fn init_root_menu<Message: Clone>(
} }
} }
if !set { if !set {
dbg!(overlay_cursor);
panic!("huh"); panic!("huh");
} }
}); });
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
pub(super) fn init_root_popup_menu<Message>( pub(super) fn init_root_popup_menu<Message>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, Message>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -1310,61 +1164,29 @@ pub(super) fn init_root_popup_menu<Message>(
) where ) where
Message: std::clone::Clone, Message: std::clone::Clone,
{ {
dbg!("init");
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
if !(state if !(state
.menu_states .menu_states
.get(menu.depth) .get(menu.depth)
.is_none_or(|s| s.is_empty()) .is_none()
&& (!menu.is_overlay || bar_bounds.contains(overlay_cursor))) && (!menu.is_overlay || bar_bounds.contains(overlay_cursor)))
{ {
dbg!("exiting init early", menu.depth);
return; return;
} }
println!( let active_roots = &state
"init root menu {} {:?}", .active_root[..=menu.depth];
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::<Vec<_>>());
// 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 mut set = false; let mut set = false;
let mt = active_roots let mt = active_roots.iter()
.iter() .skip(1)
.skip(if menu.is_overlay { 0 } else { 1 })
.fold(&menu.menu_roots[active_roots[0]], |mt, next_active_root| { .fold(&menu.menu_roots[active_roots[0]], |mt, next_active_root| {
&mt.children[*next_active_root] &mt.children[*next_active_root]
}); });
let i = active_roots.last().unwrap(); let i = active_roots.last().unwrap();
let root_bounds = menu.root_bounds_list[*i]; let root_bounds = menu.root_bounds_list[*i];
if mt.children.is_empty() { assert!(!mt.children.is_empty(), "skipping menu with no children");
panic!("skipping menu with no children");
}
dbg!(root_bounds, overlay_cursor);
let aod = Aod { let aod = Aod {
horizontal: true, horizontal: true,
vertical: true, vertical: true,
@ -1389,8 +1211,7 @@ pub(super) fn init_root_popup_menu<Message>(
&mut state.tree.children[0].children, &mut state.tree.children[0].children,
menu.is_overlay, 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 view_center = viewport_size.width * 0.5;
let rb_center = root_bounds.center_x(); let rb_center = root_bounds.center_x();
@ -1399,12 +1220,6 @@ pub(super) fn init_root_popup_menu<Message>(
} else { } else {
Direction::Positive Direction::Positive
}; };
// dbg!(
// state.tree.children.len(),
// state.tree.children[0].children.len(),
// );
set = true; set = true;
let ms = MenuState { let ms = MenuState {
@ -1412,23 +1227,18 @@ pub(super) fn init_root_popup_menu<Message>(
scroll_offset: 0.0, scroll_offset: 0.0,
menu_bounds, menu_bounds,
}; };
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth); state.menu_states.push(ms);
v.push(ms);
dbg!(v.len(), menu.depth);
dbg!(i);
// Hack to ensure menu opens properly // Hack to ensure menu opens properly
shell.invalidate_layout(); shell.invalidate_layout();
// non tree buttons arent active?
if !set { assert!(set, "oops");
panic!("oops"); });
}
})
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn process_menu_events<'b, Message: std::clone::Clone>( fn process_menu_events<Message: std::clone::Clone>(
menu: &'b mut Menu<Message>, menu: &mut Menu<Message>,
event: event::Event, event: event::Event,
view_cursor: Cursor, view_cursor: Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -1438,70 +1248,44 @@ fn process_menu_events<'b, Message: std::clone::Clone>(
) -> event::Status { ) -> event::Status {
use event::Status; use event::Status;
let window_id = menu.window_id;
let is_overlay = menu.is_overlay;
let my_state = &mut menu.tree; let my_state = &mut menu.tree;
let menu_roots = match &mut menu.menu_roots { let menu_roots = match &mut menu.menu_roots {
Cow::Borrowed(_) => panic!(), Cow::Borrowed(_) => panic!(),
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(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; return Status::Ignored;
}; };
let indices = state.get_trimmed_indices(menu.depth); let Some(hover_index) = hover.index else {
let indices = indices.collect::<Vec<_>>();
// if is_overlay {
// indices.collect::<Vec<_>>()
// } else {
// indices.take(1).collect::<Vec<_>>()
// };
if indices.is_empty() {
return Status::Ignored; return Status::Ignored;
} };
// get active item let (tree, mt) = state.active_root
// 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
.iter() .iter()
.skip(if menu.is_overlay { 0 } else { 1 }) .skip(1)
.fold( .fold(
// then use menu states for each open menu
( (
&mut state.tree.children[active_root[0]].children, &mut state.tree.children[state.active_root[0]].children,
&mut menu_roots[active_root[0]], &mut menu_roots[state.active_root[0]],
), ),
|(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), |(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]; let tree = &mut tree[mt.index];
// get layout // get layout
let last_ms = &state.menu_states[menu.depth][indices.len() - 1]; let child_node = hover.layout_single(
let child_node = last_ms.layout_single(
overlay_offset, overlay_offset,
last_ms.index.expect("missing index within menu state."), hover.index.expect("missing index within menu state."),
renderer, renderer,
mt, mt,
tree, tree,
@ -1531,8 +1315,7 @@ fn process_overlay_events<Message>(
view_cursor: Cursor, view_cursor: Cursor,
overlay_cursor: Point, overlay_cursor: Point,
cross_offset: f32, cross_offset: f32,
is_overlay: bool, _shell: &mut Shell<'_, Message>,
shell: &mut Shell<'_, Message>,
) -> (Option<(usize, MenuState)>, event::Status) ) -> (Option<(usize, MenuState)>, event::Status)
where where
Message: std::clone::Clone, Message: std::clone::Clone,
@ -1550,12 +1333,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.depth).clone() else { let active_root = &state.active_root[..=menu.depth];
if is_overlay && !menu.bar_bounds.contains(overlay_cursor) {
state.reset();
}
return (new_menu_root, Ignored);
};
if state.pressed { if state.pressed {
return (new_menu_root, Ignored); return (new_menu_root, Ignored);
@ -1566,28 +1344,24 @@ 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]) if menu.is_overlay {
.iter() state.menu_states[..state.menu_states.len().saturating_sub(1)].iter()
.map(|s| s[0].menu_bounds.children_bounds), } else {
state.menu_states[..menu.depth].iter()
}
.map(|s| s.menu_bounds.children_bounds),
) )
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let menu_states = state.menu_states.get_mut(menu.depth).unwrap();
if menu.is_overlay && menu.close_condition.leave {
if menu.close_condition.leave { for i in (0..state.menu_states.len()).rev() {
for i in (0..menu_states.len()).rev() { let mb = &state.menu_states[i].menu_bounds;
let mb = &menu_states[i].menu_bounds;
if mb.parent_bounds.contains(overlay_cursor) 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.offset_bounds.contains(overlay_cursor)
|| (mb.check_bounds.contains(overlay_cursor) || (mb.check_bounds.contains(overlay_cursor)
&& prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))) && prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)))
@ -1595,31 +1369,18 @@ where
break; break;
} }
prev_bounds.pop(); 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) state.menu_states.pop();
|| mb.children_bounds.contains(overlay_cursor)
|| prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))
{
break;
}
prev_bounds.pop();
menu_states.pop();
} }
} }
// get indices
let indices = menu_states.iter().map(|ms| ms.index).collect::<Vec<_>>();
let should_add = is_overlay || menu_states.len() < 2;
dbg!(&indices);
// 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 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 { if menu.is_overlay {
// no menus left // no menus left
// TODO do we want to avoid this for popups? // 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); return (new_menu_root, Captured);
}; };
@ -1641,26 +1401,27 @@ where
let last_parent_bounds = last_menu_bounds.parent_bounds; let last_parent_bounds = last_menu_bounds.parent_bounds;
let last_children_bounds = last_menu_bounds.children_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 // 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 // cursor is outside
{ {
last_menu_state.index = None; last_menu_state.index = None;
dbg!("cursor is outside...");
return (new_menu_root, Captured); return (new_menu_root, Captured);
} }
// cursor is in the children part // cursor is in the children part
// TODO set active root here even when not a tree.
// ensure that the
// calc new index // calc new index
let height_diff = (overlay_cursor.y let height_diff = (overlay_cursor.y
- (last_children_bounds.y + last_menu_state.scroll_offset)) - (last_children_bounds.y + last_menu_state.scroll_offset))
.clamp(0.0, last_children_bounds.height - 0.001); .clamp(0.0, last_children_bounds.height - 0.001);
// dbg!(height_diff);
let (active_tree, roots) = active_root let (active_tree, roots) = active_root
.iter() .iter()
.skip(if menu.is_overlay { 0 } else { 1 }) .skip(1)
.fold( .fold(
( (
&mut state.tree.children[active_root[0]].children, &mut state.tree.children[active_root[0]].children,
@ -1668,22 +1429,8 @@ 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 = roots;
}
let active_menu = if is_overlay {
indices[0..indices.len().saturating_sub(1)].iter().fold(
roots,
|mt: &Vec<MenuTree<Message>>, 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 new_index = match menu.item_height { let new_index = match menu.item_height {
ItemHeight::Uniform(u) => (height_diff / f32::from(u)).floor() as usize, ItemHeight::Uniform(u) => (height_diff / f32::from(u)).floor() as usize,
ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { ItemHeight::Static(_) | ItemHeight::Dynamic(_) => {
@ -1698,41 +1445,32 @@ where
) )
} }
}; };
let remove = last_menu_state
.index
.as_ref() let remove = !menu.is_overlay
.is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); && 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"))] #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
if remove { {
if let Some(id) = state.popup_id.remove(&menu.window_id) { if remove {
state.active_root.pop(); if let Some(id) = state.popup_id.remove(&menu.window_id) {
shell.publish((menu.on_surface_action.as_ref().unwrap())({ state.active_root.truncate(menu.depth + 1);
crate::surface::action::destroy_popup(id) _shell.publish((menu.on_surface_action.as_ref().unwrap())({
})) crate::surface::action::destroy_popup(id)
}; }));
}
}
} }
let item = &active_menu[new_index]; 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 // set new index
let old_index = last_menu_state.index.replace(new_index); let old_index = last_menu_state.index.replace(new_index);
// dbg!(should_add);
dbg!(old_index, new_index);
// get new active item // get new active item
// * add new menu if the new item is a menu // * add new menu if the new item is a menu
if !item.children.is_empty() { if !item.children.is_empty() && old_index.is_none_or(|i| i != new_index) {
// dbg!("its a menu");
let item_position = Point::new( let item_position = Point::new(
0.0, 0.0,
last_menu_bounds.child_positions[new_index] + last_menu_state.scroll_offset, last_menu_bounds.child_positions[new_index] + last_menu_state.scroll_offset,
@ -1770,19 +1508,14 @@ where
menu.is_overlay, 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()));
new_menu_root = Some((new_index, ms.clone())); state.menu_states.truncate(menu.depth + 1);
} state.menu_states.push(ms);
if should_add { } else if remove {
let v = super::menu_bar::get_mut_or_default(&mut state.menu_states, menu.depth); state.menu_states.truncate(menu.depth + 1);
v.push(ms);
}
} }
if remove {
state.menu_states.pop();
}
(new_menu_root, Captured) (new_menu_root, Captured)
}) })
} }
@ -1801,7 +1534,6 @@ where
use mouse::ScrollDelta; use mouse::ScrollDelta;
menu.tree.inner.with_data_mut(|state| { menu.tree.inner.with_data_mut(|state| {
dbg!(state.active_root.len());
let delta_y = match delta { let delta_y = match delta {
ScrollDelta::Lines { y, .. } => y * 60.0, ScrollDelta::Lines { y, .. } => y * 60.0,
@ -1817,13 +1549,12 @@ 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(0).unwrap();
// update // update
if menu_states.is_empty() { if state.menu_states.is_empty() {
return Ignored; return Ignored;
} else if menu_states.len() == 1 { } else if state.menu_states.len() == 1 {
let last_ms = &mut menu_states[0]; let last_ms = &mut state.menu_states[0];
if last_ms.index.is_none() { if last_ms.index.is_none() {
return Captured; return Captured;
@ -1833,8 +1564,8 @@ where
last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset); last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset);
} else { } else {
// >= 2 // >= 2
let max_index = menu_states.len() - 1; let max_index = state.menu_states.len() - 1;
let last_two = &mut menu_states[max_index - 1..=max_index]; let last_two = &mut state.menu_states[max_index - 1..=max_index];
if last_two[1].index.is_some() { if last_two[1].index.is_some() {
// scroll the last one // scroll the last one
@ -1927,7 +1658,7 @@ fn get_children_layout<Message>(
.collect(), .collect(),
}; };
let max_index = menu_tree.children.len() - 1; let max_index = menu_tree.children.len().saturating_sub(1);
let child_positions: Vec<f32> = std::iter::once(0.0) let child_positions: Vec<f32> = std::iter::once(0.0)
.chain(child_sizes[0..max_index].iter().scan(0.0, |acc, x| { .chain(child_sizes[0..max_index].iter().scan(0.0, |acc, x| {
*acc += x.height; *acc += x.height;

View file

@ -252,7 +252,7 @@ pub fn menu_items<
} }
// dbg!("button with action...", action.message()); // 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::<Message>::from(Element::from(menu_button))); trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
} }
@ -272,7 +272,7 @@ pub fn menu_items<
items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); 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::<Message>::from(Element::from(menu_button))); trees.push(MenuTree::<Message>::from(Element::from(menu_button)));
} }
@ -321,7 +321,6 @@ pub fn menu_items<
.icon() .icon()
.into(), .into(),
]) ])
.description(l.clone())
.class( .class(
// Menu folders have no on_press so they take on the disabled style by default // Menu folders have no on_press so they take on the disabled style by default
if children.is_empty() { if children.is_empty() {

View file

@ -86,7 +86,7 @@ impl ResponsiveMenuBar {
crate::widget::RcElementWrapper::new(Element::from( crate::widget::RcElementWrapper::new(Element::from(
menu::root(mt.0), menu::root(mt.0),
)), )),
menu::items(key_binds, mt.1.into()), menu::items(key_binds, mt.1),
) )
}) })
.collect(), .collect(),