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