diff --git a/src/ui/euph/links.rs b/src/ui/euph/links.rs index 3bb7f08..e782fc6 100644 --- a/src/ui/euph/links.rs +++ b/src/ui/euph/links.rs @@ -3,6 +3,7 @@ use std::io; use crossterm::event::KeyCode; use crossterm::style::{ContentStyle, Stylize}; use linkify::{LinkFinder, LinkKind}; +use toss::styled::Styled; use crate::ui::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use crate::ui::widgets::list::ListState; @@ -22,6 +23,8 @@ pub enum EventResult { ErrorOpeningLink { link: String, error: io::Error }, } +const NUMBER_KEYS: [char; 10] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']; + impl LinksState { pub fn new(content: &str) -> Self { let links = LinkFinder::new() @@ -37,6 +40,8 @@ impl LinksState { } pub fn widget(&self) -> BoxedWidget { + let style_selected = ContentStyle::default().black().on_white(); + let mut list = self.list.widget().focus(true); if self.links.is_empty() { list.add_unsel(Text::new(( @@ -45,30 +50,51 @@ impl LinksState { ))) } for (id, link) in self.links.iter().enumerate() { - list.add_sel( - id, - Text::new((link,)), - Text::new((link, ContentStyle::default().black().on_white())), - ); + let (line_normal, line_selected) = if let Some(number_key) = NUMBER_KEYS.get(id) { + ( + Styled::new( + format!("[{number_key}]"), + ContentStyle::default().dark_grey().bold(), + ) + .then_plain(" ") + .then_plain(link), + Styled::new(format!("[{number_key}]"), style_selected.bold()) + .then(" ", style_selected) + .then(link, style_selected), + ) + } else { + ( + Styled::new_plain(format!(" {link}")), + Styled::new(format!(" {link}"), style_selected), + ) + }; + + list.add_sel(id, Text::new(line_normal), Text::new(line_selected)); } Popup::new(list).title("Links").build() } - pub fn open_link(&self) -> EventResult { - if let Some(id) = self.list.cursor() { - if let Some(link) = self.links.get(id) { - if let Err(error) = open::that(link) { - return EventResult::ErrorOpeningLink { - link: link.to_string(), - error, - }; - } + fn open_link_by_id(&self, id: usize) -> EventResult { + if let Some(link) = self.links.get(id) { + if let Err(error) = open::that(link) { + return EventResult::ErrorOpeningLink { + link: link.to_string(), + error, + }; } } EventResult::Handled } + fn open_link(&self) -> EventResult { + if let Some(id) = self.list.cursor() { + self.open_link_by_id(id) + } else { + EventResult::Handled + } + } + pub fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { bindings.binding("esc", "close links popup"); bindings.binding("j/k, ↓/↑", "move cursor up/down"); @@ -77,6 +103,7 @@ impl LinksState { bindings.binding("ctrl+y/e", "scroll up/down"); bindings.empty(); bindings.binding("enter", "open selected link"); + bindings.binding("1,2,...", "open link by position"); } pub fn handle_input_event(&mut self, event: &InputEvent) -> EventResult { @@ -89,6 +116,16 @@ impl LinksState { key!(Ctrl + 'y') => self.list.scroll_up(1), key!(Ctrl + 'e') => self.list.scroll_down(1), key!(Enter) => return self.open_link(), + key!('1') => return self.open_link_by_id(0), + key!('2') => return self.open_link_by_id(1), + key!('3') => return self.open_link_by_id(2), + key!('4') => return self.open_link_by_id(3), + key!('5') => return self.open_link_by_id(4), + key!('6') => return self.open_link_by_id(5), + key!('7') => return self.open_link_by_id(6), + key!('8') => return self.open_link_by_id(7), + key!('9') => return self.open_link_by_id(8), + key!('0') => return self.open_link_by_id(9), _ => return EventResult::NotHandled, } EventResult::Handled