context_menu.rs

  1#![allow(missing_docs)]
  2use crate::{
  3    h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label,
  4    List, ListItem, ListSeparator, ListSubHeader,
  5};
  6use gpui::{
  7    px, Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle,
  8    Focusable, IntoElement, Render, Subscription,
  9};
 10use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
 11use settings::Settings;
 12use std::{rc::Rc, time::Duration};
 13use theme::ThemeSettings;
 14
 15pub enum ContextMenuItem {
 16    Separator,
 17    Header(SharedString),
 18    Label(SharedString),
 19    Entry(ContextMenuEntry),
 20    CustomEntry {
 21        entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>,
 22        handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
 23        selectable: bool,
 24    },
 25}
 26
 27impl ContextMenuItem {
 28    pub fn custom_entry(
 29        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
 30        handler: impl Fn(&mut Window, &mut App) + 'static,
 31    ) -> Self {
 32        Self::CustomEntry {
 33            entry_render: Box::new(entry_render),
 34            handler: Rc::new(move |_, window, cx| handler(window, cx)),
 35            selectable: true,
 36        }
 37    }
 38}
 39
 40pub struct ContextMenuEntry {
 41    toggle: Option<(IconPosition, bool)>,
 42    label: SharedString,
 43    icon: Option<IconName>,
 44    icon_position: IconPosition,
 45    icon_size: IconSize,
 46    icon_color: Option<Color>,
 47    handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
 48    action: Option<Box<dyn Action>>,
 49    disabled: bool,
 50    documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>,
 51}
 52
 53impl ContextMenuEntry {
 54    pub fn new(label: impl Into<SharedString>) -> Self {
 55        ContextMenuEntry {
 56            toggle: None,
 57            label: label.into(),
 58            icon: None,
 59            icon_position: IconPosition::Start,
 60            icon_size: IconSize::Small,
 61            icon_color: None,
 62            handler: Rc::new(|_, _, _| {}),
 63            action: None,
 64            disabled: false,
 65            documentation_aside: None,
 66        }
 67    }
 68
 69    pub fn toggleable(mut self, toggle_position: IconPosition, toggled: bool) -> Self {
 70        self.toggle = Some((toggle_position, toggled));
 71        self
 72    }
 73
 74    pub fn icon(mut self, icon: IconName) -> Self {
 75        self.icon = Some(icon);
 76        self
 77    }
 78
 79    pub fn icon_position(mut self, position: IconPosition) -> Self {
 80        self.icon_position = position;
 81        self
 82    }
 83
 84    pub fn icon_size(mut self, icon_size: IconSize) -> Self {
 85        self.icon_size = icon_size;
 86        self
 87    }
 88
 89    pub fn icon_color(mut self, icon_color: Color) -> Self {
 90        self.icon_color = Some(icon_color);
 91        self
 92    }
 93
 94    pub fn toggle(mut self, toggle_position: IconPosition, toggled: bool) -> Self {
 95        self.toggle = Some((toggle_position, toggled));
 96        self
 97    }
 98
 99    pub fn action(mut self, action: Option<Box<dyn Action>>) -> Self {
100        self.action = action;
101        self
102    }
103
104    pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
105        self.handler = Rc::new(move |_, window, cx| handler(window, cx));
106        self
107    }
108
109    pub fn disabled(mut self, disabled: bool) -> Self {
110        self.disabled = disabled;
111        self
112    }
113
114    pub fn documentation_aside(
115        mut self,
116        element: impl Fn(&mut App) -> AnyElement + 'static,
117    ) -> Self {
118        self.documentation_aside = Some(Rc::new(element));
119        self
120    }
121}
122
123impl From<ContextMenuEntry> for ContextMenuItem {
124    fn from(entry: ContextMenuEntry) -> Self {
125        ContextMenuItem::Entry(entry)
126    }
127}
128
129pub struct ContextMenu {
130    items: Vec<ContextMenuItem>,
131    focus_handle: FocusHandle,
132    action_context: Option<FocusHandle>,
133    selected_index: Option<usize>,
134    delayed: bool,
135    clicked: bool,
136    _on_blur_subscription: Subscription,
137    keep_open_on_confirm: bool,
138    documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>,
139}
140
141impl Focusable for ContextMenu {
142    fn focus_handle(&self, _cx: &App) -> FocusHandle {
143        self.focus_handle.clone()
144    }
145}
146
147impl EventEmitter<DismissEvent> for ContextMenu {}
148
149impl FluentBuilder for ContextMenu {}
150
151impl ContextMenu {
152    pub fn build(
153        window: &mut Window,
154        cx: &mut App,
155        f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self,
156    ) -> Entity<Self> {
157        cx.new(|cx| {
158            let focus_handle = cx.focus_handle();
159            let _on_blur_subscription = cx.on_blur(
160                &focus_handle,
161                window,
162                |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx),
163            );
164            window.refresh();
165            f(
166                Self {
167                    items: Default::default(),
168                    focus_handle,
169                    action_context: None,
170                    selected_index: None,
171                    delayed: false,
172                    clicked: false,
173                    _on_blur_subscription,
174                    keep_open_on_confirm: false,
175                    documentation_aside: None,
176                },
177                window,
178                cx,
179            )
180        })
181    }
182
183    pub fn context(mut self, focus: FocusHandle) -> Self {
184        self.action_context = Some(focus);
185        self
186    }
187
188    pub fn header(mut self, title: impl Into<SharedString>) -> Self {
189        self.items.push(ContextMenuItem::Header(title.into()));
190        self
191    }
192
193    pub fn separator(mut self) -> Self {
194        self.items.push(ContextMenuItem::Separator);
195        self
196    }
197
198    pub fn extend<I: Into<ContextMenuItem>>(mut self, items: impl IntoIterator<Item = I>) -> Self {
199        self.items.extend(items.into_iter().map(Into::into));
200        self
201    }
202
203    pub fn item(mut self, item: impl Into<ContextMenuItem>) -> Self {
204        self.items.push(item.into());
205        self
206    }
207
208    pub fn entry(
209        mut self,
210        label: impl Into<SharedString>,
211        action: Option<Box<dyn Action>>,
212        handler: impl Fn(&mut Window, &mut App) + 'static,
213    ) -> Self {
214        self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
215            toggle: None,
216            label: label.into(),
217            handler: Rc::new(move |_, window, cx| handler(window, cx)),
218            icon: None,
219            icon_position: IconPosition::End,
220            icon_size: IconSize::Small,
221            icon_color: None,
222            action,
223            disabled: false,
224            documentation_aside: None,
225        }));
226        self
227    }
228
229    pub fn toggleable_entry(
230        mut self,
231        label: impl Into<SharedString>,
232        toggled: bool,
233        position: IconPosition,
234        action: Option<Box<dyn Action>>,
235        handler: impl Fn(&mut Window, &mut App) + 'static,
236    ) -> Self {
237        self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
238            toggle: Some((position, toggled)),
239            label: label.into(),
240            handler: Rc::new(move |_, window, cx| handler(window, cx)),
241            icon: None,
242            icon_position: position,
243            icon_size: IconSize::Small,
244            icon_color: None,
245            action,
246            disabled: false,
247            documentation_aside: None,
248        }));
249        self
250    }
251
252    pub fn custom_row(
253        mut self,
254        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
255    ) -> Self {
256        self.items.push(ContextMenuItem::CustomEntry {
257            entry_render: Box::new(entry_render),
258            handler: Rc::new(|_, _, _| {}),
259            selectable: false,
260        });
261        self
262    }
263
264    pub fn custom_entry(
265        mut self,
266        entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
267        handler: impl Fn(&mut Window, &mut App) + 'static,
268    ) -> Self {
269        self.items.push(ContextMenuItem::CustomEntry {
270            entry_render: Box::new(entry_render),
271            handler: Rc::new(move |_, window, cx| handler(window, cx)),
272            selectable: true,
273        });
274        self
275    }
276
277    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
278        self.items.push(ContextMenuItem::Label(label.into()));
279        self
280    }
281
282    pub fn action(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
283        self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
284            toggle: None,
285            label: label.into(),
286            action: Some(action.boxed_clone()),
287            handler: Rc::new(move |context, window, cx| {
288                if let Some(context) = &context {
289                    window.focus(context);
290                }
291                window.dispatch_action(action.boxed_clone(), cx);
292            }),
293            icon: None,
294            icon_position: IconPosition::End,
295            icon_size: IconSize::Small,
296            icon_color: None,
297            disabled: false,
298            documentation_aside: None,
299        }));
300        self
301    }
302
303    pub fn disabled_action(
304        mut self,
305        label: impl Into<SharedString>,
306        action: Box<dyn Action>,
307    ) -> Self {
308        self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
309            toggle: None,
310            label: label.into(),
311            action: Some(action.boxed_clone()),
312            handler: Rc::new(move |context, window, cx| {
313                if let Some(context) = &context {
314                    window.focus(context);
315                }
316                window.dispatch_action(action.boxed_clone(), cx);
317            }),
318            icon: None,
319            icon_size: IconSize::Small,
320            icon_position: IconPosition::End,
321            icon_color: None,
322            disabled: true,
323            documentation_aside: None,
324        }));
325        self
326    }
327
328    pub fn link(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
329        self.items.push(ContextMenuItem::Entry(ContextMenuEntry {
330            toggle: None,
331            label: label.into(),
332            action: Some(action.boxed_clone()),
333            handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)),
334            icon: Some(IconName::ArrowUpRight),
335            icon_size: IconSize::XSmall,
336            icon_position: IconPosition::End,
337            icon_color: None,
338            disabled: false,
339            documentation_aside: None,
340        }));
341        self
342    }
343
344    pub fn keep_open_on_confirm(mut self) -> Self {
345        self.keep_open_on_confirm = true;
346        self
347    }
348
349    pub fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
350        let context = self.action_context.as_ref();
351        if let Some(
352            ContextMenuItem::Entry(ContextMenuEntry {
353                handler,
354                disabled: false,
355                ..
356            })
357            | ContextMenuItem::CustomEntry { handler, .. },
358        ) = self.selected_index.and_then(|ix| self.items.get(ix))
359        {
360            (handler)(context, window, cx)
361        }
362
363        if !self.keep_open_on_confirm {
364            cx.emit(DismissEvent);
365        }
366    }
367
368    pub fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
369        cx.emit(DismissEvent);
370        cx.emit(DismissEvent);
371    }
372
373    fn select_first(&mut self, _: &SelectFirst, _: &mut Window, cx: &mut Context<Self>) {
374        if let Some(ix) = self.items.iter().position(|item| item.is_selectable()) {
375            self.select_index(ix);
376        }
377        cx.notify();
378    }
379
380    pub fn select_last(&mut self) -> Option<usize> {
381        for (ix, item) in self.items.iter().enumerate().rev() {
382            if item.is_selectable() {
383                return self.select_index(ix);
384            }
385        }
386        None
387    }
388
389    fn handle_select_last(&mut self, _: &SelectLast, _: &mut Window, cx: &mut Context<Self>) {
390        if self.select_last().is_some() {
391            cx.notify();
392        }
393    }
394
395    fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
396        if let Some(ix) = self.selected_index {
397            let next_index = ix + 1;
398            if self.items.len() <= next_index {
399                self.select_first(&SelectFirst, window, cx);
400            } else {
401                for (ix, item) in self.items.iter().enumerate().skip(next_index) {
402                    if item.is_selectable() {
403                        self.select_index(ix);
404                        cx.notify();
405                        break;
406                    }
407                }
408            }
409        } else {
410            self.select_first(&SelectFirst, window, cx);
411        }
412    }
413
414    pub fn select_prev(&mut self, _: &SelectPrev, window: &mut Window, cx: &mut Context<Self>) {
415        if let Some(ix) = self.selected_index {
416            if ix == 0 {
417                self.handle_select_last(&SelectLast, window, cx);
418            } else {
419                for (ix, item) in self.items.iter().enumerate().take(ix).rev() {
420                    if item.is_selectable() {
421                        self.select_index(ix);
422                        cx.notify();
423                        break;
424                    }
425                }
426            }
427        } else {
428            self.handle_select_last(&SelectLast, window, cx);
429        }
430    }
431
432    fn select_index(&mut self, ix: usize) -> Option<usize> {
433        self.documentation_aside = None;
434        let item = self.items.get(ix)?;
435        if item.is_selectable() {
436            self.selected_index = Some(ix);
437            if let ContextMenuItem::Entry(entry) = item {
438                if let Some(callback) = &entry.documentation_aside {
439                    self.documentation_aside = Some((ix, callback.clone()));
440                }
441            }
442        }
443        Some(ix)
444    }
445
446    pub fn on_action_dispatch(
447        &mut self,
448        dispatched: &dyn Action,
449        window: &mut Window,
450        cx: &mut Context<Self>,
451    ) {
452        if self.clicked {
453            cx.propagate();
454            return;
455        }
456
457        if let Some(ix) = self.items.iter().position(|item| {
458            if let ContextMenuItem::Entry(ContextMenuEntry {
459                action: Some(action),
460                disabled: false,
461                ..
462            }) = item
463            {
464                action.partial_eq(dispatched)
465            } else {
466                false
467            }
468        }) {
469            self.select_index(ix);
470            self.delayed = true;
471            cx.notify();
472            let action = dispatched.boxed_clone();
473            cx.spawn_in(window, |this, mut cx| async move {
474                cx.background_executor()
475                    .timer(Duration::from_millis(50))
476                    .await;
477                cx.update(|window, cx| {
478                    this.update(cx, |this, cx| {
479                        this.cancel(&menu::Cancel, window, cx);
480                        window.dispatch_action(action, cx);
481                    })
482                })
483            })
484            .detach_and_log_err(cx);
485        } else {
486            cx.propagate()
487        }
488    }
489
490    pub fn on_blur_subscription(mut self, new_subscription: Subscription) -> Self {
491        self._on_blur_subscription = new_subscription;
492        self
493    }
494}
495
496impl ContextMenuItem {
497    fn is_selectable(&self) -> bool {
498        match self {
499            ContextMenuItem::Header(_)
500            | ContextMenuItem::Separator
501            | ContextMenuItem::Label { .. } => false,
502            ContextMenuItem::Entry(ContextMenuEntry { disabled, .. }) => !disabled,
503            ContextMenuItem::CustomEntry { selectable, .. } => *selectable,
504        }
505    }
506}
507
508impl Render for ContextMenu {
509    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
510        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
511
512        let aside = self
513            .documentation_aside
514            .as_ref()
515            .map(|(_, callback)| callback.clone());
516
517        h_flex()
518            .w_full()
519            .items_start()
520            .gap_1()
521            .when_some(aside, |this, aside| {
522                this.child(
523                    WithRemSize::new(ui_font_size)
524                        .occlude()
525                        .elevation_2(cx)
526                        .p_2()
527                        .max_w_96()
528                        .child(aside(cx)),
529                )
530            })
531            .child(
532                WithRemSize::new(ui_font_size)
533                    .occlude()
534                    .elevation_2(cx)
535                    .flex()
536                    .flex_row()
537                    .child(
538                        v_flex()
539                            .id("context-menu")
540                            .min_w(px(200.))
541                            .max_h(vh(0.75, window))
542                            .flex_1()
543                            .overflow_y_scroll()
544                            .track_focus(&self.focus_handle(cx))
545                            .on_mouse_down_out(cx.listener(|this, _, window, cx| {
546                                this.cancel(&menu::Cancel, window, cx)
547                            }))
548                            .key_context("menu")
549                            .on_action(cx.listener(ContextMenu::select_first))
550                            .on_action(cx.listener(ContextMenu::handle_select_last))
551                            .on_action(cx.listener(ContextMenu::select_next))
552                            .on_action(cx.listener(ContextMenu::select_prev))
553                            .on_action(cx.listener(ContextMenu::confirm))
554                            .on_action(cx.listener(ContextMenu::cancel))
555                            .when(!self.delayed, |mut el| {
556                                for item in self.items.iter() {
557                                    if let ContextMenuItem::Entry(ContextMenuEntry {
558                                        action: Some(action),
559                                        disabled: false,
560                                        ..
561                                    }) = item
562                                    {
563                                        el = el.on_boxed_action(
564                                            &**action,
565                                            cx.listener(ContextMenu::on_action_dispatch),
566                                        );
567                                    }
568                                }
569                                el
570                            })
571                            .child(List::new().children(self.items.iter_mut().enumerate().map(
572                                |(ix, item)| {
573                                    match item {
574                                        ContextMenuItem::Separator => {
575                                            ListSeparator.into_any_element()
576                                        }
577                                        ContextMenuItem::Header(header) => {
578                                            ListSubHeader::new(header.clone())
579                                                .inset(true)
580                                                .into_any_element()
581                                        }
582                                        ContextMenuItem::Label(label) => ListItem::new(ix)
583                                            .inset(true)
584                                            .disabled(true)
585                                            .child(Label::new(label.clone()))
586                                            .into_any_element(),
587                                        ContextMenuItem::Entry(ContextMenuEntry {
588                                            toggle,
589                                            label,
590                                            handler,
591                                            icon,
592                                            icon_position,
593                                            icon_size,
594                                            icon_color,
595                                            action,
596                                            disabled,
597                                            documentation_aside,
598                                        }) => {
599                                            let handler = handler.clone();
600                                            let menu = cx.entity().downgrade();
601
602                                            let icon_color = if *disabled {
603                                                Color::Muted
604                                            } else if toggle.is_some() {
605                                                icon_color.unwrap_or(Color::Accent)
606                                            } else {
607                                                icon_color.unwrap_or(Color::Default)
608                                            };
609
610                                            let label_color = if *disabled {
611                                                Color::Muted
612                                            } else {
613                                                Color::Default
614                                            };
615
616                                            let label_element = if let Some(icon_name) = icon {
617                                                h_flex()
618                                                    .gap_1p5()
619                                                    .when(
620                                                        *icon_position == IconPosition::Start && toggle.is_none(),
621                                                        |flex| {
622                                                            flex.child(
623                                                                Icon::new(*icon_name)
624                                                                    .size(*icon_size)
625                                                                    .color(icon_color),
626                                                            )
627                                                        },
628                                                    )
629                                                    .child(
630                                                        Label::new(label.clone())
631                                                            .color(label_color),
632                                                    )
633                                                    .when(
634                                                        *icon_position == IconPosition::End,
635                                                        |flex| {
636                                                            flex.child(
637                                                                Icon::new(*icon_name)
638                                                                    .size(*icon_size)
639                                                                    .color(icon_color),
640                                                            )
641                                                        },
642                                                    )
643                                                    .into_any_element()
644                                            } else {
645                                                Label::new(label.clone())
646                                                    .color(label_color)
647                                                    .into_any_element()
648                                            };
649
650                                            let documentation_aside_callback =
651                                                documentation_aside.clone();
652
653                                            div()
654                                                .id(("context-menu-child", ix))
655                                                .when_some(
656                                                    documentation_aside_callback,
657                                                    |this, documentation_aside_callback| {
658                                                        this.occlude().on_hover(cx.listener(
659                                                            move |menu, hovered, _, cx| {
660                                                                if *hovered {
661                                                                    menu.documentation_aside = Some((ix, documentation_aside_callback.clone()));
662                                                                    cx.notify();
663                                                                } else if matches!(menu.documentation_aside, Some((id, _)) if id == ix) {
664                                                                    menu.documentation_aside = None;
665                                                                    cx.notify();
666                                                                }
667                                                            },
668                                                        ))
669                                                    },
670                                                )
671                                                .child(
672                                                    ListItem::new(ix)
673                                                        .inset(true)
674                                                        .disabled(*disabled)
675                                                        .toggle_state(
676                                                            Some(ix) == self.selected_index,
677                                                        )
678                                                        .when_some(
679                                                            *toggle,
680                                                            |list_item, (position, toggled)| {
681                                                                let contents =
682                                                                    div().flex_none().child(
683                                                                        Icon::new(icon.unwrap_or(IconName::Check))
684                                                                            .color(icon_color)
685                                                                            .size(*icon_size)
686                                                                    )
687                                                                    .when(!toggled, |contents|
688                                                                        contents.invisible()
689                                                                    );
690
691                                                                match position {
692                                                                    IconPosition::Start => {
693                                                                        list_item
694                                                                            .start_slot(contents)
695                                                                    }
696                                                                    IconPosition::End => {
697                                                                        list_item.end_slot(contents)
698                                                                    }
699                                                                }
700                                                            },
701                                                        )
702                                                        .child(
703                                                            h_flex()
704                                                                .w_full()
705                                                                .justify_between()
706                                                                .child(label_element)
707                                                                .debug_selector(|| {
708                                                                    format!("MENU_ITEM-{}", label)
709                                                                })
710                                                                .children(
711                                                                    action.as_ref().and_then(
712                                                                        |action| {
713                                                                            self.action_context
714                                                                    .as_ref()
715                                                                    .map(|focus| {
716                                                                        KeyBinding::for_action_in(
717                                                                            &**action, focus,
718                                                                            window,
719                                                                            cx
720                                                                        )
721                                                                    })
722                                                                    .unwrap_or_else(|| {
723                                                                        KeyBinding::for_action(
724                                                                            &**action, window, cx
725                                                                        )
726                                                                    })
727                                                                    .map(|binding| {
728                                                                        div().ml_4().child(binding)
729                                                                    })
730                                                                        },
731                                                                    ),
732                                                                ),
733                                                        )
734                                                        .on_click({
735                                                            let context =
736                                                                self.action_context.clone();
737                                                            move |_, window, cx| {
738                                                                handler(
739                                                                    context.as_ref(),
740                                                                    window,
741                                                                    cx,
742                                                                );
743                                                                menu.update(cx, |menu, cx| {
744                                                                    menu.clicked = true;
745                                                                    cx.emit(DismissEvent);
746                                                                })
747                                                                .ok();
748                                                            }
749                                                        }),
750                                                )
751                                                .into_any_element()
752                                        }
753                                        ContextMenuItem::CustomEntry {
754                                            entry_render,
755                                            handler,
756                                            selectable,
757                                        } => {
758                                            let handler = handler.clone();
759                                            let menu = cx.entity().downgrade();
760                                            let selectable = *selectable;
761                                            ListItem::new(ix)
762                                                .inset(true)
763                                                .toggle_state(if selectable {
764                                                    Some(ix) == self.selected_index
765                                                } else {
766                                                    false
767                                                })
768                                                .selectable(selectable)
769                                                .when(selectable, |item| {
770                                                    item.on_click({
771                                                        let context = self.action_context.clone();
772                                                        move |_, window, cx| {
773                                                            handler(context.as_ref(), window, cx);
774                                                            menu.update(cx, |menu, cx| {
775                                                                menu.clicked = true;
776                                                                cx.emit(DismissEvent);
777                                                            })
778                                                            .ok();
779                                                        }
780                                                    })
781                                                })
782                                                .child(entry_render(window, cx))
783                                                .into_any_element()
784                                        }
785                                    }
786                                },
787                            )))
788                    ),
789            )
790    }
791}