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