diff --git a/internal/adapters/ui/handlers.go b/internal/adapters/ui/handlers.go index 8eeafa0..897e053 100644 --- a/internal/adapters/ui/handlers.go +++ b/internal/adapters/ui/handlers.go @@ -183,6 +183,43 @@ func (t *tui) handleSearchFocus() { } } +func (t *tui) handleSearchNavigate(direction int) { + if t.serverList != nil { + t.app.SetFocus(t.serverList) + + currentIdx := t.serverList.GetCurrentItem() + itemCount := t.serverList.GetItemCount() + + if itemCount == 0 { + return + } + + if direction > 0 { + if currentIdx < itemCount-1 { + t.serverList.SetCurrentItem(currentIdx + 1) + } else { + t.serverList.SetCurrentItem(0) + } + } else { + if currentIdx > 0 { + t.serverList.SetCurrentItem(currentIdx - 1) + } else { + t.serverList.SetCurrentItem(itemCount - 1) + } + } + + if server, ok := t.serverList.GetSelectedServer(); ok { + t.details.UpdateServer(server) + } + } +} + +func (t *tui) handleReturnToSearch() { + if t.searchBar != nil { + t.app.SetFocus(t.searchBar) + } +} + func (t *tui) handleServerConnect() { if server, ok := t.serverList.GetSelectedServer(); ok { diff --git a/internal/adapters/ui/search_bar.go b/internal/adapters/ui/search_bar.go index 7b03c90..7d373b3 100644 --- a/internal/adapters/ui/search_bar.go +++ b/internal/adapters/ui/search_bar.go @@ -21,8 +21,9 @@ import ( type SearchBar struct { *tview.InputField - onSearch func(string) - onEscape func() + onSearch func(string) + onEscape func() + onNavigate func(direction int) // -1 for up, 1 for down } func NewSearchBar() *SearchBar { @@ -57,6 +58,24 @@ func (s *SearchBar) build() { } } }) + + s.InputField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + //nolint:exhaustive // We only handle arrow keys and pass through others + switch event.Key() { + case tcell.KeyDown: + if s.onNavigate != nil { + s.onNavigate(1) + } + return nil + case tcell.KeyUp: + if s.onNavigate != nil { + s.onNavigate(-1) + } + return nil + default: + return event + } + }) } func (s *SearchBar) OnSearch(fn func(string)) *SearchBar { @@ -68,3 +87,8 @@ func (s *SearchBar) OnEscape(fn func()) *SearchBar { s.onEscape = fn return s } + +func (s *SearchBar) OnNavigate(fn func(direction int)) *SearchBar { + s.onNavigate = fn + return s +} diff --git a/internal/adapters/ui/server_list.go b/internal/adapters/ui/server_list.go index b175014..1a58d39 100644 --- a/internal/adapters/ui/server_list.go +++ b/internal/adapters/ui/server_list.go @@ -25,6 +25,7 @@ type ServerList struct { servers []domain.Server onSelection func(domain.Server) onSelectionChange func(domain.Server) + onReturnToSearch func() } func NewServerList() *ServerList { @@ -52,6 +53,18 @@ func (sl *ServerList) build() { sl.onSelectionChange(sl.servers[index]) } }) + + sl.List.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + //nolint:exhaustive // We only handle specific keys and pass through others + switch event.Key() { + case tcell.KeyLeft, tcell.KeyRight, tcell.KeyBackspace, tcell.KeyBackspace2, tcell.KeyESC: + if sl.onReturnToSearch != nil { + sl.onReturnToSearch() + } + return nil + } + return event + }) } func (sl *ServerList) UpdateServers(servers []domain.Server) { @@ -93,3 +106,8 @@ func (sl *ServerList) OnSelectionChange(fn func(server domain.Server)) *ServerLi sl.onSelectionChange = fn return sl } + +func (sl *ServerList) OnReturnToSearch(fn func()) *ServerList { + sl.onReturnToSearch = fn + return sl +} diff --git a/internal/adapters/ui/tui.go b/internal/adapters/ui/tui.go index 075a0e4..d938e6f 100644 --- a/internal/adapters/ui/tui.go +++ b/internal/adapters/ui/tui.go @@ -91,11 +91,13 @@ func (t *tui) buildComponents() *tui { t.header = NewAppHeader(t.version, t.commit, RepoURL) t.searchBar = NewSearchBar(). OnSearch(t.handleSearchInput). - OnEscape(t.blurSearchBar) + OnEscape(t.blurSearchBar). + OnNavigate(t.handleSearchNavigate) IsForwarding = t.serverService.IsForwarding t.serverList = NewServerList(). - OnSelectionChange(t.handleServerSelectionChange) + OnSelectionChange(t.handleServerSelectionChange). + OnReturnToSearch(t.handleReturnToSearch) t.details = NewServerDetails() t.statusBar = NewStatusBar()