diff --git a/crates/ely_app/src/main.rs b/crates/ely_app/src/main.rs index dc582ed..f25b570 100644 --- a/crates/ely_app/src/main.rs +++ b/crates/ely_app/src/main.rs @@ -1,18 +1,22 @@ mod shell; use gpui::{ - App, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, SystemMenuType, WindowBounds, - WindowOptions, actions, px, size, + App, AppContext, Application, Bounds, Focusable, KeyBinding, Menu, MenuItem, SystemMenuType, + WindowBounds, WindowOptions, actions, px, size, }; use shell::ElyShell; -actions!(ely_app, [CloseCurrentTab, Quit]); +actions!(ely_app, [CloseCurrentTab, FocusAddressBar, OpenNewTab, Quit]); fn main() { Application::new().run(|cx: &mut App| { gpui_component::init(cx); cx.on_action(quit); cx.bind_keys([ + KeyBinding::new("cmd-t", OpenNewTab, None), + KeyBinding::new("ctrl-t", OpenNewTab, None), + KeyBinding::new("cmd-l", FocusAddressBar, None), + KeyBinding::new("ctrl-l", FocusAddressBar, None), KeyBinding::new("cmd-w", CloseCurrentTab, None), KeyBinding::new("ctrl-w", CloseCurrentTab, None), KeyBinding::new("cmd-q", Quit, None), @@ -28,7 +32,11 @@ fn main() { }, Menu { name: "File".into(), - items: vec![MenuItem::action("Close Tab", CloseCurrentTab)], + items: vec![ + MenuItem::action("New Tab", OpenNewTab), + MenuItem::separator(), + MenuItem::action("Close Tab", CloseCurrentTab), + ], }, ]); @@ -41,6 +49,12 @@ fn main() { }, |window, cx| { let shell = cx.new(|cx| ElyShell::new(window, cx)); + let focus_handle = shell.focus_handle(cx); + window.defer(cx, move |window, cx| { + if window.focused(cx).is_none() { + focus_handle.focus(window); + } + }); cx.new(|cx| gpui_component::Root::new(shell, window, cx)) }, ); diff --git a/crates/ely_app/src/shell.rs b/crates/ely_app/src/shell.rs index 00bb02c..46ef6ef 100644 --- a/crates/ely_app/src/shell.rs +++ b/crates/ely_app/src/shell.rs @@ -2,16 +2,17 @@ use ely_browser_core::{BrowserCore, BrowserSnapshot, InitialBrowserConfig}; use ely_design_system::{ELY_THEME, colors, spacing}; use ely_domain::{BrowserTab, CommandIntent, TabId, UrlText}; use gpui::{ - AnyElement, AppContext, Context, Entity, InteractiveElement, IntoElement, ParentElement, - Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Window, div, px, rgb, + AnyElement, App, AppContext, Context, Entity, FocusHandle, Focusable, InteractiveElement, + IntoElement, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, + Subscription, Window, div, px, rgb, }; use gpui_component::{ Sizable, StyledExt, button::{Button, ButtonVariants}, - input::{Input, InputEvent, InputState}, + input::{Input, InputEvent, InputState, SelectAll}, }; -use crate::CloseCurrentTab; +use crate::{CloseCurrentTab, FocusAddressBar, OpenNewTab}; enum ShellState { Ready(BrowserCore), @@ -20,6 +21,7 @@ enum ShellState { pub struct ElyShell { state: ShellState, + focus_handle: FocusHandle, command_input: Entity, last_intent: Option, _command_subscription: Subscription, @@ -58,6 +60,7 @@ impl ElyShell { Self { state, + focus_handle: cx.focus_handle(), command_input, last_intent: None, _command_subscription: command_subscription, @@ -70,10 +73,18 @@ impl ElyShell { { core.open_tab(url); self.sync_address_input(window, cx); + self.focus_address_bar(window, cx); cx.notify(); } } + fn focus_address_bar(&mut self, window: &mut Window, cx: &mut Context) { + self.command_input.update(cx, |input, cx| { + input.focus(window, cx); + }); + window.dispatch_action(Box::new(SelectAll), cx); + } + fn select_tab(&mut self, tab_id: &TabId, window: &mut Window, cx: &mut Context) { if let ShellState::Ready(core) = &mut self.state && core.select_tab(tab_id).is_ok() @@ -101,6 +112,19 @@ impl ElyShell { self.close_active_tab(window, cx); } + fn on_focus_address_bar( + &mut self, + _: &FocusAddressBar, + window: &mut Window, + cx: &mut Context, + ) { + self.focus_address_bar(window, cx); + } + + fn on_open_new_tab(&mut self, _: &OpenNewTab, window: &mut Window, cx: &mut Context) { + self.open_new_tab(window, cx); + } + fn sync_address_input(&mut self, window: &mut Window, cx: &mut Context) { let ShellState::Ready(core) = &mut self.state else { return; @@ -115,6 +139,12 @@ impl ElyShell { } } +impl Focusable for ElyShell { + fn focus_handle(&self, _: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + impl Render for ElyShell { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { match &self.state { @@ -136,7 +166,10 @@ impl ElyShell { ) -> AnyElement { div() .size_full() + .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_close_current_tab)) + .on_action(cx.listener(Self::on_focus_address_bar)) + .on_action(cx.listener(Self::on_open_new_tab)) .bg(rgb(ELY_THEME.canvas)) .text_color(rgb(ELY_THEME.ink)) .flex()