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 + 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 + 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 (id, layout.children().map(|lo| lo.bounds()).collect())
393 } else {
394 (
395 window::Id::unique(),
396 layout.children().map(|lo| lo.bounds()).collect(),
397 )
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 let reset = self.window_id != window::Id::NONE
598 && my_state
599 .inner
600 .with_data(|d| !d.open && !d.active_root.is_empty());
601
602 let open = my_state.inner.with_data_mut(|state| {
603 if reset {
604 if let Some(popup_id) = state.popup_id.get(&self.window_id).copied() {
605 if let Some(handler) = self.on_surface_action.as_ref() {
606 shell.publish((handler)(crate::surface::Action::DestroyPopup(popup_id)));
607 state.reset();
608 }
609 }
610 }
611 state.open
612 });
613
614 match event {
615 Mouse(mouse::Event::ButtonPressed(Left))
616 | Touch(touch::Event::FingerPressed { .. })
617 if view_cursor.is_over(layout.bounds()) =>
618 {
619 shell.capture_event();
621 }
622 Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => {
623 let create_popup = my_state.inner.with_data_mut(|state| {
624 let mut create_popup = false;
625 if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) {
626 state.view_cursor = view_cursor;
627 state.open = true;
628 create_popup = true;
629 } else if let Some(_id) = state.popup_id.remove(&self.window_id) {
630 state.menu_states.clear();
631 state.active_root.clear();
632 state.open = false;
633 #[cfg(wayland_platform)]
634 {
635 let surface_action = self.on_surface_action.as_ref().unwrap();
636 shell.capture_event();
637
638 shell.publish(surface_action(crate::surface::action::destroy_popup(
639 _id,
640 )));
641 }
642 state.view_cursor = view_cursor;
643 }
644 create_popup
645 });
646
647 if !create_popup {
648 return;
649 }
650 shell.capture_event();
651 #[cfg(wayland_platform)]
652 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
653 self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
654 }
655 }
656 Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
657 if open && view_cursor.is_over(layout.bounds()) =>
658 {
659 shell.capture_event();
660 #[cfg(wayland_platform)]
661 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
662 self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
663 }
664 }
665 _ => (),
666 }
667 }
668
669 fn draw(
670 &self,
671 tree: &Tree,
672 renderer: &mut Renderer,
673 theme: &crate::Theme,
674 style: &renderer::Style,
675 layout: Layout<'_>,
676 view_cursor: Cursor,
677 viewport: &Rectangle,
678 ) {
679 let state = tree.state.downcast_ref::<MenuBarState>();
680 let cursor_pos = view_cursor.position().unwrap_or_default();
681 state.inner.with_data_mut(|state| {
682 let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) {
683 state.view_cursor
684 } else {
685 view_cursor
686 };
687
688 if self.path_highlight.is_some() {
690 let mut is_overlay = true;
691 #[cfg(wayland_platform)]
692 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
693 && self.on_surface_action.is_some()
694 && self.window_id != window::Id::NONE
695 {
696 is_overlay = true;
697 };
698 let styling = theme.appearance(&self.style, is_overlay);
699 if let Some(active) = state.active_root.first() {
700 let active_bounds = layout
701 .children()
702 .nth(*active)
703 .expect("Active child not found in menu?")
704 .bounds();
705 let path_quad = renderer::Quad {
706 bounds: active_bounds,
707 border: Border {
708 radius: styling.bar_border_radius.into(),
709 ..Default::default()
710 },
711 shadow: Shadow::default(),
712 snap: true,
713 };
714
715 renderer.fill_quad(path_quad, styling.path);
716 }
717 }
718
719 self.menu_roots
720 .iter()
721 .zip(&tree.children)
722 .zip(layout.children())
723 .for_each(|((root, t), lo)| {
724 root.item.draw(
725 &t.children[root.index],
726 renderer,
727 theme,
728 style,
729 lo,
730 position,
731 viewport,
732 );
733 });
734 });
735 }
736
737 fn overlay<'b>(
738 &'b mut self,
739 tree: &'b mut Tree,
740 layout: Layout<'b>,
741 _renderer: &Renderer,
742 viewport: &Rectangle,
743 translation: Vector,
744 ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
745 #[cfg(wayland_platform)]
746 if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
747 && self.on_surface_action.is_some()
748 && self.window_id != window::Id::NONE
749 {
750 return None;
751 }
752
753 let state = tree.state.downcast_ref::<MenuBarState>();
754 if state.inner.with_data(|state| !state.open) {
755 return None;
756 }
757
758 Some(
759 Menu {
760 tree: state.clone(),
761 menu_roots: std::borrow::Cow::Owned(self.menu_roots.clone()),
762 bounds_expand: self.bounds_expand,
763 menu_overlays_parent: false,
764 close_condition: self.close_condition,
765 item_width: self.item_width,
766 item_height: self.item_height,
767 bar_bounds: layout.bounds(),
768 main_offset: self.main_offset,
769 cross_offset: self.cross_offset,
770 root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(),
771 path_highlight: self.path_highlight,
772 style: std::borrow::Cow::Borrowed(&self.style),
773 position: Point::new(translation.x, translation.y),
774 is_overlay: true,
775 window_id: window::Id::NONE,
776 depth: 0,
777 on_surface_action: self.on_surface_action.clone(),
778 }
779 .overlay(),
780 )
781 }
782}
783
784impl<Message> From<MenuBar<Message>> for Element<'_, Message, crate::Theme, Renderer>
785where
786 Message: Clone + 'static,
787{
788 fn from(value: MenuBar<Message>) -> Self {
789 Self::new(value)
790 }
791}
792
793#[allow(unused_results, clippy::too_many_arguments)]
794fn process_root_events<Message>(
795 menu_roots: &mut [MenuTree<Message>],
796 view_cursor: Cursor,
797 tree: &mut Tree,
798 event: &event::Event,
799 layout: Layout<'_>,
800 renderer: &Renderer,
801 clipboard: &mut dyn Clipboard,
802 shell: &mut Shell<'_, Message>,
803 viewport: &Rectangle,
804) {
805 for ((root, t), lo) in menu_roots
806 .iter_mut()
807 .zip(&mut tree.children)
808 .zip(layout.children())
809 {
810 root.item.update(
812 &mut t.children[root.index],
813 event,
814 lo,
815 view_cursor,
816 renderer,
817 clipboard,
818 shell,
819 viewport,
820 );
821 }
822}