From a1043eafd30bd9097a00662b00686e94dc8fdb53 Mon Sep 17 00:00:00 2001 From: JRF Date: Mon, 29 Aug 2022 19:00:42 -0500 Subject: [PATCH] Add key bindings to select and open links --- Cargo.lock | 17 +++++++++++++++ Cargo.toml | 1 + src/ui/euph/links.rs | 50 ++++++++++++++++++++++++++++++-------------- src/ui/euph/room.rs | 30 +++++++++++++------------- 4 files changed, 66 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33ea96f..5082530 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,7 @@ dependencies = [ "edit", "euphoxide", "log", + "open", "parking_lot", "rusqlite", "serde", @@ -612,6 +613,16 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +[[package]] +name = "open" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23a407004a1033f53e93f9b45580d14de23928faad187384f891507c9b0c045" +dependencies = [ + "pathdiff", + "windows-sys", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -647,6 +658,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4347a44..5c98f49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ crossterm = "0.25.0" directories = "4.0.1" edit = "0.1.4" log = { version = "0.4.17", features = ["std"] } +open = "3.0.2" parking_lot = "0.12.1" rusqlite = { version = "0.28.0", features = ["bundled", "time"] } serde = { version = "1.0.144", features = ["derive"] } diff --git a/src/ui/euph/links.rs b/src/ui/euph/links.rs index 0b71681..2512b69 100644 --- a/src/ui/euph/links.rs +++ b/src/ui/euph/links.rs @@ -1,12 +1,8 @@ use std::io; -use std::sync::Arc; use crossterm::event::KeyCode; use crossterm::style::{ContentStyle, Stylize}; -use parking_lot::FairMutex; -use toss::terminal::Terminal; -use crate::euph::Room; use crate::ui::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use crate::ui::widgets::list::ListState; use crate::ui::widgets::popup::Popup; @@ -38,7 +34,7 @@ impl LinksState { } pub fn widget(&self) -> BoxedWidget { - let mut list = self.list.widget(); + let mut list = self.list.widget().focus(true); for (id, link) in self.links.iter().enumerate() { list.add_sel( id, @@ -50,20 +46,42 @@ impl LinksState { Popup::new(list).title("Links").build() } - pub fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { - bindings.binding("esc", "close links popup") + 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, + }; + } + } + } + EventResult::Handled } - pub fn handle_input_event( - &mut self, - terminal: &mut Terminal, - crossterm_lock: &Arc>, - event: &InputEvent, - room: &Option, - ) -> EventResult { + pub fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { + bindings.binding("esc", "close links popup"); + bindings.binding("j/k, ↓/↑", "move cursor up/down"); + bindings.binding("g, home", "move cursor to top"); + bindings.binding("G, end", "move cursor to bottom"); + bindings.binding("ctrl+y/e", "scroll up/down"); + bindings.empty(); + bindings.binding("enter", "open selected link"); + } + + pub fn handle_input_event(&mut self, event: &InputEvent) -> EventResult { match event { - key!(Esc) => EventResult::Close, - _ => EventResult::NotHandled, + key!(Esc) => return EventResult::Close, + key!('k') | key!(Up) => self.list.move_cursor_up(), + key!('j') | key!(Down) => self.list.move_cursor_down(), + key!('g') | key!(Home) => self.list.move_cursor_to_top(), + key!('G') | key!(End) => self.list.move_cursor_to_bottom(), + key!(Ctrl + 'y') => self.list.scroll_up(1), + key!(Ctrl + 'e') => self.list.scroll_down(1), + key!(Enter) => return self.open_link(), + _ => return EventResult::NotHandled, } + EventResult::Handled } } diff --git a/src/ui/euph/room.rs b/src/ui/euph/room.rs index 9b780ed..3395205 100644 --- a/src/ui/euph/room.rs +++ b/src/ui/euph/room.rs @@ -464,23 +464,21 @@ impl EuphRoom { } } } - State::Links(links) => { - match links.handle_input_event(terminal, crossterm_lock, event, &self.room) { - links::EventResult::NotHandled => false, - links::EventResult::Handled => true, - links::EventResult::Close => { - self.state = State::Normal; - true - } - links::EventResult::ErrorOpeningLink { link, error } => { - self.popups.push_front(RoomPopup::Error { - description: format!("Failed to open link: {link}"), - reason: format!("{error}"), - }); - true - } + State::Links(links) => match links.handle_input_event(event) { + links::EventResult::NotHandled => false, + links::EventResult::Handled => true, + links::EventResult::Close => { + self.state = State::Normal; + true } - } + links::EventResult::ErrorOpeningLink { link, error } => { + self.popups.push_front(RoomPopup::Error { + description: format!("Failed to open link: {link}"), + reason: format!("{error}"), + }); + true + } + }, } }