use crate::{ Icon, IconName, IconSize, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader, h_flex, prelude::*, utils::WithRemSize, v_flex,};use gpui::{ Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, px,};use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};use settings::Settings;use std::{rc::Rc, time::Duration};use theme::ThemeSettings; pub enum ContextMenuItem { Separator, Header(SharedString), Label(SharedString), Entry(ContextMenuEntry), CustomEntry { entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, selectable: bool, },} impl ContextMenuItem { pub fn custom_entry( entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { Self::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, } }} pub struct ContextMenuEntry { toggle: Option<(IconPosition, bool)>, label: SharedString, icon: Option<IconName>, icon_position: IconPosition, icon_size: IconSize, icon_color: Option<Color>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, action: Option<Box<dyn Action>>, disabled: bool, documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>,} impl ContextMenuEntry { pub fn new(label: impl Into<SharedString>) -> Self { ContextMenuEntry { toggle: None, label: label.into(), icon: None, icon_position: IconPosition::Start, icon_size: IconSize::Small, icon_color: None, handler: Rc::new(|_, _, _| {}), action: None, disabled: false, documentation_aside: None, } } pub fn toggleable(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn icon(mut self, icon: IconName) -> Self { self.icon = Some(icon); self } pub fn icon_position(mut self, position: IconPosition) -> Self { self.icon_position = position; self } pub fn icon_size(mut self, icon_size: IconSize) -> Self { self.icon_size = icon_size; self } pub fn icon_color(mut self, icon_color: Color) -> Self { self.icon_color = Some(icon_color); self } pub fn toggle(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn action(mut self, action: Box<dyn Action>) -> Self { self.action = Some(action); self } pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self { self.handler = Rc::new(move |_, window, cx| handler(window, cx)); self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn documentation_aside( mut self, element: impl Fn(&mut App) -> AnyElement + 'static, ) -> Self { self.documentation_aside = Some(Rc::new(element)); self }} impl From<ContextMenuEntry> for ContextMenuItem { fn from(entry: ContextMenuEntry) -> Self { ContextMenuItem::Entry(entry) }} pub struct ContextMenu { builder: Option<Rc<dyn Fn(Self, &mut Window, &mut Context<Self>) -> Self>>, items: Vec<ContextMenuItem>, focus_handle: FocusHandle, action_context: Option<FocusHandle>, selected_index: Option<usize>, delayed: bool, clicked: bool, _on_blur_subscription: Subscription, keep_open_on_confirm: bool, documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>,} impl Focusable for ContextMenu { fn focus_handle(&self, _cx: &App) -> FocusHandle { self.focus_handle.clone() }} impl EventEmitter<DismissEvent> for ContextMenu {} impl FluentBuilder for ContextMenu {} impl ContextMenu { pub fn build( window: &mut Window, cx: &mut App, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Entity<Self> { cx.new(|cx| { let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); f( Self { builder: None, items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription, keep_open_on_confirm: false, documentation_aside: None, }, window, cx, ) }) } /// Builds a [`ContextMenu`] that will stay open when making changes instead of closing after each confirmation. /// /// The main difference from [`ContextMenu::build`] is the type of the `builder`, as we need to be able to hold onto /// it to call it again. pub fn build_persistent( window: &mut Window, cx: &mut App, builder: impl Fn(Self, &mut Window, &mut Context<Self>) -> Self + 'static, ) -> Entity<Self> { cx.new(|cx| { let builder = Rc::new(builder); let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription, keep_open_on_confirm: true, documentation_aside: None, }, window, cx, ) }) } /// Rebuilds the menu. /// /// This is used to refresh the menu entries when entries are toggled when the menu is configured with /// `keep_open_on_confirm = true`. /// /// This only works if the [`ContextMenu`] was constructed using [`ContextMenu::build_persistent`]. Otherwise it is /// a no-op. fn rebuild(&mut self, window: &mut Window, cx: &mut Context<Self>) { let Some(builder) = self.builder.clone() else { return; }; // The way we rebuild the menu is a bit of a hack. let focus_handle = cx.focus_handle(); let new_menu = (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle: focus_handle.clone(), action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription: cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ), keep_open_on_confirm: false, documentation_aside: None, }, window, cx, ); self.items = new_menu.items; cx.notify(); } pub fn context(mut self, focus: FocusHandle) -> Self { self.action_context = Some(focus); self } pub fn header(mut self, title: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Header(title.into())); self } pub fn separator(mut self) -> Self { self.items.push(ContextMenuItem::Separator); self } pub fn extend<I: Into<ContextMenuItem>>(mut self, items: impl IntoIterator<Item = I>) -> Self { self.items.extend(items.into_iter().map(Into::into)); self } pub fn item(mut self, item: impl Into<ContextMenuItem>) -> Self { self.items.push(item.into()); self } pub fn entry( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, })); self } pub fn toggleable_entry( mut self, label: impl Into<SharedString>, toggled: bool, position: IconPosition, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: Some((position, toggled)), label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, icon_position: position, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, })); self } pub fn custom_row( mut self, entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, ) -> Self { self.items.push(ContextMenuItem::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(|_, _, _| {}), selectable: false, }); self } pub fn custom_entry( mut self, entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, }); self } pub fn label(mut self, label: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Label(label.into())); self } pub fn action(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |context, window, cx| { if let Some(context) = &context { window.focus(context); } window.dispatch_action(action.boxed_clone(), cx); }), icon: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, disabled: false, documentation_aside: None, })); self } pub fn disabled_action( mut self, label: impl Into<SharedString>, action: Box<dyn Action>, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |context, window, cx| { if let Some(context) = &context { window.focus(context); } window.dispatch_action(action.boxed_clone(), cx); }), icon: None, icon_size: IconSize::Small, icon_position: IconPosition::End, icon_color: None, disabled: true, documentation_aside: None, })); self } pub fn link(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)), icon: Some(IconName::ArrowUpRight), icon_size: IconSize::XSmall, icon_position: IconPosition::End, icon_color: None, disabled: false, documentation_aside: None, })); self } pub fn keep_open_on_confirm(mut self) -> Self { self.keep_open_on_confirm = true; self } pub fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) { let context = self.action_context.as_ref(); if let Some( ContextMenuItem::Entry(ContextMenuEntry { handler, disabled: false, .. }) | ContextMenuItem::CustomEntry { handler, .. }, ) = self.selected_index.and_then(|ix| self.items.get(ix)) { (handler)(context, window, cx) } if self.keep_open_on_confirm { self.rebuild(window, cx); } else { cx.emit(DismissEvent); } } pub fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) { cx.emit(DismissEvent); cx.emit(DismissEvent); } fn select_first(&mut self, _: &SelectFirst, _: &mut Window, cx: &mut Context<Self>) { if let Some(ix) = self.items.iter().position(|item| item.is_selectable()) { self.select_index(ix); } cx.notify(); } pub fn select_last(&mut self) -> Option<usize> { for (ix, item) in self.items.iter().enumerate().rev() { if item.is_selectable() { return self.select_index(ix); } } None } fn handle_select_last(&mut self, _: &SelectLast, _: &mut Window, cx: &mut Context<Self>) { if self.select_last().is_some() { cx.notify(); } } fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) { if let Some(ix) = self.selected_index { let next_index = ix + 1; if self.items.len() <= next_index { self.select_first(&SelectFirst, window, cx); } else { for (ix, item) in self.items.iter().enumerate().skip(next_index) { if item.is_selectable() { self.select_index(ix); cx.notify(); break; } } } } else { self.select_first(&SelectFirst, window, cx); } } pub fn select_previous( &mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>,Lines 1–500 of 883. Load more
use crate::{ Icon, IconName, IconSize, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader, h_flex, prelude::*, utils::WithRemSize, v_flex, }; use gpui::{ Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, px, }; use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious}; use settings::Settings; use std::{rc::Rc, time::Duration}; use theme::ThemeSettings; pub enum ContextMenuItem { Separator, Header(SharedString), Label(SharedString), Entry(ContextMenuEntry), CustomEntry { entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, selectable: bool, }, } impl ContextMenuItem { pub fn custom_entry( entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { Self::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, } } } pub struct ContextMenuEntry { toggle: Option<(IconPosition, bool)>, label: SharedString, icon: Option<IconName>, icon_position: IconPosition, icon_size: IconSize, icon_color: Option<Color>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, action: Option<Box<dyn Action>>, disabled: bool, documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>, } impl ContextMenuEntry { pub fn new(label: impl Into<SharedString>) -> Self { ContextMenuEntry { toggle: None, label: label.into(), icon: None, icon_position: IconPosition::Start, icon_size: IconSize::Small, icon_color: None, handler: Rc::new(|_, _, _| {}), action: None, disabled: false, documentation_aside: None, } } pub fn toggleable(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn icon(mut self, icon: IconName) -> Self { self.icon = Some(icon); self } pub fn icon_position(mut self, position: IconPosition) -> Self { self.icon_position = position; self } pub fn icon_size(mut self, icon_size: IconSize) -> Self { self.icon_size = icon_size; self } pub fn icon_color(mut self, icon_color: Color) -> Self { self.icon_color = Some(icon_color); self } pub fn toggle(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn action(mut self, action: Box<dyn Action>) -> Self { self.action = Some(action); self } pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self { self.handler = Rc::new(move |_, window, cx| handler(window, cx)); self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn documentation_aside( mut self, element: impl Fn(&mut App) -> AnyElement + 'static, ) -> Self { self.documentation_aside = Some(Rc::new(element)); self } } impl From<ContextMenuEntry> for ContextMenuItem { fn from(entry: ContextMenuEntry) -> Self { ContextMenuItem::Entry(entry) } } pub struct ContextMenu { builder: Option<Rc<dyn Fn(Self, &mut Window, &mut Context<Self>) -> Self>>, items: Vec<ContextMenuItem>, focus_handle: FocusHandle, action_context: Option<FocusHandle>, selected_index: Option<usize>, delayed: bool, clicked: bool, _on_blur_subscription: Subscription, keep_open_on_confirm: bool, documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>, } impl Focusable for ContextMenu { fn focus_handle(&self, _cx: &App) -> FocusHandle { self.focus_handle.clone() } } impl EventEmitter<DismissEvent> for ContextMenu {} impl FluentBuilder for ContextMenu {} impl ContextMenu { pub fn build( window: &mut Window, cx: &mut App, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Entity<Self> { cx.new(|cx| { let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); f( Self { builder: None, items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription, keep_open_on_confirm: false, documentation_aside: None, }, window, cx, ) }) } /// Builds a [`ContextMenu`] that will stay open when making changes instead of closing after each confirmation. /// /// The main difference from [`ContextMenu::build`] is the type of the `builder`, as we need to be able to hold onto /// it to call it again. pub fn build_persistent( window: &mut Window, cx: &mut App, builder: impl Fn(Self, &mut Window, &mut Context<Self>) -> Self + 'static, ) -> Entity<Self> { cx.new(|cx| { let builder = Rc::new(builder); let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription, keep_open_on_confirm: true, documentation_aside: None, }, window, cx, ) }) } /// Rebuilds the menu. /// /// This is used to refresh the menu entries when entries are toggled when the menu is configured with /// `keep_open_on_confirm = true`. /// /// This only works if the [`ContextMenu`] was constructed using [`ContextMenu::build_persistent`]. Otherwise it is /// a no-op. fn rebuild(&mut self, window: &mut Window, cx: &mut Context<Self>) { let Some(builder) = self.builder.clone() else { return; }; // The way we rebuild the menu is a bit of a hack. let focus_handle = cx.focus_handle(); let new_menu = (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle: focus_handle.clone(), action_context: None, selected_index: None, delayed: false, clicked: false, _on_blur_subscription: cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ), keep_open_on_confirm: false, documentation_aside: None, }, window, cx, ); self.items = new_menu.items; cx.notify(); } pub fn context(mut self, focus: FocusHandle) -> Self { self.action_context = Some(focus); self } pub fn header(mut self, title: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Header(title.into())); self } pub fn separator(mut self) -> Self { self.items.push(ContextMenuItem::Separator); self } pub fn extend<I: Into<ContextMenuItem>>(mut self, items: impl IntoIterator<Item = I>) -> Self { self.items.extend(items.into_iter().map(Into::into)); self } pub fn item(mut self, item: impl Into<ContextMenuItem>) -> Self { self.items.push(item.into()); self } pub fn entry( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, })); self } pub fn toggleable_entry( mut self, label: impl Into<SharedString>, toggled: bool, position: IconPosition, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: Some((position, toggled)), label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, icon_position: position, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, })); self } pub fn custom_row( mut self, entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, ) -> Self { self.items.push(ContextMenuItem::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(|_, _, _| {}), selectable: false, }); self } pub fn custom_entry( mut self, entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, }); self } pub fn label(mut self, label: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Label(label.into())); self } pub fn action(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |context, window, cx| { if let Some(context) = &context { window.focus(context); } window.dispatch_action(action.boxed_clone(), cx); }), icon: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, disabled: false, documentation_aside: None, })); self } pub fn disabled_action( mut self, label: impl Into<SharedString>, action: Box<dyn Action>, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |context, window, cx| { if let Some(context) = &context { window.focus(context); } window.dispatch_action(action.boxed_clone(), cx); }), icon: None, icon_size: IconSize::Small, icon_position: IconPosition::End, icon_color: None, disabled: true, documentation_aside: None, })); self } pub fn link(mut self, label: impl Into<SharedString>, action: Box<dyn Action>) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |_, window, cx| window.dispatch_action(action.boxed_clone(), cx)), icon: Some(IconName::ArrowUpRight), icon_size: IconSize::XSmall, icon_position: IconPosition::End, icon_color: None, disabled: false, documentation_aside: None, })); self } pub fn keep_open_on_confirm(mut self) -> Self { self.keep_open_on_confirm = true; self } pub fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) { let context = self.action_context.as_ref(); if let Some( ContextMenuItem::Entry(ContextMenuEntry { handler, disabled: false, .. }) | ContextMenuItem::CustomEntry { handler, .. }, ) = self.selected_index.and_then(|ix| self.items.get(ix)) { (handler)(context, window, cx) } if self.keep_open_on_confirm { self.rebuild(window, cx); } else { cx.emit(DismissEvent); } } pub fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) { cx.emit(DismissEvent); cx.emit(DismissEvent); } fn select_first(&mut self, _: &SelectFirst, _: &mut Window, cx: &mut Context<Self>) { if let Some(ix) = self.items.iter().position(|item| item.is_selectable()) { self.select_index(ix); } cx.notify(); } pub fn select_last(&mut self) -> Option<usize> { for (ix, item) in self.items.iter().enumerate().rev() { if item.is_selectable() { return self.select_index(ix); } } None } fn handle_select_last(&mut self, _: &SelectLast, _: &mut Window, cx: &mut Context<Self>) { if self.select_last().is_some() { cx.notify(); } } fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) { if let Some(ix) = self.selected_index { let next_index = ix + 1; if self.items.len() <= next_index { self.select_first(&SelectFirst, window, cx); } else { for (ix, item) in self.items.iter().enumerate().skip(next_index) { if item.is_selectable() { self.select_index(ix); cx.notify(); break; } } } } else { self.select_first(&SelectFirst, window, cx); } } pub fn select_previous( &mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>,