
The Tab Switcher implementation (#7653): - `ctrl-tab` opens the Tab Switcher and moves selection to the previously selcted tab. It also cycles selection forward. - `ctrl-shift-tab` opens the Tab Switcher and moves selection to the last tab in the list. It also cycles selection backward. - Tab is selected and the Tab Switcher is closed on the shortcut modifier key (`ctrl` by default) release. - List items are in reverse activation history order. - The list reacts to the item changes in background (new tab, tab closed, tab title changed etc.) Intentionally not in scope of this PR: - File icons - Close buttons I will come back to these features. I think they need to be implemented in separate PRs, and be synchronized with changes in how tabs are rendered, to reuse the code as it's done in the current implementation. The Tab Switcher looks usable even without them. Known Issues: Tab Switcher doesn't react to mouse click on a list item. It's not a tab switcher specific problem, it looks like ctrl-clicks are not handled the same way in Zed as cmd-clicks. For instance, menu items can be activated with cmd-click, but don't react to ctrl-click. Since the Tab Switcher's default keybinding is `ctrl-tab`, the user can only click an item with `ctrl` pushed down, thus preventing `on_click()` from firing. fixes #7653, #7321 Release Notes: - Added Tab Switcher which is accessible via `ctrl-tab` and `ctrl-shift-tab` (#7653) (#7321) Related issues: - Unblocks #7356, I hope 😄 How it looks and works (it's only `ctrl-tab`'s and `ctrl-shift-tab`'s, no `enter`'s or mouse clicks): https://github.com/zed-industries/zed/assets/2101250/4ad4ec6a-5314-481b-8b35-7ac85e43eb92 --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Mikayla Maki <mikayla@zed.dev>
64 lines
1.7 KiB
Rust
64 lines
1.7 KiB
Rust
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>,
|
|
edit_handler: impl FnMut(&mut V, View<Editor>, &EditorEvent, &mut ViewContext<'_, V>) + 'static,
|
|
cx: &mut ViewContext<V>,
|
|
) -> 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<V: 'static>(
|
|
blur_handler: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
|
|
cx: &mut ViewContext<V>,
|
|
) -> Self {
|
|
let head = cx.new_view(|cx| EmptyHead::new(cx));
|
|
cx.on_blur(&head.focus_handle(cx), blur_handler).detach();
|
|
Self::Empty(head)
|
|
}
|
|
}
|
|
|
|
/// 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()
|
|
}
|
|
}
|