The footer chips already advertised ↑↓ to navigate and ↵ to open; they now match reality. - chrome::command_match exposes COMMAND_ACTIONS / matching_actions alongside the tab/history/bookmark match helpers and a CommandSelection enum + visible_command_rows that returns the flat ordered list of activatable rows. command_overlay drops its private copies of the action const and matcher and consumes them from the shared module so render and key handling share one source of truth. - ElyShell tracks command_selected_index with command_select_next / command_select_prev (cyclic, no notify when index doesn't change) and activate_selected_command which dispatches the right shell call for the currently selected CommandSelection variant and dismisses the overlay. Dismissing command mode resets the index to 0. - The shell root captures key_down via on_command_overlay_key_down. When the live snapshot's command_query starts with '>', up / down / enter run the matching helper and the event stops propagating so the omnibar input doesn't move its caret. - Each rendered row receives a `selected` flag. The selected row gets the design's tinted bg + accent-bar on the left edge so the active result is unambiguous at any keyboard step.
154 lines
5.2 KiB
Rust
154 lines
5.2 KiB
Rust
use ely_domain::UrlText;
|
|
use gpui::{ClipboardItem, Context, Window};
|
|
use gpui_component::input::SelectAll;
|
|
|
|
use super::{ElyShell, ShellState};
|
|
|
|
impl ElyShell {
|
|
pub(super) fn active_tab_matches_url(&self, url: &str) -> bool {
|
|
match &self.state {
|
|
ShellState::Ready(core) => core.active_tab().is_ok_and(|tab| tab.url().as_str() == url),
|
|
ShellState::StartupError(_) => false,
|
|
}
|
|
}
|
|
|
|
pub(super) fn open_new_tab(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
if let ShellState::Ready(core) = &mut self.state
|
|
&& core.open_new_tab().is_ok()
|
|
{
|
|
self.sync_address_input(window, cx);
|
|
self.focus_address_bar(window, cx);
|
|
cx.notify();
|
|
}
|
|
}
|
|
|
|
pub(super) fn open_downloads(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
self.open_internal_tab("ely://downloads", window, cx);
|
|
}
|
|
|
|
pub(super) fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
self.open_internal_tab("ely://history", window, cx);
|
|
}
|
|
|
|
pub(super) fn open_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
self.open_internal_tab("ely://settings", window, cx);
|
|
}
|
|
|
|
pub(super) fn open_task_manager(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
self.open_internal_tab("ely://task-manager", window, cx);
|
|
}
|
|
|
|
pub(super) fn open_internal_tab(
|
|
&mut self,
|
|
url_text: &str,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) {
|
|
if let Ok(url) = UrlText::parse(url_text) {
|
|
self.open_url(url, window, cx);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn open_url(&mut self, url: UrlText, window: &mut Window, cx: &mut Context<Self>) {
|
|
if let ShellState::Ready(core) = &mut self.state {
|
|
core.open_tab(url);
|
|
self.sync_address_input(window, cx);
|
|
self.focus_address_bar(window, cx);
|
|
cx.notify();
|
|
}
|
|
}
|
|
|
|
pub(super) fn focus_address_bar(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
self.command_input.update(cx, |input, cx| {
|
|
input.focus(window, cx);
|
|
});
|
|
window.dispatch_action(Box::new(SelectAll), cx);
|
|
}
|
|
|
|
pub(super) fn copy_active_tab_url(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
|
if let ShellState::Ready(core) = &self.state
|
|
&& let Ok(tab) = core.active_tab()
|
|
{
|
|
cx.write_to_clipboard(ClipboardItem::new_string(tab.url().as_str().to_string()));
|
|
}
|
|
}
|
|
|
|
pub(crate) fn dismiss_command_mode(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
|
if let ShellState::Ready(core) = &mut self.state {
|
|
core.set_command_query(String::new());
|
|
}
|
|
|
|
self.command_input.update(cx, |input, cx| {
|
|
input.set_value("", window, cx);
|
|
});
|
|
self.command_selected_index = 0;
|
|
cx.notify();
|
|
}
|
|
|
|
pub(crate) fn command_select_next(&mut self, total_rows: usize, cx: &mut Context<Self>) {
|
|
if total_rows == 0 {
|
|
self.command_selected_index = 0;
|
|
return;
|
|
}
|
|
let next = (self.command_selected_index + 1) % total_rows;
|
|
if next != self.command_selected_index {
|
|
self.command_selected_index = next;
|
|
cx.notify();
|
|
}
|
|
}
|
|
|
|
pub(crate) fn command_select_prev(&mut self, total_rows: usize, cx: &mut Context<Self>) {
|
|
if total_rows == 0 {
|
|
self.command_selected_index = 0;
|
|
return;
|
|
}
|
|
let prev = if self.command_selected_index == 0 {
|
|
total_rows - 1
|
|
} else {
|
|
self.command_selected_index - 1
|
|
};
|
|
if prev != self.command_selected_index {
|
|
self.command_selected_index = prev;
|
|
cx.notify();
|
|
}
|
|
}
|
|
|
|
pub(crate) fn activate_selected_command(
|
|
&mut self,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) {
|
|
let snapshot = match &self.state {
|
|
ShellState::Ready(core) => match core.snapshot() {
|
|
Ok(snapshot) => snapshot,
|
|
Err(_) => return,
|
|
},
|
|
ShellState::StartupError(_) => return,
|
|
};
|
|
let needle_owned = snapshot.command_query.as_str();
|
|
let Some(stripped) = needle_owned.strip_prefix('>') else {
|
|
return;
|
|
};
|
|
let needle = stripped.trim().to_lowercase();
|
|
let rows = crate::shell::chrome::command_match::visible_command_rows(
|
|
&snapshot, &needle,
|
|
);
|
|
if rows.is_empty() {
|
|
return;
|
|
}
|
|
let index = self.command_selected_index.min(rows.len() - 1);
|
|
match rows[index].clone() {
|
|
crate::shell::chrome::command_match::CommandSelection::SelectTab(tab_id) => {
|
|
self.select_tab(&tab_id, window, cx);
|
|
}
|
|
crate::shell::chrome::command_match::CommandSelection::OpenUrl(url) => {
|
|
self.open_internal_tab(url.as_str(), window, cx);
|
|
}
|
|
crate::shell::chrome::command_match::CommandSelection::OpenRoute(route) => {
|
|
self.open_internal_tab(route, window, cx);
|
|
}
|
|
}
|
|
self.dismiss_command_mode(window, cx);
|
|
}
|
|
}
|