Allow Picker to be headless (#9099)

At the moment, `Picker` always has an editor at the top, that allows the
user to search list elements by text. Sometimes, the UI doesn't need
such an editor. Like in the [tab
switcher](https://github.com/zed-industries/zed/issues/7653) that will
confirm selection on the modifier keys release, so there will be no
searching capabilities.

This PR adds support for a "headless picker" that doesn't display an
editor. It only has an invisible element to hold input focus for
preventing it from jumping back to the workspace.

At the moment, none of the picker implementations is made headless. It's
for the future implementations. But I'd like to make it a separate PR to
keep it focused on this particular feature.

Release Notes:

- N/A

Related Issues:
- Part of #7653

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
Andrew Lygin 2024-03-18 18:59:53 +03:00 committed by GitHub
parent ad97c357a5
commit 7512c9949c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 120 additions and 36 deletions

59
crates/picker/src/head.rs Normal file
View file

@ -0,0 +1,59 @@
use std::sync::Arc;
use editor::{Editor, EditorEvent};
use gpui::{prelude::*, AppContext, FocusHandle, FocusableView, View};
use ui::prelude::*;
/// The head of a [`Picker`](crate::Picker).
pub(crate) enum Head {
/// Picker has an editor that allows the user to filter the list.
Editor(View<Editor>),
/// Picker has no head, it's just a list of items.
Empty(View<EmptyHead>),
}
impl Head {
pub fn editor<V: 'static>(
placeholder_text: Arc<str>,
cx: &mut ViewContext<V>,
edit_handler: impl FnMut(&mut V, View<Editor>, &EditorEvent, &mut ViewContext<'_, V>) + 'static,
) -> Self {
let editor = cx.new_view(|cx| {
let mut editor = Editor::single_line(cx);
editor.set_placeholder_text(placeholder_text, cx);
editor
});
cx.subscribe(&editor, edit_handler).detach();
Self::Editor(editor)
}
pub fn empty(cx: &mut WindowContext) -> Self {
Self::Empty(cx.new_view(|cx| EmptyHead::new(cx)))
}
}
/// An invisible element that can hold focus.
pub(crate) struct EmptyHead {
focus_handle: FocusHandle,
}
impl EmptyHead {
fn new(cx: &mut ViewContext<Self>) -> Self {
Self {
focus_handle: cx.focus_handle(),
}
}
}
impl Render for EmptyHead {
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
div().track_focus(&self.focus_handle)
}
}
impl FocusableView for EmptyHead {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}