1use std::collections::HashMap;
5use std::sync::Arc;
6
7use super::menu_inner::{
8 CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight,
9};
10use super::menu_tree::MenuTree;
11use crate::Renderer;
12#[cfg(wayland_platform)]
13use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
14use crate::style::menu_bar::StyleSheet;
15use crate::widget::RcWrapper;
16use crate::widget::dropdown::menu::{self, State};
17use crate::widget::menu::menu_inner::init_root_menu;
18
19use iced::event::Status;
20use iced::{Point, Shadow, Vector, window};
21use iced_core::Border;
22use iced_widget::core::layout::{Limits, Node};
23use iced_widget::core::mouse::{self, Cursor};
24use iced_widget::core::renderer::{self, Renderer as IcedRenderer};
25use iced_widget::core::widget::{Tree, tree};
26use iced_widget::core::{
27 Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Widget, event,
28 overlay, touch,
29};
30
31pub fn menu_bar<Message>(menu_roots: Vec<MenuTree<Message>>) -> MenuBar<Message>
33where
34 Message: Clone + 'static,
35{
36 MenuBar::new(menu_roots)
37}
38
39#[derive(Clone, Default)]
40pub(crate) struct MenuBarState {
41 pub(crate) inner: RcWrapper<MenuBarStateInner>,
42}
43
44pub(crate) struct MenuBarStateInner {
45 pub(crate) tree: Tree,
46 pub(crate) popup_id: HashMap<window::Id, window::Id>,
47 pub(crate) pressed: bool,
48 pub(crate) bar_pressed: bool,
49 pub(crate) view_cursor: Cursor,
50 pub(crate) open: bool,
51 pub(crate) active_root: Vec<usize>,
52 pub(crate) horizontal_direction: Direction,
53 pub(crate) vertical_direction: Direction,
54 pub(crate) menu_states: Vec<MenuState>,
56}
57impl MenuBarStateInner {
58 pub(super) fn get_trimmed_indices(&self, index: usize) -> impl Iterator<Item = usize> + '_ {
60 self.menu_states
61 .iter()
62 .skip(index)
63 .take_while(|ms| ms.index.is_some())
64 .map(|ms| ms.index.expect("No indices were found in the menu state."))
65 }
66
67 pub(crate) fn reset(&mut self) {
68 self.open = false;
69 self.active_root = Vec::new();
70 self.menu_states.clear();
71 }
72}
73impl Default for MenuBarStateInner {
74 fn default() -> Self {
75 Self {
76 tree: Tree::empty(),
77 pressed: false,
78 view_cursor: Cursor::Available([-0.5, -0.5].into()),
79 open: false,
80 active_root: Vec::new(),
81 horizontal_direction: Direction::Positive,
82 vertical_direction: Direction::Positive,
83 menu_states: Vec::new(),
84 popup_id: HashMap::new(),
85 bar_pressed: false,
86 }
87 }
88}
89
90pub(crate) fn menu_roots_children<Message>(menu_roots: &[MenuTree<Message>]) -> Vec<Tree>
91where
92 Message: Clone + 'static,
93{
94 menu_roots
104 .iter()
105 .map(|root| {
106 let mut tree = Tree::empty();
107 let flat = root
108 .flattern()
109 .iter()
110 .map(|mt| Tree::new(mt.item.clone()))
111 .collect();
112 tree.children = flat;
113 tree
114 })
115 .collect()
116}
117
118#[allow(invalid_reference_casting)]
119pub(crate) fn menu_roots_diff<Message>(menu_roots: &mut [MenuTree<Message>], tree: &mut Tree)
120where
121 Message: Clone + 'static,
122{
123 if tree.children.len() > menu_roots.len() {
124 tree.children.truncate(menu_roots.len());
125 }
126
127 tree.children
128 .iter_mut()
129 .zip(menu_roots.iter())
130 .for_each(|(t, root)| {
131 let mut flat = root
132 .flattern()
133 .iter()
134 .map(|mt| {
135 let widget = &mt.item;
136 let widget_ptr = widget as *const dyn Widget<Message, crate::Theme, Renderer>;
137 let widget_ptr_mut =
138 widget_ptr as *mut dyn Widget<Message, crate::Theme, Renderer>;
139 unsafe { &mut *widget_ptr_mut }
141 })
142 .collect::<Vec<_>>();
143
144 t.diff_children(flat.as_mut_slice());
145 });
146
147 if tree.children.len() < menu_roots.len() {
148 let extended = menu_roots[tree.children.len()..].iter().map(|root| {
149 let mut tree = Tree::empty();
150 let flat = root
151 .flattern()
152 .iter()
153 .map(|mt| Tree::new(mt.item.clone()))
154 .collect();
155 tree.children = flat;
156 tree
157 });
158 tree.children.extend(extended);
159 }
160}
161
162pub fn get_mut_or_default<T: Default>(vec: &mut Vec<T>, index: usize) -> &mut T {
163 if index < vec.len() {
164 &mut vec[index]
165 } else {
166 vec.resize_with(index + 1, T::default);
167 &mut vec[index]
168 }
169}
170
171#[allow(missing_debug_implementations)]
173pub struct MenuBar<Message> {
174 width: Length,
175 height: Length,
176 spacing: f32,
177 padding: Padding,
178 bounds_expand: u16,
179 main_offset: i32,
180 cross_offset: i32,
181 close_condition: CloseCondition,
182 item_width: ItemWidth,
183 item_height: ItemHeight,
184 path_highlight: Option<PathHighlight>,
185 menu_roots: Vec<MenuTree<Message>>,
186 style: <crate::Theme as StyleSheet>::Style,
187 window_id: window::Id,
188 #[cfg(wayland_platform)]
189 positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
190 pub(crate) on_surface_action:
191 Option<Arc<dyn Fn(crate::surface::Action<Message>) -> Message + Send + Sync + 'static>>,
192}
193
194impl<Message> MenuBar<Message>
195where
196 Message: Clone + 'static,
197{
198 #[must_use]
200 pub fn new(menu_roots: Vec<MenuTree<Message>>) -> Self {
201 let mut menu_roots = menu_roots;
202 menu_roots.iter_mut().for_each(MenuTree::set_index);
203
204 Self {
205 width: Length::Shrink,
206 height: Length::Shrink,
207 spacing: 0.0,
208 padding: Padding::ZERO,
209 bounds_expand: 16,
210 main_offset: 0,
211 cross_offset: 0,
212 close_condition: CloseCondition {
213 leave: false,
214 click_outside: true,
215 click_inside: true,
216 },
217 item_width: ItemWidth::Uniform(150),
218 item_height: ItemHeight::Uniform(30),
219 path_highlight: Some(PathHighlight::MenuActive),
220 menu_roots,
221 style: <crate::Theme as StyleSheet>::Style::default(),
222 window_id: window::Id::RESERVED,
223 #[cfg(wayland_platform)]
224 positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
225 on_surface_action: None,
226 }
227 }
228
229 #[must_use]
235 pub fn bounds_expand(mut self, value: u16) -> Self {
236 self.bounds_expand = value;
237 self
238 }
239
240 #[must_use]
242 pub fn close_condition(mut self, close_condition: CloseCondition) -> Self {
243 self.close_condition = close_condition;
244 self
245 }
246
247 #[must_use]
249 pub fn cross_offset(mut self, value: i32) -> Self {
250 self.cross_offset = value;
251 self
252 }
253
254 #[must_use]
256 pub fn height(mut self, height: Length) -> Self {
257 self.height = height;
258 self
259 }
260
261 #[must_use]
263 pub fn item_height(mut self, item_height: ItemHeight) -> Self {
264 self.item_height = item_height;
265 self
266 }
267
268 #[must_use]
270 pub fn item_width(mut self, item_width: ItemWidth) -> Self {
271 self.item_width = item_width;
272 self
273 }
274
275 #[must_use]
277 pub fn main_offset(mut self, value: i32) -> Self {
278 self.main_offset = value;
279 self
280 }
281
282 #[must_use]
284 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
285 self.padding = padding.into();
286 self
287 }
288
289 #[must_use]
291 pub fn path_highlight(mut self, path_highlight: Option<PathHighlight>) -> Self {
292 self.path_highlight = path_highlight;
293 self
294 }
295
296 #[must_use]
298 pub fn spacing(mut self, units: f32) -> Self {
299 self.spacing = units;
300 self
301 }
302
303 #[must_use]
305 pub fn style(mut self, style: impl Into<<crate::Theme as StyleSheet>::Style>) -> Self {
306 self.style = style.into();
307 self
308 }
309
310 #[must_use]
312 pub fn width(mut self, width: Length) -> Self {
313 self.width = width;
314 self
315 }
316
317 #[cfg(wayland_platform)]
318 pub fn with_positioner(
319 mut self,
320 positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
321 ) -> Self {
322 self.positioner = positioner;
323 self
324 }
325
326 #[must_use]
327 pub fn window_id(mut self, id: window::Id) -> Self {
328 self.window_id = id;
329 self
330 }
331
332 #[must_use]
333 pub fn window_id_maybe(mut self, id: Option<window::Id>) -> Self {
334 if let Some(id) = id {
335 self.window_id = id;
336 }
337 self
338 }
339
340 #[must_use]
341 pub fn on_surface_action(
342 mut self,
343 handler: impl Fn(crate::surface::Action<Message>) -> Message + Send + Sync + 'static,
344 ) -> Self {
345 self.on_surface_action = Some(Arc::new(handler));
346 self
347 }
348
349 #[cfg(wayland_platform)]
350 #[allow(clippy::too_many_lines)]
351 fn create_popup(
352 &mut self,
353 layout: Layout<'_>,
354 view_cursor: Cursor,
355 renderer: &Renderer,
356 shell: &mut Shell<'_, Message>,
357 viewport: &Rectangle,
358 my_state: &mut MenuBarState,
359 ) {
360 if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
361 use crate::surface::action::{LiveSettings, destroy_popup};
362 use crate::theme::THEME;
363 use iced_runtime::platform_specific::wayland::CornerRadius;
364 use iced_runtime::platform_specific::wayland::popup::{
365 SctkPopupSettings, SctkPositioner,
366 };
367
368 let surface_action = self.on_surface_action.as_ref().unwrap();
369 let old_active_root = my_state
370 .inner
371 .with_data(|state| state.active_root.first().copied());
372
373 let hovered_root = layout
375 .children()
376 .position(|lo| view_cursor.is_over(lo.bounds()));
377 if hovered_root.is_none()
378 || old_active_root
379 .zip(hovered_root)
380 .is_some_and(|r| r.0 == r.1)
381 {
382 return;
383 }
384
385 let (id, root_list) = my_state.inner.with_data_mut(|state| {
386 if let Some(id) = state.popup_id.get(&self.window_id).copied() {
387 state.menu_states.clear();
389 state.active_root.clear();
390 shell.publish(surface_action(destroy_popup(id)));
391 state.view_cursor = view_cursor;
392 }
393 (
395 window::Id::unique(),
396 layout.children().map(|lo| lo.bounds()).collect(),
397 )
398 });
399
400 let mut popup_menu: Menu<'static, _> = Menu {
401 tree: my_state.clone(),
402 menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()),
403 bounds_expand: self.bounds_expand,
404 menu_overlays_parent: false,
405 close_condition: self.close_condition,
406 item_width: self.item_width,
407 item_height: self.item_height,
408 bar_bounds: layout.bounds(),
409 main_offset: self.main_offset,
410 cross_offset: self.cross_offset,
411 root_bounds_list: root_list,
412 path_highlight: self.path_highlight,
413 style: std::borrow::Cow::Owned(self.style.clone()),
414 position: Point::new(0., 0.),
415 is_overlay: false,
416 window_id: id,
417 depth: 0,
418 on_surface_action: self.on_surface_action.clone(),
419 };
420
421 init_root_menu(
422 &mut popup_menu,
423 renderer,
424 shell,
425 view_cursor.position().unwrap(),
426 viewport.size(),
427 Vector::new(0., 0.),
428 layout.bounds(),
429 self.main_offset as f32,
430 );
431 let (anchor_rect, gravity) = my_state.inner.with_data_mut(|state| {
432 state.popup_id.insert(self.window_id, id);
433 (state
434 .menu_states
435 .iter()
436 .find(|s| s.index.is_none())
437 .map(|s| s.menu_bounds.parent_bounds)
438 .map_or_else(
439 || {
440 let bounds = layout.bounds();
441 Rectangle {
442 x: bounds.x as i32,
443 y: bounds.y as i32,
444 width: bounds.width as i32,
445 height: bounds.height as i32,
446 }
447 },
448 |r| Rectangle {
449 x: r.x as i32,
450 y: r.y as i32,
451 width: r.width as i32,
452 height: r.height as i32,
453 },
454 ), match (state.horizontal_direction, state.vertical_direction) {
455 (Direction::Positive, Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
456 (Direction::Positive, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight,
457 (Direction::Negative, Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft,
458 (Direction::Negative, Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft,
459 })
460 });
461
462 let menu_node = popup_menu.layout(renderer, Limits::NONE.min_width(1.).min_height(1.));
463 let popup_size = menu_node.size();
464 let positioner = SctkPositioner {
465 size: Some((
466 popup_size.width.ceil() as u32 + 2,
467 popup_size.height.ceil() as u32 + 2,
468 )),
469 anchor_rect,
470 anchor:
471 cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft,
472 gravity,
473 reactive: true,
474 ..Default::default()
475 };
476 let parent = self.window_id;
477
478 let t = THEME.lock().unwrap();
479 let styling = t.appearance(&crate::theme::menu_bar::MenuBarStyle::Default, false);
480 drop(t);
481 let rad = styling.menu_border_radius;
482
483 shell.publish((surface_action)(crate::surface::action::simple_popup(
484 move || LiveSettings {
485 corners: Some(CornerRadius {
486 top_left: rad[0] as u32,
487 top_right: rad[1] as u32,
488 bottom_left: rad[2] as u32,
489 bottom_right: rad[3] as u32,
490 }),
491 ..Default::default()
492 },
493 move || SctkPopupSettings {
494 parent,
495 id,
496 positioner: positioner.clone(),
497 parent_size: None,
498 grab: true,
499 close_with_children: false,
500 input_zone: None,
501 },
502 Some(move || {
503 Element::from(crate::widget::container(popup_menu.clone()).center(Length::Fill))
504 .map(crate::action::app)
505 }),
506 )));
507 }
508 }
509}
510impl<Message> Widget<Message, crate::Theme, Renderer> for MenuBar<Message>
511where
512 Message: Clone + 'static,
513{
514 fn size(&self) -> iced_core::Size<Length> {
515 iced_core::Size::new(self.width, self.height)
516 }
517
518 fn diff(&mut self, tree: &mut Tree) {
519 let state = tree.state.downcast_mut::<MenuBarState>();
520 state
521 .inner
522 .with_data_mut(|inner| menu_roots_diff(&mut self.menu_roots, &mut inner.tree));
523 }
524
525 fn tag(&self) -> tree::Tag {
526 tree::Tag::of::<MenuBarState>()
527 }
528
529 fn state(&self) -> tree::State {
530 tree::State::new(MenuBarState::default())
531 }
532
533 fn children(&self) -> Vec<Tree> {
534 menu_roots_children(&self.menu_roots)
535 }
536
537 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
538 use super::flex;
539
540 let limits = limits.width(self.width).height(self.height);
541 let mut children = self
542 .menu_roots
543 .iter_mut()
544 .map(|root| &mut root.item)
545 .collect::<Vec<_>>();
546 let mut tree_children = tree
548 .children
549 .iter_mut()
550 .map(|t| &mut t.children[0])
551 .collect::<Vec<_>>();
552 flex::resolve_wrapper(
553 &flex::Axis::Horizontal,
554 renderer,
555 &limits,
556 self.padding,
557 self.spacing,
558 Alignment::Center,
559 &mut children,
560 &mut tree_children,
561 )
562 }
563
564 #[allow(clippy::too_many_lines)]
565 fn update(
566 &mut self,
567 tree: &mut Tree,
568 event: &event::Event,
569 layout: Layout<'_>,
570 view_cursor: Cursor,
571 renderer: &Renderer,
572 clipboard: &mut dyn Clipboard,
573 shell: &mut Shell<'_, Message>,
574 viewport: &Rectangle,
575 ) {
576 use event::Event::{Mouse, Touch};
577 use mouse::Button::Left;
578 use mouse::Event::ButtonReleased;
579 use touch::Event::{FingerLifted, FingerLost};
580
581 process_root_events(
582 &mut self.menu_roots,
583 view_cursor,
584 tree,
585 event,
586 layout,
587 renderer,
588 clipboard,
589 shell,
590 viewport,
591 );
592
593 let my_state = tree.state.downcast_mut::<MenuBarState>();
594
595 #[cfg(wayland_platform)]
597 if let iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
598 iced::event::wayland::Event::Popup(iced::event::wayland::PopupEvent::Done, _, popup),
599 )) = event
600 {
601 my_state.inner.with_data_mut(|d| {
602 if d.popup_id.get(&self.window_id) == Some(popup) {
603 d.popup_id.clear();
605 d.reset();
606 }
607 });
608 }
609
610 let reset = self.window_id != window::Id::NONE
612 && my_state
613 .inner
614 .with_data(|d| !d.open && !d.active_root.is_empty());
615
616 let open = my_state.inner.with_data_mut(|state| {
617 if reset {
618 if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
619 if let Some(handler) = self.on_surface_action.as_ref() {
620 shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
621 state.reset();
622 }
623 }
624 }
625 state.open
626 });
627
628 match event {
629 Mouse(mouse::Event::ButtonPressed(Left))
630 | Touch(touch::Event::FingerPressed { .. })
631 if view_cursor.is_over(layout.bounds()) =>
632 {
633 shell.capture_event();
635 }
636 Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
637 let create_popup = my_state.inner.with_data_mut(|state| {
638 let mut create_popup = false;
639 if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
640 state.view_cursor = view_cursor;
641 state.open = true;
642 create_popup = true;
643 } else if let Some(_id) = state.popup_id.remove(&self.window_id) {
644 state.menu_states.clear();
645 state.active_root.clear();
646 state.open = false;
647 #[cfg(wayland_platform)]
648 {
649 let surface_action = self.on_surface_action.as_ref().unwrap();
650 shell.capture_event();
651
652 shell.publish(surface_action(crate::surface::action::destroy_popup(
653 _id,
654 )));
655 }
656 state.view_cursor = view_cursor;
657 }
658 create_popup
659 });
660
661 if !create_popup {
662 return;
663 }
664 shell.capture_event();
665 shell.request_redraw();
666 #[cfg(wayland_platform)]
667 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
668 self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
669 }
670 }
671 Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
672 if open && view_cursor.is_over(layout.bounds()) =>
673 {
674 shell.request_redraw();
675 shell.capture_event();
676 #[cfg(wayland_platform)]
677 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
678 self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
679 }
680 }
681 _ => (),
682 }
683 }
684
685 fn draw(
686 &self,
687 tree: &Tree,
688 renderer: &mut Renderer,
689 theme: &crate::Theme,
690 style: &renderer::Style,
691 layout: Layout<'_>,
692 view_cursor: Cursor,
693 viewport: &Rectangle,
694 ) {
695 let state = tree.state.downcast_ref::<MenuBarState>();
696 let cursor_pos = view_cursor.position().unwrap_or_default();
697 state.inner.with_data_mut(|state| {
698 let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) {
699 state.view_cursor
700 } else {
701 view_cursor
702 };
703
704 if self.path_highlight.is_some() {
706 let mut is_overlay = true;
707 #[cfg(wayland_platform)]
708 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
709 && self.on_surface_action.is_some()
710 && self.window_id != window::Id::NONE
711 {
712 is_overlay = true;
713 };
714 let styling = theme.appearance(&self.style, is_overlay);
715 if let Some(active) = state.active_root.first() {
716 let active_bounds = layout
717 .children()
718 .nth(*active)
719 .expect("Active child not found in menu?")
720 .bounds();
721 let path_quad = renderer::Quad {
722 bounds: active_bounds,
723 border: Border {
724 radius: styling.bar_border_radius.into(),
725 ..Default::default()
726 },
727 shadow: Shadow::default(),
728 snap: true,
729 };
730
731 renderer.fill_quad(path_quad, styling.path);
732 }
733 }
734
735 self.menu_roots
736 .iter()
737 .zip(&tree.children)
738 .zip(layout.children())
739 .for_each(|((root, t), lo)| {
740 root.item.draw(
741 &t.children[root.index],
742 renderer,
743 theme,
744 style,
745 lo,
746 position,
747 viewport,
748 );
749 });
750 });
751 }
752
753 fn overlay<'b>(
754 &'b mut self,
755 tree: &'b mut Tree,
756 layout: Layout<'b>,
757 _renderer: &Renderer,
758 viewport: &Rectangle,
759 translation: Vector,
760 ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
761 #[cfg(wayland_platform)]
762 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
763 && self.on_surface_action.is_some()
764 && self.window_id != window::Id::NONE
765 {
766 return None;
767 }
768
769 let state = tree.state.downcast_ref::<MenuBarState>();
770 if state.inner.with_data(|state| !state.open) {
771 return None;
772 }
773
774 Some(
775 Menu {
776 tree: state.clone(),
777 menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()),
778 bounds_expand: self.bounds_expand,
779 menu_overlays_parent: false,
780 close_condition: self.close_condition,
781 item_width: self.item_width,
782 item_height: self.item_height,
783 bar_bounds: layout.bounds(),
784 main_offset: self.main_offset,
785 cross_offset: self.cross_offset,
786 root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(),
787 path_highlight: self.path_highlight,
788 style: std::borrow::Cow::Borrowed(&self.style),
789 position: Point::new(translation.x, translation.y),
790 is_overlay: true,
791 window_id: window::Id::NONE,
792 depth: 0,
793 on_surface_action: self.on_surface_action.clone(),
794 }
795 .overlay(),
796 )
797 }
798}
799
800impl<Message> From<MenuBar<Message>> for Element<'_, Message, crate::Theme, Renderer>
801where
802 Message: Clone + 'static,
803{
804 fn from(value: MenuBar<Message>) -> Self {
805 Self::new(value)
806 }
807}
808
809#[allow(unused_results, clippy::too_many_arguments)]
810fn process_root_events<Message>(
811 menu_roots: &mut [MenuTree<Message>],
812 view_cursor: Cursor,
813 tree: &mut Tree,
814 event: &event::Event,
815 layout: Layout<'_>,
816 renderer: &Renderer,
817 clipboard: &mut dyn Clipboard,
818 shell: &mut Shell<'_, Message>,
819 viewport: &Rectangle,
820) {
821 for ((root, t), lo) in menu_roots
822 .iter_mut()
823 .zip(&mut tree.children)
824 .zip(layout.children())
825 {
826 root.item.update(
828 &mut t.children[root.index],
829 event,
830 lo,
831 view_cursor,
832 renderer,
833 clipboard,
834 shell,
835 viewport,
836 );
837 }
838}