Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions crates/ui/src/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,28 @@ impl CommandPalette {
}
}

/// Focus the search input when the palette opens.
pub fn focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
/// Reset the palette as it opens. With `focus_query` the search input
/// takes focus; without it the overlay does, so Escape still closes the
/// palette but no software keyboard is raised.
pub fn open(&mut self, focus_query: bool, window: &mut Window, cx: &mut Context<Self>) {
self.query.update(cx, |state, cx| {
state.set_value(String::new(), window, cx);
state.focus(window, cx);
if focus_query {
state.focus(window, cx);
}
});
if !focus_query {
self.focus_handle.focus(window, cx);
}
self.selected = 0;
self.content_hits.clear();
}

#[cfg(test)]
pub(crate) fn query_focus_handle(&self, cx: &gpui::App) -> FocusHandle {
self.query.read(cx).focus_handle(cx)
}

/// Debounce, then ask the host for content hits. The index, cache and
/// session metadata are host-owned; this keeps only the debounce and the
/// generation guard that discards an answer a newer query superseded.
Expand Down
39 changes: 35 additions & 4 deletions crates/ui/src/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1539,14 +1539,22 @@ impl AppShell {

/// New thread: one project starts a draft directly, several go through the
/// palette, which already owns "new thread in <project>" and can search.
/// On a phone the palette opens without the keyboard: the projects are
/// there to tap, and the search pill still raises it on demand.
fn start_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some(attachment) = &self.attachment else {
return;
};
let projects = attachment.link.store.read(cx).projects();
let Some(project) = projects.first().cloned().filter(|_| projects.len() == 1) else {
self.window_state
.update(cx, |state, cx| state.open_palette(cx));
let mobile = crate::window_seam::is_mobile(cx);
self.window_state.update(cx, |state, cx| {
if mobile {
state.open_palette_without_keyboard(cx);
} else {
state.open_palette(cx);
}
});
return;
};
attachment.link.store.update(cx, |store, cx| {
Expand Down Expand Up @@ -2395,12 +2403,17 @@ impl Render for AppShell {
// first frame, or an inset change that narrowed the content box).
self.sync_layout(window, cx);
// The palette takes focus on the frame it opens, at either width.
let palette_open = self.window_state.read(cx).palette_open;
let (palette_open, focus_query) = {
let state = self.window_state.read(cx);
(state.palette_open, state.palette_focuses_query)
};
if palette_open
&& !self.palette_was_open
&& let Some(attachment) = &self.attachment
{
attachment.palette.update(cx, |p, cx| p.focus(window, cx));
attachment
.palette
.update(cx, |p, cx| p.open(focus_query, window, cx));
}
self.palette_was_open = palette_open;
let body = if self.compact(cx) {
Expand Down Expand Up @@ -3548,6 +3561,24 @@ mod tests {
draw(cx);
if project_count > 1 || !compact {
assert!(shell.read_with(cx, |shell, cx| shell.window_state.read(cx).palette_open));
cx.update(|window, cx| {
let palette = &shell.read(cx).attachment.as_ref().unwrap().palette;
let query = palette.read(cx).query_focus_handle(cx);
// The chooser is tapped through on a phone; a desktop
// window (even a narrow one) types into it.
assert_eq!(
query.is_focused(window),
!(compact && crate::window_seam::is_mobile(cx)),
"project chooser raises the keyboard only off-phone"
);
assert!(
palette
.read(cx)
.focus_handle(cx)
.contains_focused(window, cx),
"the palette owns focus so Escape closes it"
);
});
let row = cx
.debug_bounds("palette-new-thread-project-1")
.expect("project choice");
Expand Down
14 changes: 14 additions & 0 deletions crates/ui/src/window_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ pub struct WindowState {
/// [`Destination::Hosts`], the root the platform's Back gesture falls off.
history: Vec<Destination>,
pub palette_open: bool,
/// Whether the palette focuses its search input when it opens. A tap on
/// the search pill wants the keyboard; the mobile "+" button lists
/// projects to tap and must not raise it.
pub palette_focuses_query: bool,
pub sidebar_collapsed: bool,
pub quit_prompt_epoch: u64,
pub quit_prompt_open: bool,
Expand All @@ -154,6 +158,7 @@ impl WindowState {
compact: false,
history: vec![Destination::Hosts],
palette_open: false,
palette_focuses_query: true,
sidebar_collapsed,
quit_prompt_epoch: 0,
quit_prompt_open: false,
Expand Down Expand Up @@ -312,6 +317,15 @@ impl WindowState {

pub fn open_palette(&mut self, cx: &mut Context<Self>) {
self.palette_open = true;
self.palette_focuses_query = true;
cx.notify();
}

/// Open the palette as a list to tap through, leaving the search input
/// unfocused so no software keyboard comes up.
pub fn open_palette_without_keyboard(&mut self, cx: &mut Context<Self>) {
self.palette_open = true;
self.palette_focuses_query = false;
cx.notify();
}

Expand Down