diff --git a/src/widget/context_menu.rs b/src/widget/context_menu.rs index 46dc1b88..dc46da01 100644 --- a/src/widget/context_menu.rs +++ b/src/widget/context_menu.rs @@ -260,6 +260,7 @@ impl Widget is_overlay: true, window_id: window::Id::NONE, depth: 0, + on_surface_action: None, } .overlay(), ) diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index 5cdbf2d2..21e0331e 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -1,7 +1,7 @@ // From iced_aw, license MIT //! A widget that handles menu trees -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use super::{ menu_inner::{ @@ -12,6 +12,7 @@ use super::{ use crate::{ Renderer, style::menu_bar::StyleSheet, + surface::action::destroy_popup, widget::{ RcWrapper, dropdown::menu::{self, State}, @@ -70,6 +71,7 @@ impl MenuBarStateInner { } pub(super) fn reset(&mut self) { + dbg!("reset"); self.open = false; self.active_root = Vec::new(); self.menu_states.clear(); @@ -192,7 +194,8 @@ pub struct MenuBar { window_id: window::Id, #[cfg(all(feature = "wayland", feature = "winit"))] positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, - pub(crate) on_surface_action: Option Message>>, + pub(crate) on_surface_action: + Option Message + Send + Sync + 'static>>, } impl MenuBar @@ -348,9 +351,9 @@ where pub fn on_surface_action( mut self, - handler: impl Fn(crate::surface::Action) -> Message + 'static, + handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static, ) -> Self { - self.on_surface_action = Some(Box::new(handler)); + self.on_surface_action = Some(Arc::new(handler)); self } } @@ -420,9 +423,10 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - use event::Event::{Mouse, Touch}; + use event::Event::{Mouse, Touch, Window}; use mouse::{Button::Left, Event::ButtonReleased}; use touch::Event::{FingerLifted, FingerLost}; + use window::Event::Focused; let root_status = process_root_events( &mut self.menu_roots, @@ -439,12 +443,16 @@ where let my_state = tree.state.downcast_mut::(); // XXX this should reset the state if there are no other copies of the state, which implies no dropdown menus open. - let reset = self.window_id != window::Id::NONE && my_state.inner.with_data(|d| d.open); + let reset = self.window_id != window::Id::NONE + && my_state + .inner + .with_data(|d| !d.open && !d.active_root.is_empty()); my_state.inner.with_data_mut(|state| { if reset { if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { dbg!("reset destroy"); + if let Some(handler) = self.on_surface_action.as_ref() { shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id))); state.reset(); @@ -457,13 +465,29 @@ where match event { Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { - my_state.inner.with_data_mut(|state| { + let create_popup = my_state.inner.with_data_mut(|state| { + let mut create_popup = false; if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { state.view_cursor = view_cursor; + dbg!(view_cursor.is_over(layout.bounds())); state.open = true; + create_popup = true; + } else if let Some(id) = state.popup_id.remove(&self.window_id) { + dbg!("destroy popup..."); + state.menu_states.clear(); + state.active_root.clear(); + let surface_action = self.on_surface_action.as_ref().unwrap(); + state.open = false; + shell.publish(surface_action(destroy_popup(id))); + state.view_cursor = view_cursor; } + create_popup }); - #[cfg(all(feature = "wayland", feature = "winit"))] + dbg!(create_popup); + if !create_popup { + return root_status; + } + #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] dbg!( self.window_id != window::Id::NONE, @@ -533,6 +557,7 @@ where is_overlay: false, window_id: id, depth: 0, + on_surface_action: self.on_surface_action.clone(), }; init_root_menu( @@ -545,7 +570,8 @@ where layout.bounds(), self.main_offset as f32, ); - let anchor_rect = my_state.inner.with_data(|state| { + 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()) @@ -601,11 +627,15 @@ where } } // Window(Focused) => { - // if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { - // dbg!("window focused"); - // shell.publish(Message::from(SurfaceMessage::DestroyPopup(popup_id))); - // } - // state.reset(); + // my_state.inner.with_data_mut(|state| { + // if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() { + // dbg!("window focused"); + // if let Some(handler) = self.on_surface_action.as_ref() { + // shell.publish((handler)(destroy_popup(popup_id))); + // state.reset(); + // } + // } + // }); // } _ => (), } @@ -635,7 +665,11 @@ where // draw path highlight if self.path_highlight.is_some() { let styling = theme.appearance(&self.style); - if let Some(active) = get_mut_or_default(&mut state.active_root, 0).get(0) { + if let Some(active) = state + .active_root + .get(0) + .and_then(|active_root| active_root.get(0)) + { let active_bounds = layout .children() .nth(*active) @@ -679,13 +713,13 @@ where _renderer: &Renderer, translation: Vector, ) -> Option> { - #[cfg(all(feature = "wayland", feature = "winit"))] + // #[cfg(all(feature = "wayland", feature = "winit"))] return None; let state = tree.state.downcast_ref::(); if state .inner - .with_data_mut(|state| !state.open || state.active_root.is_empty()) + .with_data(|state| !state.open || state.active_root.is_empty()) { return None; }; @@ -709,6 +743,7 @@ where is_overlay: true, window_id: window::Id::NONE, depth: 0, + on_surface_action: self.on_surface_action.clone(), } .overlay(), ) diff --git a/src/widget/menu/menu_inner.rs b/src/widget/menu/menu_inner.rs index 5ff73053..d8103436 100644 --- a/src/widget/menu/menu_inner.rs +++ b/src/widget/menu/menu_inner.rs @@ -1,7 +1,7 @@ // From iced_aw, license MIT //! Menu tree overlay -use std::borrow::Cow; +use std::{borrow::Cow, sync::Arc}; use super::{menu_bar::MenuBarState, menu_tree::MenuTree}; use crate::style::menu_bar::StyleSheet; @@ -466,6 +466,8 @@ pub(crate) struct Menu<'b, Message: std::clone::Clone> { /// window id for this popup pub(crate) window_id: window::Id, pub(crate) depth: usize, + pub(crate) on_surface_action: + Option Message + Send + Sync + 'static>>, } impl<'b, Message: Clone + 'static> Menu<'b, Message> { pub(crate) fn overlay(self) -> overlay::Element<'b, Message, crate::Theme, crate::Renderer> { @@ -473,6 +475,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { } pub(crate) fn layout(&self, renderer: &crate::Renderer, limits: Limits) -> Node { + dbg!("layout"); // layout children; let position = self.position; let mut intrinsic_size = Size::ZERO; @@ -480,6 +483,8 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { dbg!(self.depth); let empty = Vec::new(); self.tree.inner.with_data_mut(|data| { + dbg!(data.active_root.len()); + let overlay_offset = Point::ORIGIN - position; let tree_children = &mut data.tree.children; dbg!(&data.active_root,); @@ -557,11 +562,12 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { .unwrap_or_default(); // dbg!(intrinsic_size); // overlay space viewport rectangle - Node::with_children( + let node = Node::with_children( limits.resolve(Length::Shrink, Length::Shrink, intrinsic_size), children, ) - .translate(Point::ORIGIN - position) + .translate(Point::ORIGIN - position); + node }) } @@ -574,6 +580,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> (Option<(usize, MenuState)>, event::Status) { + // dbg!("on event"); use event::{ Event::{Mouse, Touch}, Status::{Captured, Ignored}, @@ -606,7 +613,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { overlay_offset, ); - dbg!("init_root_menu"); init_root_menu( self, renderer, @@ -650,6 +656,8 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { self.cross_offset as f32, self.is_overlay, ); + dbg!(new_root.as_ref().map(|r| r.0)); + return (new_root, status.merge(menu_status)); } @@ -668,19 +676,24 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { let is_inside = state.menu_states[self.depth] .iter() .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); - - if self.close_condition.click_inside + let mut needs_reset = false; + needs_reset |= self.close_condition.click_inside && is_inside && matches!( event, Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. }) - ) - { - state.reset(); - return Captured; - } + ); + + needs_reset |= self.close_condition.click_outside && !is_inside; + + if needs_reset { + dbg!("reset"); + if let Some(handler) = self.on_surface_action.as_ref() { + shell.publish((handler)(crate::surface::Action::DestroyPopup( + self.window_id, + ))); + } - if self.close_condition.click_outside && !is_inside { state.reset(); return Captured; } @@ -711,11 +724,12 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { view_cursor: Cursor, ) { self.tree.inner.with_data(|state| { + if !state.open { + return; + } let Some(active_root) = state.active_root.get(self.depth) else { - // dbg!(self.window_id); return; }; - dbg!(active_root); let viewport = layout.bounds(); let viewport_size = viewport.size(); let overlay_offset = Point::ORIGIN - viewport.position(); @@ -725,7 +739,6 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { } else { Rectangle::new(Point::ORIGIN, Size::INFINITY) }; - // dbg!(self.window_id, &active_root); let styling = theme.appearance(&self.style); let (active_tree, roots) = active_root.iter().take(self.depth).fold( @@ -736,24 +749,8 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { |(tree, mt), next_active_root| (tree, &mt[*next_active_root].children), ); - dbg!( - state.tree.children.len(), - state.tree.children[active_root[0]].children.len() - ); - // let tree = &state.tree.children[active_root].children; - // let root = &self.menu_roots[active_root]; - dbg!(&active_root.len()); - let indices = state.get_trimmed_indices(self.depth).collect::>(); - dbg!(self.depth); - dbg!( - self.window_id, - state.menu_states.len(), - state.menu_states[self.depth].len(), - layout.children().count(), - layout.bounds() - ); state.menu_states[self.depth] .iter() .zip(layout.children()) @@ -822,7 +819,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { } } if start_index < menu_roots.len() { - dbg!(start_index, end_index, menu_roots.len()); + // dbg!(start_index, end_index, menu_roots.len()); // draw item menu_roots[start_index..=end_index] .iter() @@ -906,7 +903,7 @@ impl<'a, Message: std::clone::Clone + 'static> Widget iced_core::layout::Node { - // dbg!(self.window_id, limits); + // dbg!("layout", self.window_id, limits); Menu::layout(self, renderer, *limits) } @@ -935,70 +932,84 @@ impl<'a, Message: std::clone::Clone + 'static> Widget event::Status { let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); - // #[cfg(feature = "wayland")] + dbg!(new_root.as_ref().map(|r| r.0)); + // #[cfg(all(feature = "wayland", feature = "surface-message"))] // if let Some((new_root, new_ms)) = new_root { // use iced_runtime::platform_specific::wayland::popup::{ // SctkPopupSettings, SctkPositioner, // }; - // let mut guard = self.tree.inner.lock().unwrap(); - // let popup_id = *guard - // .popup_id - // .entry(self.window_id) - // .or_insert_with(window::Id::unique); - // let active_roots = &guard.active_root[&self.window_id]; - // // dbg!(active_roots); - // let root_bounds_list = active_roots - // .into_iter() - // .fold(layout, |l, active_root| { - // // dbg!(active_root); - // l.children().nth(*active_root).unwrap() - // }) - // .children() - // .map(|c| c.bounds()) - // .collect(); - // drop(guard); + // dbg!(new_root); + // let Some((mut menu, popup_id)) = self.tree.inner.with_data_mut(|state| { + // let popup_id = *state + // .popup_id + // .entry(self.window_id) + // .or_insert_with(window::Id::unique); + // let active_roots = state + // .active_root + // .get(self.depth) + // .cloned() + // .unwrap_or_default(); // dbg!(active_roots); + // // let root_bounds_list = active_roots + // // .into_iter() + // // .fold(layout, |l, active_root| { + // // // dbg!(active_root); + // // l.children().nth(active_root).unwrap() + // // }) + // // .children() + // // .map(|c| c.bounds()) + // // .collect(); + // let root_bounds_list = layout.children().map(|lo| lo.bounds()).collect(); + // // drop(state); - // // dbg!(&root_bounds_list); - // // dbg!(self.menu_roots.clone().len()); - // // dbg!(self - // // .menu_roots - // // .clone() - // // .iter() - // // .map(|r| r.index) - // // .collect::>()); - // let mut popup_menu = Menu { - // tree: self.tree.clone(), - // menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())), - // bounds_expand: self.bounds_expand, - // menu_overlays_parent: false, - // close_condition: self.close_condition, - // item_width: self.item_width, - // item_height: self.item_height, - // bar_bounds: layout.bounds(), - // main_offset: self.main_offset, - // cross_offset: self.cross_offset, - // root_bounds_list, - // path_highlight: self.path_highlight, - // style: Cow::Owned(Cow::into_owned(self.style.clone())), - // position: Point::new(0., 0.), - // is_overlay: false, - // window_id: popup_id, - // depth: self.depth + 1, - // }; - // let mut guard = self.tree.inner.lock().unwrap(); - // // dbg!(guard.menu_states.keys()); - // let Some(parent_root) = guard.active_root.get(&self.window_id) else { - // // TODO log warning + // // dbg!(&root_bounds_list); + // // dbg!(self.menu_roots.clone().len()); + // // dbg!(self + // // .menu_roots + // // .clone() + // // .iter() + // // .map(|r| r.index) + // // .collect::>()); + // let mut popup_menu = Menu { + // tree: self.tree.clone(), + // menu_roots: Cow::Owned(Cow::into_owned(self.menu_roots.clone())), + // bounds_expand: self.bounds_expand, + // menu_overlays_parent: false, + // close_condition: self.close_condition, + // item_width: self.item_width, + // item_height: self.item_height, + // bar_bounds: layout.bounds(), + // main_offset: self.main_offset, + // cross_offset: self.cross_offset, + // root_bounds_list, + // path_highlight: self.path_highlight, + // style: Cow::Owned(Cow::into_owned(self.style.clone())), + // position: Point::new(0., 0.), + // is_overlay: false, + // window_id: popup_id, + // depth: self.depth + 1, + // on_surface_action: self.on_surface_action.clone(), + // }; + // // let mut state = self.tree.inner.lock().unwrap(); + // // 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 { + // return None; + // }; + // let mut roots = parent_root.clone(); + // roots.push(new_root); + // // dbg!(&roots); + // state.active_root.push(roots); + // // _ = state.menu_states.remove(&popup_id); + // // drop(state); + // Some((popup_menu, popup_id)) + // }) else { // return status; // }; - // let mut roots = parent_root.clone(); - // roots.push(new_root); - // // dbg!(&roots); - // guard.active_root.insert(popup_id, roots); - // _ = guard.menu_states.remove(&popup_id); - // drop(guard); // init_root_popup_menu( - // &mut popup_menu, + // &mut menu, // renderer, // shell, // cursor.position().unwrap(), @@ -1007,40 +1018,38 @@ impl<'a, Message: std::clone::Clone + 'static> Widget Widget Widget( ) where Message: std::clone::Clone, { + dbg!("init"); menu.tree.inner.with_data_mut(|state| { if !(state .menu_states @@ -1396,11 +1409,9 @@ fn process_menu_events<'b, Message: std::clone::Clone>( Cow::Owned(o) => o.as_mut_slice(), }; my_state.inner.with_data_mut(|state| { - dbg!(&state.active_root); let Some(active_root) = state.active_root.get(menu.depth).cloned() else { return Status::Ignored; }; - dbg!("got the active root"); let indices = state.get_trimmed_indices(menu.depth); @@ -1414,7 +1425,6 @@ fn process_menu_events<'b, Message: std::clone::Clone>( if indices.is_empty() { return Status::Ignored; } - dbg!(&active_root, &indices); // get active item // let mt = indices.iter().fold(root | mt, &i | &mut mt.children[i]); @@ -1425,7 +1435,6 @@ fn process_menu_events<'b, Message: std::clone::Clone>( ), |(tree, mt), next_active_root| (tree, &mut mt.children[*next_active_root]), ); - dbg!(mt.children.len(), mt.index,); // let Some(i) = state.menu_states[menu.depth].iter().position(|ms| { // ms.menu_bounds // .check_bounds @@ -1437,7 +1446,6 @@ fn process_menu_events<'b, Message: std::clone::Clone>( // widget tree let tree = &mut tree[mt.index]; - dbg!(tree.children.len()); // get layout let last_ms = &state.menu_states[menu.depth][indices.len() - 1]; @@ -1450,9 +1458,7 @@ fn process_menu_events<'b, Message: std::clone::Clone>( ); let child_layout = Layout::new(&child_node); - dbg!("item on event handler..."); // process only the last widget - dbg!(&event); mt.item.on_event( tree, event, @@ -1495,12 +1501,14 @@ where menu.tree.inner.with_data_mut(|state| { let Some(active_root) = state.active_root.get(menu.depth).clone() else { if is_overlay && !menu.bar_bounds.contains(overlay_cursor) { + dbg!("overlay"); state.reset(); } return (new_menu_root, Ignored); }; if state.pressed { + dbg!("pressed"); return (new_menu_root, Ignored); } @@ -1543,7 +1551,6 @@ where { break; } - // dbg!(menu_states.len()); prev_bounds.pop(); menu_states.pop(); } @@ -1562,6 +1569,7 @@ where // keep state.open when the cursor is still inside the menu bar // this allows the overlay to keep drawing when the cursor is // moving aroung the menu bar + dbg!("oops menu bar shouldn't disable popup"); if !menu.bar_bounds.contains(overlay_cursor) { state.open = false; } @@ -1621,6 +1629,15 @@ where ) } }; + // if 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); + // } + dbg!(&last_menu_state.index, new_index); let item = &active_menu[new_index]; // dbg!(new_index); @@ -1707,6 +1724,8 @@ where use mouse::ScrollDelta; menu.tree.inner.with_data_mut(|state| { + dbg!(state.active_root.len()); + let delta_y = match delta { ScrollDelta::Lines { y, .. } => y * 60.0, ScrollDelta::Pixels { y, .. } => y, diff --git a/src/widget/responsive_menu_bar.rs b/src/widget/responsive_menu_bar.rs index 307395a4..af9d7f76 100644 --- a/src/widget/responsive_menu_bar.rs +++ b/src/widget/responsive_menu_bar.rs @@ -64,7 +64,7 @@ impl ResponsiveMenuBar { core: &Core, key_binds: &HashMap, id: crate::widget::Id, - action_message: impl Fn(crate::surface::Action) -> Message + Clone + 'static, + action_message: impl Fn(crate::surface::Action) -> Message + Send + Sync + Clone + 'static, trees: Vec<(S, Vec>)>, ) -> Element<'a, Message> { use crate::widget::id_container; diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index ba57719d..313b686d 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -1618,6 +1618,7 @@ where is_overlay: true, window_id: window::Id::NONE, depth: 0, + on_surface_action: None, } .overlay(), )