diff --git a/src/app.rs b/src/app.rs index b343f5f..1106b51 100644 --- a/src/app.rs +++ b/src/app.rs @@ -23,14 +23,31 @@ pub enum Page { ClipboardHistory, } +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub enum ArrowKey { + ArrowUp, + ArrowDown, + ArrowLeft, + ArrowRight, +} + +#[derive(Debug, Clone)] +pub enum Move { + Back, + Forwards(String), +} + /// The message type that iced uses for actions that can do something #[derive(Debug, Clone)] pub enum Message { OpenWindow, SearchQueryChanged(String, Id), KeyPressed(u32), + FocusTextInput(Move), HideWindow(Id), RunFunction(Function), + OpenFocused, ReturnFocus, ClearSearchResults, WindowFocusChanged(Id, bool), @@ -39,6 +56,7 @@ pub enum Message { SetSender(ExtSender), SwitchToPage(Page), ClipboardHistory(ClipBoardContentType), + ChangeFocus(ArrowKey), } /// The window settings for rustcast diff --git a/src/app/apps.rs b/src/app/apps.rs index b4ad196..f73d317 100644 --- a/src/app/apps.rs +++ b/src/app/apps.rs @@ -108,6 +108,8 @@ impl App { pub fn render<'a>( &'a self, theme: &'a crate::config::Theme, + id_num: u32, + focussed_id: u32, ) -> impl Into> { let mut tile = Row::new().width(Fill).height(55); @@ -155,13 +157,20 @@ impl App { ) .width(Fill); + let (highlight_opacity, border_width) = if focussed_id == id_num { + (0.7, 0.55) + } else { + (0.5, 0.1) + }; + container(tile) - .style(|_| iced::widget::container::Style { + .id(format!("result-{}", id_num)) + .style(move |_| iced::widget::container::Style { text_color: Some(theme.text_color(1.)), background: Some(Background::Color(theme.bg_color())), border: iced::Border { - color: theme.text_color(0.5), - width: 0.1, + color: theme.text_color(highlight_opacity), + width: border_width, radius: Radius::new(0), }, ..Default::default() diff --git a/src/app/tile.rs b/src/app/tile.rs index 08bebef..de5e4de 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -4,7 +4,7 @@ mod update; use crate::app::apps::{App, AppCommand}; use crate::app::tile::elm::default_app_paths; -use crate::app::{Message, Page}; +use crate::app::{ArrowKey, Message, Move, Page}; use crate::clipboard::ClipBoardContentType; use crate::commands::Function; use crate::config::Config; @@ -59,6 +59,7 @@ impl Drop for ExtSender { #[derive(Clone)] pub struct Tile { theme: iced::Theme, + focus_id: u32, query: String, query_lc: String, prev_query_lc: String, @@ -126,13 +127,27 @@ impl Tile { keyboard::Key::Named(Named::Escape) => { return Some(Message::KeyPressed(65598)); } + keyboard::Key::Named(Named::ArrowUp) => { + return Some(Message::ChangeFocus(ArrowKey::ArrowUp)); + } + keyboard::Key::Named(Named::ArrowDown) => { + return Some(Message::ChangeFocus(ArrowKey::ArrowDown)); + } keyboard::Key::Character(chr) => { if modifiers.command() && chr.to_string().to_lowercase() == "r" { return Some(Message::ReloadConfig); } else if modifiers.command() && chr.to_string() == "," { open_settings(); + } else { + return Some(Message::FocusTextInput(Move::Forwards( + chr.to_string(), + ))); } } + keyboard::Key::Named(Named::Enter) => return Some(Message::OpenFocused), + keyboard::Key::Named(Named::Backspace) => { + return Some(Message::FocusTextInput(Move::Back)); + } _ => {} } None diff --git a/src/app/tile/elm.rs b/src/app/tile/elm.rs index 60e25b1..8c1794a 100644 --- a/src/app/tile/elm.rs +++ b/src/app/tile/elm.rs @@ -3,7 +3,7 @@ use global_hotkey::hotkey::{Code, Modifiers}; use iced::border::Radius; -use iced::widget::scrollable::{Direction, Scrollbar}; +use iced::widget::scrollable::{Anchor, Direction, Scrollbar}; use iced::widget::text::LineHeight; use iced::widget::{Column, Scrollable, container, space}; use iced::{Color, window}; @@ -69,6 +69,7 @@ pub fn new( query: String::new(), query_lc: String::new(), prev_query_lc: String::new(), + focus_id: 0, results: vec![], options, hotkey, @@ -111,15 +112,23 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> { .padding(20); let scrollbar_direction = if tile.config.theme.show_scroll_bar { - Direction::Vertical(Scrollbar::new().width(2).scroller_width(2)) + let anchor = if tile.focus_id > 4 { + Anchor::End + } else { + Anchor::Start + }; + Direction::Vertical(Scrollbar::new().width(2).scroller_width(2).anchor(anchor)) } else { Direction::Vertical(Scrollbar::hidden()) }; let results = match tile.page { Page::Main => { let mut search_results = Column::new(); + let mut i = 0_u32; for result in &tile.results { - search_results = search_results.push(result.render(&tile.config.theme)); + search_results = + search_results.push(result.render(&tile.config.theme, i, tile.focus_id)); + i += 1; } search_results } diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index ccd06d8..d92748e 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -13,7 +13,9 @@ use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use rayon::slice::ParallelSliceMut; +use crate::app::ArrowKey; use crate::app::DEFAULT_WINDOW_HEIGHT; +use crate::app::Move; use crate::app::RUSTCAST_DESC_NAME; use crate::app::WINDOW_WIDTH; use crate::app::apps::App; @@ -53,6 +55,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { } Message::SearchQueryChanged(input, id) => { + tile.focus_id = 0; #[cfg(target_os = "macos")] if tile.config.haptic_feedback { perform_haptic(HapticPattern::Alignment); @@ -106,7 +109,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { open_command: AppCommand::Function(Function::GoogleSearch(tile.query.clone())), icons: None, desc: "Web Search".to_string(), - name: format!("Google for: {}", tile.query), + name: format!("Search for: {}", tile.query), name_lc: String::new(), }]; return window::resize( @@ -198,6 +201,24 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { Task::none() } + Message::ChangeFocus(key) => { + let u32_len = tile.results.len() as u32; + match key { + ArrowKey::ArrowDown => tile.focus_id = (tile.focus_id + 1) % u32_len, + ArrowKey::ArrowUp => tile.focus_id = (tile.focus_id + u32_len - 1) % u32_len, + _ => {} + } + operation::focus(format!("result-{}", tile.focus_id)) + } + + Message::OpenFocused => match tile.results.get(tile.focus_id as usize) { + Some(App { + open_command: AppCommand::Function(func), + .. + }) => Task::done(Message::RunFunction(func.to_owned())), + Some(_) | None => Task::none(), + }, + Message::ReloadConfig => { let new_config: Config = toml::from_str( &fs::read_to_string( @@ -292,6 +313,26 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { Task::none() } + Message::FocusTextInput(update_query_char) => { + match update_query_char { + Move::Forwards(query_char) => { + tile.query += &query_char.clone(); + tile.query_lc += &query_char.clone().to_lowercase(); + } + Move::Back => { + tile.query.pop(); + tile.query_lc.pop(); + } + } + let updated_query = tile.query.clone(); + Task::batch([ + operation::focus("query"), + window::latest() + .map(|x| x.unwrap()) + .map(move |x| Message::SearchQueryChanged(updated_query.clone(), x)), + ]) + } + Message::ClearSearchResults => { tile.results = vec![]; Task::none()