use anyhow::Result;use gpui::Action;use gpui::{ AnyElement, App, Bounds, ClickEvent, Context, DismissEvent, EventEmitter, FocusHandle, Focusable, ListSizingBehavior, ListState, MouseButton, MouseUpEvent, Pixels, ScrollStrategy, Task, UniformListScrollHandle, Window, actions, canvas, div, list, prelude::*, uniform_list,};use head::Head;use schemars::JsonSchema;use serde::Deserialize;use std::{ cell::Cell, cell::RefCell, collections::HashMap, ops::Range, rc::Rc, sync::Arc, time::Duration,};use ui::{ Checkbox, ContextMenu, Divider, DocumentationAside, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*,};use ui_input::ErasedEditorEvent;use util::ResultExt;use workspace::ModalView;use zed_actions::editor::{MoveDown, MoveUp}; mod footer;mod head;pub mod highlighted_match_with_paths;pub mod parts;mod persistence;pub mod popover_menu;mod preview;mod render;mod shape; use crate::shape::RelativeHeight;use crate::shape::RelativeWidth;pub use footer::PickerAction;pub use language::{HighlightedText, HighlightedTextBuilder};pub use preview::Layout as PreviewLayout;pub use preview::MatchLocation;pub use preview::Preview;pub use preview::PreviewBackend;pub use preview::PreviewSource;pub use preview::Update as PreviewUpdate;pub use ui_input::ErasedEditor; pub const DEFAULT_MODAL_WIDTH: Rems = Rems(34.0);pub const DEFAULT_MODAL_MAX_HEIGHT: Rems = Rems(24.0); enum ElementContainer { List(ListState), UniformList(UniformListScrollHandle),} pub enum Direction { Up, Down,} #[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum ScrollBehavior { RevealSelected, PreserveOffset,} actions!( picker, [ /// Confirms the selected completion in the picker. ConfirmCompletion, /// Toggles the preview between hidden and visible. TogglePreview, /// Shows the preview to the right of the results. SetPreviewRight, /// Shows the preview below the results. SetPreviewBelow, /// Hides the preview. SetPreviewHidden, /// Opens the footer's actions menu. ToggleActionsMenu, /// Toggles multi-select mode, in which clicking items adds them to /// the selection instead of opening them ToggleMultiSelect, /// Toggles the current item in the multi-selection and advances to /// the next item, starting multi-select mode if it isn't already /// active MultiSelectNext, ]); /// ConfirmInput is an alternative editor action which - instead of selecting active picker entry - treats pickers editor input literally,/// performing some kind of action on it.#[derive(Clone, PartialEq, Deserialize, JsonSchema, Default, Action)]#[action(namespace = picker)]#[serde(deny_unknown_fields)]pub struct ConfirmInput { pub secondary: bool,} struct PendingUpdateMatches { delegate_update_matches: Option<Task<()>>, _task: Task<Result<()>>,} #[derive(Clone, Copy)]enum Presentation { /// A self-contained modal: draws its own elevated background and dismisses /// when it loses focus. May optionally be resized (persisting its size); /// resizing only makes sense for modals. Modal { resizable: bool }, /// A popover attached to a menu/trigger: draws its own elevated background /// and dismisses when it loses focus, but is never resizable. Popover, /// Embedded inside a larger container (e.g. another modal) that provides its /// own chrome and handles dismissal. Embedded,} /// The default size for a given preview layout. With the preview hidden the/// picker uses its standard size; showing a preview expands it to the larger/// "telescope" size so the results pane isn't cramped beside the preview.fn default_shape_for_layout(hidden: shape::Centered, layout: preview::Layout) -> shape::Centered { match layout { preview::Layout::Hidden => hidden, preview::Layout::Right | preview::Layout::Below => shape::Centered::default(), }} pub struct Picker<D: PickerDelegate> { pub delegate: D, element_container: ElementContainer, head: Head, preview: Option<Preview>, pending_update_matches: Option<PendingUpdateMatches>, confirm_on_update: Option<bool>, select_instead_of_open: bool, shape: shape::Shape, /// The size the picker opens at (and resets to). Defaults depend on whether /// the picker has a preview; see [`Picker::initial_width`] / [`Picker::max_height`]. default_shape: shape::Centered, size_bounds: shape::SizeBounds, /// An external control to display a scrollbar in the `Picker`. show_scrollbar: bool, /// How the picker is presented, which controls its chrome and behavior. presentation: Presentation, /// Bounds tracking for the picker container (for aside positioning) picker_bounds: Rc<Cell<Option<Bounds<Pixels>>>>, /// Bounds tracking for items (for aside positioning) - maps item index to bounds item_bounds: Rc<RefCell<HashMap<usize, Bounds<Pixels>>>>, shape_loaded_from_persistence: bool, /// Handle for the default footer's Actions popover menu. Used to keep the /// picker open while that menu has focus. actions_menu_handle: PopoverMenuHandle<ContextMenu>, reopenable: bool,} #[derive(Debug, Default, Clone, Copy, PartialEq)]pub enum PickerEditorPosition { #[default] /// Render the editor at the start of the picker. Usually the top Start, /// Render the editor at the end of the picker. Usually the bottom End,} pub trait PickerDelegate: Sized + 'static { type ListItem: IntoElement; /// Name of the picker, this is the key for serialization. We could use the /// typename of the delegate but then a rename would break persistence. fn name() -> &'static str; fn match_count(&self) -> usize; fn selected_index(&self) -> usize; fn separators_after_indices(&self) -> Vec<usize> { Vec::new() } fn set_selected_index( &mut self, ix: usize, window: &mut Window, cx: &mut Context<Picker<Self>>, ); /// Called before the picker handles `SelectPrevious` or `SelectNext`. Return `Some(query)` to /// set a new query and prevent the default selection behavior. fn select_history( &mut self, _direction: Direction, _query: &str, _window: &mut Window, _cx: &mut App, ) -> Option<String> { None } fn can_select( &self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> bool { true } fn select_on_hover(&self) -> bool { true } // Allows binding some optional effect to when the selection changes. fn selected_index_changed( &self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Box<dyn Fn(&mut Window, &mut App) + 'static>> { None } fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str>; fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option<SharedString> { Some("No matches".into()) } fn update_matches( &mut self, query: String, window: &mut Window, cx: &mut Context<Picker<Self>>, ) -> Task<()>; // Delegates that support this method (e.g. the CommandPalette) can chose to block on any background // work for up to `duration` to try and get a result synchronously. // This avoids a flash of an empty command-palette on cmd-shift-p, and lets workspace::SendKeystrokes // mostly work when dismissing a palette. fn finalize_update_matches( &mut self, _query: String, _duration: Duration, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> bool { false } /// Override if you want to have <enter> update the query instead of confirming. fn confirm_update_query( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>); /// Whether this delegate supports selecting multiple items at once. When /// `true`, the delegate owns the multi-selection state; the picker only /// drives the generic UX (toggle action, indicator, click routing). fn supports_multi_select(&self) -> bool { false } /// Whether the item at `ix` is part of the current multi-selection. fn is_item_selected(&self, _ix: usize) -> bool { false } /// Toggle whether the item at `ix` is part of the multi-selection. Items /// that cannot participate in multi-selection (e.g. non-file entries) /// should leave the selection unchanged. fn toggle_item_selected( &mut self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) { } /// Number of items currently in the multi-selection. fn selected_item_count(&self) -> usize { 0 } /// Clear the multi-selection. fn clear_selection(&mut self, _cx: &mut Context<Picker<Self>>) {} /// Open every item in the multi-selection. Called on confirm when the /// selection is non-empty; implementations should clear the selection. fn confirm_multi( &mut self, _secondary: bool, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) { } /// Instead of interacting with currently selected entry, treats editor input literally, /// performing some kind of action on it. fn confirm_input( &mut self, _secondary: bool, _window: &mut Window, _: &mut Context<Picker<Self>>, ) { } fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>); fn should_dismiss(&self) -> bool { true } fn confirm_completion( &mut self, _query: String, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<String> { None } /// Called when `SelectChild` fires (e.g. shift-right-arrow). Return `Some(query)` /// to step into the currently selected item (e.g. a directory); the picker /// will set the query and refresh matches. fn select_child( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } /// Called when `SelectParent` fires (e.g. shift-left-arrow). Return `Some(query)` /// to step back to the parent; the picker will set the query and refresh /// matches. fn select_parent( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } fn editor_position(&self) -> PickerEditorPosition { PickerEditorPosition::default() } /// Prevent closing the modal on clicking in a popover menu that portrudes out /// This is already set by the Actions menu from the picker, this is here to /// support extra menus added by the delegate. fn has_another_open_menu(&self, _window: &Window, _cx: &App) -> bool { false } /// An optional control rendered at the trailing edge of the search bar, e.g. /// a filter toggle. Returning `Some` is the easy way to add such a control; /// for full control over the search bar, override [`Self::render_editor`]. fn searchbar_trailer( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Overrides the search bar entirely. Most delegates should return `None` /// to get the picker-rendered default (which includes /// [`Self::searchbar_trailer`] and the multi-select toggle); override for /// full control over the search bar. fn render_editor( &self, _editor: &Arc<dyn ErasedEditor>, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Div> { None } fn try_get_preview_data_for_match(&self, _cx: &App) -> Option<PreviewUpdate> { None } /// Called on the delegate when opening a preview to the side. Delegates can /// then change how much space they use for rendering the match fn preview_layout_changed(&mut self, _layout_is_horizontal: bool) {} fn render_match( &self, ix: usize, selected: bool, window: &mut Window, cx: &mut Context<Picker<Self>>, ) -> Option<Self::ListItem>; fn render_match_with_checkbox( &self, _ix: usize, _selected: bool, _checkbox: AnyElement, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Self::ListItem> { None } fn render_header( &self, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Overrides the picker's footer. /// /// Note there normally isn't a footer unless this is set or the picker has /// a preview. If the picker has a preview add actions to it using picker_actions. fn render_footer( &self, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Make this non-empty to have an `Actions` menu show up in the footer fn actions_menu( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Vec<footer::PickerAction> { Vec::new() } fn documentation_aside( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<DocumentationAside> { None } /// Returns the index of the item whose documentation aside should be shown. /// This is used to position the aside relative to that item. /// Typically this is the hovered item, not necessarily the selected item. fn documentation_aside_index(&self) -> Option<usize> { None }} impl<D: PickerDelegate> Focusable for Picker<D> { fn focus_handle(&self, cx: &App) -> FocusHandle { match &self.head { Head::Editor(editor) => editor.focus_handle(cx), Head::Empty(head) => head.focus_handle(cx), } }} #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]enum ContainerKind { List, UniformList,} impl<D: PickerDelegate> Picker<D> { /// A picker, which displays its matches using `gpui::uniform_list`, all matches should have the same height. /// The picker allows the user to perform search items by text. /// If `PickerDelegate::render_match` can return items with different heights, use `Picker::list`. pub fn uniform_list(delegate: D, window: &mut Window, cx: &mut Context<Self>) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx), Self::on_input_editor_event, window, cx, ); Self::new(delegate, ContainerKind::UniformList, head, None, window, cx) } /// A picker similar to [`uniform_list()`](Self::uniform_list) however this picker has a /// preview window where it shows extra information. pub fn uniform_list_with_preview( delegate: D, preview: Arc<dyn PreviewBackend>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx), Self::on_input_editor_event, window, cx, ); let preview = Preview::new(preview); Self::new( delegate, ContainerKind::UniformList, head, Some(preview), window, cx, ) } /// A picker similar to [`list()`](Self::list) (variable-height rows) but with /// a preview window. Use this instead of [`uniform_list_with_preview()`](Self::uniform_list_with_preview) /// when [`PickerDelegate::render_match`] can return rows of different heights /// (e.g. section headers and separators interleaved with matches). pub fn list_with_preview( delegate: D, preview: Arc<dyn PreviewBackend>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx),Lines 1–500 of 1939. Load more
use anyhow::Result; use gpui::Action; use gpui::{ AnyElement, App, Bounds, ClickEvent, Context, DismissEvent, EventEmitter, FocusHandle, Focusable, ListSizingBehavior, ListState, MouseButton, MouseUpEvent, Pixels, ScrollStrategy, Task, UniformListScrollHandle, Window, actions, canvas, div, list, prelude::*, uniform_list, }; use head::Head; use schemars::JsonSchema; use serde::Deserialize; use std::{ cell::Cell, cell::RefCell, collections::HashMap, ops::Range, rc::Rc, sync::Arc, time::Duration, }; use ui::{ Checkbox, ContextMenu, Divider, DocumentationAside, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*, }; use ui_input::ErasedEditorEvent; use util::ResultExt; use workspace::ModalView; use zed_actions::editor::{MoveDown, MoveUp}; mod footer; mod head; pub mod highlighted_match_with_paths; pub mod parts; mod persistence; pub mod popover_menu; mod preview; mod render; mod shape; use crate::shape::RelativeHeight; use crate::shape::RelativeWidth; pub use footer::PickerAction; pub use language::{HighlightedText, HighlightedTextBuilder}; pub use preview::Layout as PreviewLayout; pub use preview::MatchLocation; pub use preview::Preview; pub use preview::PreviewBackend; pub use preview::PreviewSource; pub use preview::Update as PreviewUpdate; pub use ui_input::ErasedEditor; pub const DEFAULT_MODAL_WIDTH: Rems = Rems(34.0); pub const DEFAULT_MODAL_MAX_HEIGHT: Rems = Rems(24.0); enum ElementContainer { List(ListState), UniformList(UniformListScrollHandle), } pub enum Direction { Up, Down, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ScrollBehavior { RevealSelected, PreserveOffset, } actions!( picker, [ /// Confirms the selected completion in the picker. ConfirmCompletion, /// Toggles the preview between hidden and visible. TogglePreview, /// Shows the preview to the right of the results. SetPreviewRight, /// Shows the preview below the results. SetPreviewBelow, /// Hides the preview. SetPreviewHidden, /// Opens the footer's actions menu. ToggleActionsMenu, /// Toggles multi-select mode, in which clicking items adds them to /// the selection instead of opening them ToggleMultiSelect, /// Toggles the current item in the multi-selection and advances to /// the next item, starting multi-select mode if it isn't already /// active MultiSelectNext, ] ); /// ConfirmInput is an alternative editor action which - instead of selecting active picker entry - treats pickers editor input literally, /// performing some kind of action on it. #[derive(Clone, PartialEq, Deserialize, JsonSchema, Default, Action)] #[action(namespace = picker)] #[serde(deny_unknown_fields)] pub struct ConfirmInput { pub secondary: bool, } struct PendingUpdateMatches { delegate_update_matches: Option<Task<()>>, _task: Task<Result<()>>, } #[derive(Clone, Copy)] enum Presentation { /// A self-contained modal: draws its own elevated background and dismisses /// when it loses focus. May optionally be resized (persisting its size); /// resizing only makes sense for modals. Modal { resizable: bool }, /// A popover attached to a menu/trigger: draws its own elevated background /// and dismisses when it loses focus, but is never resizable. Popover, /// Embedded inside a larger container (e.g. another modal) that provides its /// own chrome and handles dismissal. Embedded, } /// The default size for a given preview layout. With the preview hidden the /// picker uses its standard size; showing a preview expands it to the larger /// "telescope" size so the results pane isn't cramped beside the preview. fn default_shape_for_layout(hidden: shape::Centered, layout: preview::Layout) -> shape::Centered { match layout { preview::Layout::Hidden => hidden, preview::Layout::Right | preview::Layout::Below => shape::Centered::default(), } } pub struct Picker<D: PickerDelegate> { pub delegate: D, element_container: ElementContainer, head: Head, preview: Option<Preview>, pending_update_matches: Option<PendingUpdateMatches>, confirm_on_update: Option<bool>, select_instead_of_open: bool, shape: shape::Shape, /// The size the picker opens at (and resets to). Defaults depend on whether /// the picker has a preview; see [`Picker::initial_width`] / [`Picker::max_height`]. default_shape: shape::Centered, size_bounds: shape::SizeBounds, /// An external control to display a scrollbar in the `Picker`. show_scrollbar: bool, /// How the picker is presented, which controls its chrome and behavior. presentation: Presentation, /// Bounds tracking for the picker container (for aside positioning) picker_bounds: Rc<Cell<Option<Bounds<Pixels>>>>, /// Bounds tracking for items (for aside positioning) - maps item index to bounds item_bounds: Rc<RefCell<HashMap<usize, Bounds<Pixels>>>>, shape_loaded_from_persistence: bool, /// Handle for the default footer's Actions popover menu. Used to keep the /// picker open while that menu has focus. actions_menu_handle: PopoverMenuHandle<ContextMenu>, reopenable: bool, } #[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum PickerEditorPosition { #[default] /// Render the editor at the start of the picker. Usually the top Start, /// Render the editor at the end of the picker. Usually the bottom End, } pub trait PickerDelegate: Sized + 'static { type ListItem: IntoElement; /// Name of the picker, this is the key for serialization. We could use the /// typename of the delegate but then a rename would break persistence. fn name() -> &'static str; fn match_count(&self) -> usize; fn selected_index(&self) -> usize; fn separators_after_indices(&self) -> Vec<usize> { Vec::new() } fn set_selected_index( &mut self, ix: usize, window: &mut Window, cx: &mut Context<Picker<Self>>, ); /// Called before the picker handles `SelectPrevious` or `SelectNext`. Return `Some(query)` to /// set a new query and prevent the default selection behavior. fn select_history( &mut self, _direction: Direction, _query: &str, _window: &mut Window, _cx: &mut App, ) -> Option<String> { None } fn can_select( &self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> bool { true } fn select_on_hover(&self) -> bool { true } // Allows binding some optional effect to when the selection changes. fn selected_index_changed( &self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Box<dyn Fn(&mut Window, &mut App) + 'static>> { None } fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str>; fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option<SharedString> { Some("No matches".into()) } fn update_matches( &mut self, query: String, window: &mut Window, cx: &mut Context<Picker<Self>>, ) -> Task<()>; // Delegates that support this method (e.g. the CommandPalette) can chose to block on any background // work for up to `duration` to try and get a result synchronously. // This avoids a flash of an empty command-palette on cmd-shift-p, and lets workspace::SendKeystrokes // mostly work when dismissing a palette. fn finalize_update_matches( &mut self, _query: String, _duration: Duration, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> bool { false } /// Override if you want to have <enter> update the query instead of confirming. fn confirm_update_query( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>); /// Whether this delegate supports selecting multiple items at once. When /// `true`, the delegate owns the multi-selection state; the picker only /// drives the generic UX (toggle action, indicator, click routing). fn supports_multi_select(&self) -> bool { false } /// Whether the item at `ix` is part of the current multi-selection. fn is_item_selected(&self, _ix: usize) -> bool { false } /// Toggle whether the item at `ix` is part of the multi-selection. Items /// that cannot participate in multi-selection (e.g. non-file entries) /// should leave the selection unchanged. fn toggle_item_selected( &mut self, _ix: usize, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) { } /// Number of items currently in the multi-selection. fn selected_item_count(&self) -> usize { 0 } /// Clear the multi-selection. fn clear_selection(&mut self, _cx: &mut Context<Picker<Self>>) {} /// Open every item in the multi-selection. Called on confirm when the /// selection is non-empty; implementations should clear the selection. fn confirm_multi( &mut self, _secondary: bool, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) { } /// Instead of interacting with currently selected entry, treats editor input literally, /// performing some kind of action on it. fn confirm_input( &mut self, _secondary: bool, _window: &mut Window, _: &mut Context<Picker<Self>>, ) { } fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>); fn should_dismiss(&self) -> bool { true } fn confirm_completion( &mut self, _query: String, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<String> { None } /// Called when `SelectChild` fires (e.g. shift-right-arrow). Return `Some(query)` /// to step into the currently selected item (e.g. a directory); the picker /// will set the query and refresh matches. fn select_child( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } /// Called when `SelectParent` fires (e.g. shift-left-arrow). Return `Some(query)` /// to step back to the parent; the picker will set the query and refresh /// matches. fn select_parent( &mut self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<String> { None } fn editor_position(&self) -> PickerEditorPosition { PickerEditorPosition::default() } /// Prevent closing the modal on clicking in a popover menu that portrudes out /// This is already set by the Actions menu from the picker, this is here to /// support extra menus added by the delegate. fn has_another_open_menu(&self, _window: &Window, _cx: &App) -> bool { false } /// An optional control rendered at the trailing edge of the search bar, e.g. /// a filter toggle. Returning `Some` is the easy way to add such a control; /// for full control over the search bar, override [`Self::render_editor`]. fn searchbar_trailer( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Overrides the search bar entirely. Most delegates should return `None` /// to get the picker-rendered default (which includes /// [`Self::searchbar_trailer`] and the multi-select toggle); override for /// full control over the search bar. fn render_editor( &self, _editor: &Arc<dyn ErasedEditor>, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Div> { None } fn try_get_preview_data_for_match(&self, _cx: &App) -> Option<PreviewUpdate> { None } /// Called on the delegate when opening a preview to the side. Delegates can /// then change how much space they use for rendering the match fn preview_layout_changed(&mut self, _layout_is_horizontal: bool) {} fn render_match( &self, ix: usize, selected: bool, window: &mut Window, cx: &mut Context<Picker<Self>>, ) -> Option<Self::ListItem>; fn render_match_with_checkbox( &self, _ix: usize, _selected: bool, _checkbox: AnyElement, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<Self::ListItem> { None } fn render_header( &self, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Overrides the picker's footer. /// /// Note there normally isn't a footer unless this is set or the picker has /// a preview. If the picker has a preview add actions to it using picker_actions. fn render_footer( &self, _window: &mut Window, _: &mut Context<Picker<Self>>, ) -> Option<AnyElement> { None } /// Make this non-empty to have an `Actions` menu show up in the footer fn actions_menu( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Vec<footer::PickerAction> { Vec::new() } fn documentation_aside( &self, _window: &mut Window, _cx: &mut Context<Picker<Self>>, ) -> Option<DocumentationAside> { None } /// Returns the index of the item whose documentation aside should be shown. /// This is used to position the aside relative to that item. /// Typically this is the hovered item, not necessarily the selected item. fn documentation_aside_index(&self) -> Option<usize> { None } } impl<D: PickerDelegate> Focusable for Picker<D> { fn focus_handle(&self, cx: &App) -> FocusHandle { match &self.head { Head::Editor(editor) => editor.focus_handle(cx), Head::Empty(head) => head.focus_handle(cx), } } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] enum ContainerKind { List, UniformList, } impl<D: PickerDelegate> Picker<D> { /// A picker, which displays its matches using `gpui::uniform_list`, all matches should have the same height. /// The picker allows the user to perform search items by text. /// If `PickerDelegate::render_match` can return items with different heights, use `Picker::list`. pub fn uniform_list(delegate: D, window: &mut Window, cx: &mut Context<Self>) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx), Self::on_input_editor_event, window, cx, ); Self::new(delegate, ContainerKind::UniformList, head, None, window, cx) } /// A picker similar to [`uniform_list()`](Self::uniform_list) however this picker has a /// preview window where it shows extra information. pub fn uniform_list_with_preview( delegate: D, preview: Arc<dyn PreviewBackend>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx), Self::on_input_editor_event, window, cx, ); let preview = Preview::new(preview); Self::new( delegate, ContainerKind::UniformList, head, Some(preview), window, cx, ) } /// A picker similar to [`list()`](Self::list) (variable-height rows) but with /// a preview window. Use this instead of [`uniform_list_with_preview()`](Self::uniform_list_with_preview) /// when [`PickerDelegate::render_match`] can return rows of different heights /// (e.g. section headers and separators interleaved with matches). pub fn list_with_preview( delegate: D, preview: Arc<dyn PreviewBackend>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let head = Head::editor( delegate.placeholder_text(window, cx),