From ca024351c834d85babbcf765f80ef6bd89241c3b Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 16:18:25 -0500 Subject: [PATCH 01/14] render placeholder alias filter popup --- src/alias_filter.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++ src/app.rs | 10 +++++++- src/handler.rs | 8 +++++- src/lib.rs | 2 ++ src/ui.rs | 1 + 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/alias_filter.rs diff --git a/src/alias_filter.rs b/src/alias_filter.rs new file mode 100644 index 0000000..415cf63 --- /dev/null +++ b/src/alias_filter.rs @@ -0,0 +1,60 @@ +use std::sync::Arc; + +use ratatui::{ + layout::{Alignment, Constraint, Direction, Layout, Margin}, + style::{Color, Style, Stylize}, + widgets::{Row, Table, TableState}, + Frame, +}; + +use crate::{app::ColorMode, config::Config}; + +#[derive(Debug)] +pub struct AliasFilter{ + pub filter: Option, + state: TableState, +} + +impl AliasFilter { + pub fn new(config: Arc) -> Self { + let mut state = TableState::new().with_offset(0); + state.select(Some(0)); + + Self { + state, + filter: None + } + } + + pub fn render(&mut self, frame: &mut Frame, color_mode: ColorMode) { + let widths = [ + Constraint::Length(20), + Constraint::Length(20), + ]; + + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Fill(1), + Constraint::Length(28), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + let block = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Fill(1), + Constraint::Length(70), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(layout[1])[1]; + + let row = Row::new(vec!["asdf"]); + let table = Table::new([row], widths); + + frame.render_widget(table, block); + } +} diff --git a/src/app.rs b/src/app.rs index e2aa102..13e9c2e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -20,6 +20,7 @@ use crate::{ config::Config, confirmation::PairingConfirmation, help::Help, + alias_filter::AliasFilter, notification::Notification, spinner::Spinner, }; @@ -38,6 +39,7 @@ pub enum FocusedBlock { Help, PassKeyConfirmation, SetDeviceAliasBox, + AliasFilterPopup } #[derive(Debug, Clone, Copy, PartialEq)] @@ -52,6 +54,7 @@ pub struct App { pub session: Arc, pub agent: AgentHandle, pub help: Help, + pub alias_filter: AliasFilter, pub spinner: Spinner, pub notifications: Vec, pub controllers: Vec, @@ -110,7 +113,8 @@ impl App { running: true, session, agent: handle, - help: Help::new(config), + help: Help::new(config.clone()), + alias_filter: AliasFilter::new(config.clone()), spinner: Spinner::default(), notifications: Vec::new(), controllers, @@ -613,6 +617,10 @@ impl App { let rows: Vec = selected_controller .new_devices .iter() + .filter(|d| match &self.alias_filter.filter { + Some(filter) => { d.alias.contains(filter) } + None => { true } + }) .map(|d| { Row::new(vec![d.addr.to_string(), { if let Some(icon) = &d.icon { diff --git a/src/handler.rs b/src/handler.rs index cd34d10..04c4aa2 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -72,9 +72,13 @@ pub async fn handle_key_events( app.focused_block = FocusedBlock::Help; } + KeyCode::Char('/') => { + app.focused_block = FocusedBlock::AliasFilterPopup; + } + // Discard help popup KeyCode::Esc => { - if app.focused_block == FocusedBlock::Help { + if app.focused_block == FocusedBlock::Help || app.focused_block == FocusedBlock::AliasFilterPopup { app.focused_block = FocusedBlock::Adapter; } } @@ -750,6 +754,8 @@ pub async fn handle_key_events( } } } + } else if KeyCode::Char('/') == key_event.code { + app.focused_block = FocusedBlock::AliasFilterPopup; } } diff --git a/src/lib.rs b/src/lib.rs index 11f4851..e43ec4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,8 @@ pub mod spinner; pub mod help; +pub mod alias_filter; + pub mod config; pub mod rfkill; diff --git a/src/ui.rs b/src/ui.rs index 26d0662..dc1c635 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -8,6 +8,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { match app.focused_block { FocusedBlock::Help => app.help.render(frame, app.color_mode), + FocusedBlock::AliasFilterPopup => app.alias_filter.render(frame, app.color_mode), FocusedBlock::SetDeviceAliasBox => app.render_set_alias(frame), _ => {} } From 8e0cffece3853e0c50fbf4b6f8c4494fcc875698 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 17:16:43 -0500 Subject: [PATCH 02/14] accept user input --- Cargo.toml | 2 +- src/alias_filter.rs | 53 ++++++++++++++++++++++++++++++++++----------- src/handler.rs | 23 +++++++++++++++++--- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d0eda29..e06639a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ toml = "0.8" serde = { version = "1", features = ["derive"] } clap = { version = "4", features = ["derive", "cargo"] } terminal-light = "1" -tui-input = "0.11" +tui-input = "0.11.1" [profile.release] strip = true diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 415cf63..99e0eea 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -1,17 +1,18 @@ use std::sync::Arc; +use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ - layout::{Alignment, Constraint, Direction, Layout, Margin}, - style::{Color, Style, Stylize}, - widgets::{Row, Table, TableState}, - Frame, + layout::{Alignment, Constraint, Direction, Layout, Margin}, style::{Color, Style, Stylize}, text::ToText, widgets::{Block, BorderType, Borders, Padding, Row, Table, TableState}, Frame }; +use tui_input::{backend::crossterm::EventHandler, Input, InputRequest}; use crate::{app::ColorMode, config::Config}; + #[derive(Debug)] pub struct AliasFilter{ pub filter: Option, + input: Input, state: TableState, } @@ -20,18 +21,28 @@ impl AliasFilter { let mut state = TableState::new().with_offset(0); state.select(Some(0)); + let input = Input::new("".to_string()); + Self { state, + input, filter: None } } - pub fn render(&mut self, frame: &mut Frame, color_mode: ColorMode) { - let widths = [ - Constraint::Length(20), - Constraint::Length(20), - ]; + pub fn insert_char(&mut self, c: char) { + let req = InputRequest::InsertChar(c); + self.input.handle(req); + self.filter = Some(self.input.value().to_string()); + } + pub fn delete_char(&mut self) { + let req = InputRequest::DeletePrevChar; + self.input.handle(req); + self.filter = Some(self.input.value().to_string()); + } + + pub fn render(&mut self, frame: &mut Frame, color_mode: ColorMode) { let layout = Layout::default() .direction(Direction::Vertical) .constraints([ @@ -46,15 +57,31 @@ impl AliasFilter { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Length(70), + Constraint::Length(60), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; - let row = Row::new(vec!["asdf"]); - let table = Table::new([row], widths); + let text = match &self.filter { + Some(f) => f.to_string(), + None => "".to_string() + }; + + let row = Row::new(vec![text]); + + let table = Table::new([row], [Constraint::Length(20)]).block( + Block::default() + .padding(Padding::uniform(2)) + .title(" Filter Device Names ") + .title_style(Style::default().bold().fg(Color::Green)) + .title_alignment(Alignment::Center) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::Thick) + .border_style(Style::default().fg(Color::Green)), + ); - frame.render_widget(table, block); + frame.render_stateful_widget(table, block, &mut self.state); } } diff --git a/src/handler.rs b/src/handler.rs index 04c4aa2..a6ba1d0 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -56,6 +56,25 @@ pub async fn handle_key_events( .handle_event(&crossterm::event::Event::Key(key_event)); } }, + + FocusedBlock::AliasFilterPopup => { + match key_event.code { + KeyCode::Backspace => { + app.alias_filter.delete_char(); + } + + KeyCode::Char(c) => { + app.alias_filter.insert_char(c); + } + + KeyCode::Esc => { + app.focused_block = FocusedBlock::NewDevices; + } + + _=> {}, + } + }, + _ => { match key_event.code { // Exit the app @@ -78,7 +97,7 @@ pub async fn handle_key_events( // Discard help popup KeyCode::Esc => { - if app.focused_block == FocusedBlock::Help || app.focused_block == FocusedBlock::AliasFilterPopup { + if app.focused_block == FocusedBlock::Help { app.focused_block = FocusedBlock::Adapter; } } @@ -754,8 +773,6 @@ pub async fn handle_key_events( } } } - } else if KeyCode::Char('/') == key_event.code { - app.focused_block = FocusedBlock::AliasFilterPopup; } } From 7cbb4500d09063de90597af244a8057cdae4e851 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 18:08:53 -0500 Subject: [PATCH 03/14] show blinking cursor --- src/alias_filter.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 99e0eea..613fae1 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::time::Instant; use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ @@ -14,6 +15,7 @@ pub struct AliasFilter{ pub filter: Option, input: Input, state: TableState, + start: Instant } impl AliasFilter { @@ -22,11 +24,14 @@ impl AliasFilter { state.select(Some(0)); let input = Input::new("".to_string()); + + let start = Instant::now(); Self { state, input, - filter: None + filter: None, + start, } } @@ -63,11 +68,13 @@ impl AliasFilter { .flex(ratatui::layout::Flex::SpaceBetween) .split(layout[1])[1]; - let text = match &self.filter { + let mut text = match &self.filter { Some(f) => f.to_string(), None => "".to_string() }; + self.insert_cursor(&mut text); + let row = Row::new(vec![text]); let table = Table::new([row], [Constraint::Length(20)]).block( @@ -84,4 +91,16 @@ impl AliasFilter { frame.render_stateful_widget(table, block, &mut self.state); } + + fn insert_cursor(&self, s: &mut String) { + let time = Instant::now() + .duration_since(self.start) + .as_secs(); + + let c = + if time % 2 == 0 { '_' } + else { ' ' }; + + s.insert(self.input.cursor(), c); + } } From ff128c2e8eaba004ec8a91ee8ffb3c4e0eedf6a5 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 18:18:46 -0500 Subject: [PATCH 04/14] fix dimensions --- src/alias_filter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 613fae1..0dab6eb 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -52,7 +52,7 @@ impl AliasFilter { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(28), + Constraint::Length(5), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -62,7 +62,7 @@ impl AliasFilter { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Length(60), + Constraint::Min(80), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) @@ -79,7 +79,7 @@ impl AliasFilter { let table = Table::new([row], [Constraint::Length(20)]).block( Block::default() - .padding(Padding::uniform(2)) + .padding(Padding::uniform(1)) .title(" Filter Device Names ") .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) From ad81e604c55e5f21cad7ce07d2bbd5f92a84046d Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 18:21:08 -0500 Subject: [PATCH 05/14] cleanup --- src/alias_filter.rs | 14 ++++++++------ src/ui.rs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 0dab6eb..ef952bb 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -1,13 +1,15 @@ use std::sync::Arc; use std::time::Instant; -use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ - layout::{Alignment, Constraint, Direction, Layout, Margin}, style::{Color, Style, Stylize}, text::ToText, widgets::{Block, BorderType, Borders, Padding, Row, Table, TableState}, Frame + layout::{Alignment, Constraint, Direction, Layout}, + style::{Color, Style, Stylize}, + widgets::{Block, BorderType, Borders, Padding, Row, Table, TableState}, + Frame }; -use tui_input::{backend::crossterm::EventHandler, Input, InputRequest}; +use tui_input::{Input, InputRequest}; -use crate::{app::ColorMode, config::Config}; +use crate::config::Config; #[derive(Debug)] @@ -19,7 +21,7 @@ pub struct AliasFilter{ } impl AliasFilter { - pub fn new(config: Arc) -> Self { + pub fn new(_config: Arc) -> Self { let mut state = TableState::new().with_offset(0); state.select(Some(0)); @@ -47,7 +49,7 @@ impl AliasFilter { self.filter = Some(self.input.value().to_string()); } - pub fn render(&mut self, frame: &mut Frame, color_mode: ColorMode) { + pub fn render(&mut self, frame: &mut Frame) { let layout = Layout::default() .direction(Direction::Vertical) .constraints([ diff --git a/src/ui.rs b/src/ui.rs index dc1c635..2dcc6aa 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -8,7 +8,7 @@ pub fn render(app: &mut App, frame: &mut Frame) { match app.focused_block { FocusedBlock::Help => app.help.render(frame, app.color_mode), - FocusedBlock::AliasFilterPopup => app.alias_filter.render(frame, app.color_mode), + FocusedBlock::AliasFilterPopup => app.alias_filter.render(frame), FocusedBlock::SetDeviceAliasBox => app.render_set_alias(frame), _ => {} } From dcd311b7c594c84045601290b2a633f6dea56ee9 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 19:24:07 -0500 Subject: [PATCH 06/14] add Enter to unfocus bindings --- src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler.rs b/src/handler.rs index a6ba1d0..1b134e2 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -67,7 +67,7 @@ pub async fn handle_key_events( app.alias_filter.insert_char(c); } - KeyCode::Esc => { + KeyCode::Esc | KeyCode::Enter => { app.focused_block = FocusedBlock::NewDevices; } From 08133743971e502976a7bf21b572ad2d6c9474f3 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 19:36:52 -0500 Subject: [PATCH 07/14] add background clear --- src/alias_filter.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index ef952bb..0b9738a 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -4,7 +4,7 @@ use std::time::Instant; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout}, style::{Color, Style, Stylize}, - widgets::{Block, BorderType, Borders, Padding, Row, Table, TableState}, + widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table, TableState}, Frame }; use tui_input::{Input, InputRequest}; @@ -91,6 +91,7 @@ impl AliasFilter { .border_style(Style::default().fg(Color::Green)), ); + frame.render_widget(Clear, block); frame.render_stateful_widget(table, block, &mut self.state); } From 78a19164998a189a5a76c5031b97da9a14e2cc55 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sat, 22 Feb 2025 19:40:21 -0500 Subject: [PATCH 08/14] update help menu --- src/help.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/help.rs b/src/help.rs index 5c0455a..6e49a90 100644 --- a/src/help.rs +++ b/src/help.rs @@ -99,6 +99,10 @@ impl Help { Cell::from(config.new_device.pair.to_string()).bold(), "Pair the device", ), + ( + Cell::from("/").bold(), + "Filter device names" + ) ], } } @@ -137,7 +141,7 @@ impl Help { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(28), + Constraint::Length(29), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) From 3888a6ebec5d26fcc3980297a56cf3de45ead691 Mon Sep 17 00:00:00 2001 From: Mike Interlandi Date: Sun, 23 Feb 2025 11:22:00 -0500 Subject: [PATCH 09/14] format --- src/alias_filter.rs | 25 ++++++++++--------------- src/app.rs | 8 ++++---- src/handler.rs | 24 +++++++++++------------- src/help.rs | 5 +---- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 0b9738a..2c58856 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -5,19 +5,18 @@ use ratatui::{ layout::{Alignment, Constraint, Direction, Layout}, style::{Color, Style, Stylize}, widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table, TableState}, - Frame + Frame, }; use tui_input::{Input, InputRequest}; use crate::config::Config; - #[derive(Debug)] -pub struct AliasFilter{ +pub struct AliasFilter { pub filter: Option, input: Input, state: TableState, - start: Instant + start: Instant, } impl AliasFilter { @@ -26,7 +25,7 @@ impl AliasFilter { state.select(Some(0)); let input = Input::new("".to_string()); - + let start = Instant::now(); Self { @@ -59,7 +58,7 @@ impl AliasFilter { ]) .flex(ratatui::layout::Flex::SpaceBetween) .split(frame.area()); - + let block = Layout::default() .direction(Direction::Horizontal) .constraints([ @@ -72,7 +71,7 @@ impl AliasFilter { let mut text = match &self.filter { Some(f) => f.to_string(), - None => "".to_string() + None => "".to_string(), }; self.insert_cursor(&mut text); @@ -96,14 +95,10 @@ impl AliasFilter { } fn insert_cursor(&self, s: &mut String) { - let time = Instant::now() - .duration_since(self.start) - .as_secs(); - - let c = - if time % 2 == 0 { '_' } - else { ' ' }; - + let time = Instant::now().duration_since(self.start).as_secs(); + + let c = if time % 2 == 0 { '_' } else { ' ' }; + s.insert(self.input.cursor(), c); } } diff --git a/src/app.rs b/src/app.rs index 13e9c2e..84563a6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -16,11 +16,11 @@ use ratatui::{ use tui_input::Input; use crate::{ + alias_filter::AliasFilter, bluetooth::{request_confirmation, Controller}, config::Config, confirmation::PairingConfirmation, help::Help, - alias_filter::AliasFilter, notification::Notification, spinner::Spinner, }; @@ -39,7 +39,7 @@ pub enum FocusedBlock { Help, PassKeyConfirmation, SetDeviceAliasBox, - AliasFilterPopup + AliasFilterPopup, } #[derive(Debug, Clone, Copy, PartialEq)] @@ -618,8 +618,8 @@ impl App { .new_devices .iter() .filter(|d| match &self.alias_filter.filter { - Some(filter) => { d.alias.contains(filter) } - None => { true } + Some(filter) => d.alias.contains(filter), + None => true, }) .map(|d| { Row::new(vec![d.addr.to_string(), { diff --git a/src/handler.rs b/src/handler.rs index 1b134e2..b4b17d8 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -57,22 +57,20 @@ pub async fn handle_key_events( } }, - FocusedBlock::AliasFilterPopup => { - match key_event.code { - KeyCode::Backspace => { - app.alias_filter.delete_char(); - } + FocusedBlock::AliasFilterPopup => match key_event.code { + KeyCode::Backspace => { + app.alias_filter.delete_char(); + } - KeyCode::Char(c) => { - app.alias_filter.insert_char(c); - } - - KeyCode::Esc | KeyCode::Enter => { - app.focused_block = FocusedBlock::NewDevices; - } + KeyCode::Char(c) => { + app.alias_filter.insert_char(c); + } - _=> {}, + KeyCode::Esc | KeyCode::Enter => { + app.focused_block = FocusedBlock::NewDevices; } + + _ => {} }, _ => { diff --git a/src/help.rs b/src/help.rs index 6e49a90..a68501f 100644 --- a/src/help.rs +++ b/src/help.rs @@ -99,10 +99,7 @@ impl Help { Cell::from(config.new_device.pair.to_string()).bold(), "Pair the device", ), - ( - Cell::from("/").bold(), - "Filter device names" - ) + (Cell::from("/").bold(), "Filter device names"), ], } } From 5d57089d41a7b476bb20cec4a39e1653c7147052 Mon Sep 17 00:00:00 2001 From: Mike Interlandi <43190101+Minterl@users.noreply.github.com> Date: Sat, 10 May 2025 21:48:25 -0400 Subject: [PATCH 10/14] only show filter popup if discovered devices is not empty --- src/handler.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/handler.rs b/src/handler.rs index b4b17d8..04fa83b 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -90,7 +90,12 @@ pub async fn handle_key_events( } KeyCode::Char('/') => { - app.focused_block = FocusedBlock::AliasFilterPopup; + if let Some(selected_controller_index) = app.controller_state.selected() { + let selected_controller = &app.controllers[selected_controller_index]; + if !selected_controller.new_devices.is_empty() { + app.focused_block = FocusedBlock::AliasFilterPopup; + } + } } // Discard help popup From 08cc098dcc5f1fb23094516eb1e47a4e649240d0 Mon Sep 17 00:00:00 2001 From: Mike Interlandi <43190101+Minterl@users.noreply.github.com> Date: Sat, 10 May 2025 22:37:31 -0400 Subject: [PATCH 11/14] highlight matched alias pattern --- src/app.rs | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/app.rs b/src/app.rs index 84563a6..9b6f50c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,7 +6,7 @@ use futures::FutureExt; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Margin}, style::{Color, Modifier, Style, Stylize}, - text::{Line, Span}, + text::{Span, Line}, widgets::{ Block, BorderType, Borders, Cell, Clear, Padding, Paragraph, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, @@ -618,17 +618,35 @@ impl App { .new_devices .iter() .filter(|d| match &self.alias_filter.filter { - Some(filter) => d.alias.contains(filter), + Some(pattern) => d.alias.contains(pattern), None => true, }) .map(|d| { - Row::new(vec![d.addr.to_string(), { - if let Some(icon) = &d.icon { - format!("{} {}", icon, &d.alias) - } else { - d.alias.to_owned() - } - }]) + let device_name = match &self.alias_filter.filter { + Some(pattern) => { + let match_idxs: Vec = d.alias.match_indices(pattern) + .map(|(i, _)| i) + .collect(); + + let highlighted = Style::default() + .bg(Color::LightBlue); + + Line::from(vec![ + Span::raw(&d.alias[0..match_idxs[0]]), + Span::styled(pattern, highlighted), + Span::raw(&d.alias[match_idxs[0] + pattern.len()..]), + ]) + }, + None => Line::raw(d.alias.clone()), + }; + + let icon = Line::styled(if let Some(icon) = &d.icon { + format!("{} {}", icon, &d.alias) + } else { + d.alias.to_owned() + }, Style::default()); + + Row::new(vec![device_name, icon]) }) .collect(); let rows_len = rows.len(); From c9ce5d1f4e60646af102c60af483228fda9006a8 Mon Sep 17 00:00:00 2001 From: Mike Interlandi <43190101+Minterl@users.noreply.github.com> Date: Sat, 10 May 2025 22:53:39 -0400 Subject: [PATCH 12/14] move alias filter popup to bottom --- src/alias_filter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/alias_filter.rs b/src/alias_filter.rs index 2c58856..52a14e0 100644 --- a/src/alias_filter.rs +++ b/src/alias_filter.rs @@ -53,8 +53,8 @@ impl AliasFilter { .direction(Direction::Vertical) .constraints([ Constraint::Fill(1), - Constraint::Length(5), Constraint::Fill(1), + Constraint::Length(5), ]) .flex(ratatui::layout::Flex::SpaceBetween) .split(frame.area()); @@ -63,11 +63,11 @@ impl AliasFilter { .direction(Direction::Horizontal) .constraints([ Constraint::Fill(1), - Constraint::Min(80), + Constraint::Min(100), Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) - .split(layout[1])[1]; + .split(layout[2])[1]; let mut text = match &self.filter { Some(f) => f.to_string(), From 69719286fecf231b50b8a57fdbfaeae23118fffd Mon Sep 17 00:00:00 2001 From: Mike Interlandi <43190101+Minterl@users.noreply.github.com> Date: Sat, 10 May 2025 22:55:44 -0400 Subject: [PATCH 13/14] format --- src/app.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9b6f50c..84da886 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,7 +6,7 @@ use futures::FutureExt; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Margin}, style::{Color, Modifier, Style, Stylize}, - text::{Span, Line}, + text::{Line, Span}, widgets::{ Block, BorderType, Borders, Cell, Clear, Padding, Paragraph, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, @@ -624,28 +624,29 @@ impl App { .map(|d| { let device_name = match &self.alias_filter.filter { Some(pattern) => { - let match_idxs: Vec = d.alias.match_indices(pattern) - .map(|(i, _)| i) - .collect(); + let match_idxs: Vec = + d.alias.match_indices(pattern).map(|(i, _)| i).collect(); - let highlighted = Style::default() - .bg(Color::LightBlue); + let highlighted = Style::default().bg(Color::LightBlue); Line::from(vec![ Span::raw(&d.alias[0..match_idxs[0]]), Span::styled(pattern, highlighted), Span::raw(&d.alias[match_idxs[0] + pattern.len()..]), ]) - }, + } None => Line::raw(d.alias.clone()), }; - let icon = Line::styled(if let Some(icon) = &d.icon { - format!("{} {}", icon, &d.alias) - } else { - d.alias.to_owned() - }, Style::default()); - + let icon = Line::styled( + if let Some(icon) = &d.icon { + format!("{} {}", icon, &d.alias) + } else { + d.alias.to_owned() + }, + Style::default(), + ); + Row::new(vec![device_name, icon]) }) .collect(); From 74ddaa8bb931ce23b6a9276257fd55d327b2289f Mon Sep 17 00:00:00 2001 From: Mike Interlandi <43190101+Minterl@users.noreply.github.com> Date: Mon, 12 May 2025 11:58:55 -0400 Subject: [PATCH 14/14] discard alias filter on esc --- src/handler.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/handler.rs b/src/handler.rs index 04fa83b..44b0be7 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -66,7 +66,12 @@ pub async fn handle_key_events( app.alias_filter.insert_char(c); } - KeyCode::Esc | KeyCode::Enter => { + KeyCode::Enter => { + app.focused_block = FocusedBlock::NewDevices; + } + + KeyCode::Esc => { + app.alias_filter.filter = None; app.focused_block = FocusedBlock::NewDevices; }