From dc97166f914f8273e2521188ba5f1121f0ca3fda Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 14 Oct 2024 11:21:40 -0600 Subject: [PATCH] fix(segmented_button): diff the context menu --- src/widget/menu.rs | 1 + src/widget/menu/menu_bar.rs | 139 +++++++++++++++----------- src/widget/segmented_button/widget.rs | 32 +++--- 3 files changed, 95 insertions(+), 77 deletions(-) diff --git a/src/widget/menu.rs b/src/widget/menu.rs index a4deb345..d5ea3222 100644 --- a/src/widget/menu.rs +++ b/src/widget/menu.rs @@ -72,5 +72,6 @@ pub use menu_tree::{ }; pub use crate::style::menu_bar::{Appearance, StyleSheet}; +pub(crate) use menu_bar::{menu_roots_children, menu_roots_diff}; pub(crate) use menu_inner::Menu; pub use menu_inner::{CloseCondition, ItemHeight, ItemWidth, PathHighlight}; diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index c191ac0d..257ccbfb 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -63,6 +63,82 @@ impl Default for MenuBarState { } } +pub(crate) fn menu_roots_children<'a, Message, Renderer>( + menu_roots: &Vec>, +) -> Vec +where + Renderer: renderer::Renderer, +{ + /* + menu bar + menu root 1 (stateless) + flat tree + menu root 2 (stateless) + flat tree + ... + */ + + menu_roots + .iter() + .map(|root| { + let mut tree = Tree::empty(); + let flat = root + .flattern() + .iter() + .map(|mt| Tree::new(mt.item.as_widget())) + .collect(); + tree.children = flat; + tree + }) + .collect() +} + +#[allow(invalid_reference_casting)] +pub(crate) fn menu_roots_diff<'a, Message, Renderer>( + menu_roots: &mut Vec>, + tree: &mut Tree, +) where + Renderer: renderer::Renderer, +{ + if tree.children.len() > menu_roots.len() { + tree.children.truncate(menu_roots.len()); + } + + tree.children + .iter_mut() + .zip(menu_roots.iter()) + .for_each(|(t, root)| { + let mut flat = root + .flattern() + .iter() + .map(|mt| { + let widget = mt.item.as_widget(); + let widget_ptr = widget as *const dyn Widget; + let widget_ptr_mut = + widget_ptr as *mut dyn Widget; + //TODO: find a way to diff_children without unsafe code + unsafe { &mut *widget_ptr_mut } + }) + .collect::>(); + + t.diff_children(flat.as_mut_slice()); + }); + + if tree.children.len() < menu_roots.len() { + let extended = menu_roots[tree.children.len()..].iter().map(|root| { + let mut tree = Tree::empty(); + let flat = root + .flattern() + .iter() + .map(|mt| Tree::new(mt.item.as_widget())) + .collect(); + tree.children = flat; + tree + }); + tree.children.extend(extended); + } +} + /// A `MenuBar` collects `MenuTree`s and handles all the layout, event processing, and drawing. #[allow(missing_debug_implementations)] pub struct MenuBar<'a, Message, Renderer = crate::Renderer> @@ -212,46 +288,8 @@ where iced_core::Size::new(self.width, self.height) } - #[allow(invalid_reference_casting)] fn diff(&mut self, tree: &mut Tree) { - if tree.children.len() > self.menu_roots.len() { - tree.children.truncate(self.menu_roots.len()); - } - - tree.children - .iter_mut() - .zip(self.menu_roots.iter()) - .for_each(|(t, root)| { - let mut flat = root - .flattern() - .iter() - .map(|mt| { - let widget = mt.item.as_widget(); - let widget_ptr = - widget as *const dyn Widget; - let widget_ptr_mut = - widget_ptr as *mut dyn Widget; - //TODO: find a way to diff_children without unsafe code - unsafe { &mut *widget_ptr_mut } - }) - .collect::>(); - - t.diff_children(flat.as_mut_slice()); - }); - - if tree.children.len() < self.menu_roots.len() { - let extended = self.menu_roots[tree.children.len()..].iter().map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }); - tree.children.extend(extended); - } + menu_roots_diff(&mut self.menu_roots, tree); } fn tag(&self) -> tree::Tag { @@ -263,28 +301,7 @@ where } fn children(&self) -> Vec { - /* - menu bar - menu root 1 (stateless) - flat tree - menu root 2 (stateless) - flat tree - ... - */ - - self.menu_roots - .iter() - .map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }) - .collect() + menu_roots_children(&self.menu_roots) } fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index fc0b39cc..f070c7e7 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -6,7 +6,8 @@ use crate::iced_core::id::Internal; use crate::theme::{SegmentedButton as Style, THEME}; use crate::widget::dnd_destination::DragId; use crate::widget::menu::{ - self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, + self, menu_roots_children, menu_roots_diff, CloseCondition, ItemHeight, ItemWidth, + MenuBarState, PathHighlight, }; use crate::widget::{icon, Icon}; use crate::{Element, Renderer}; @@ -560,20 +561,7 @@ where if let Some(ref context_menu) = self.context_menu { let mut tree = Tree::empty(); tree.state = tree::State::new(MenuBarState::default()); - tree.children = context_menu - .iter() - .map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }) - .collect(); - + tree.children = menu_roots_children(&context_menu); children.push(tree); } @@ -655,7 +643,19 @@ where } } - // TODO: diff the context menu + // Diff the context menu + if let Some(context_menu) = &mut self.context_menu { + if tree.children.is_empty() { + let mut child_tree = Tree::empty(); + child_tree.state = tree::State::new(MenuBarState::default()); + tree.children.push(child_tree); + } else { + tree.children.truncate(1); + } + menu_roots_diff(context_menu, &mut tree.children[0]); + } else { + tree.children.clear(); + } } fn size(&self) -> Size {