From 95bfede1e085ea632ce4ad81f1e3dffa8eec7de0 Mon Sep 17 00:00:00 2001 From: electria Date: Wed, 26 Aug 2026 09:42:10 -0700 Subject: [PATCH] feat: basic entry selection --- src/app/mod.rs | 78 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 57e171e..7de9448 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -22,6 +22,10 @@ enum Message { OpenDb, DbOpened(Result), + SelectionChanged(usize), + SelectionUp, + SelectionDown, + FocusNext, FocusPrevious, } @@ -33,6 +37,9 @@ struct State { db: Option, + db_view: Vec, + selected_entry: usize, + password: String, is_secure: bool, @@ -88,6 +95,10 @@ impl State { match result { Ok(db) => { self.error = "".into(); + self.db_view = db + .iter_all_entries() + .map(|entry_ref| entry_ref.clone()) + .collect(); self.db = Some(db) } Err(e) => self.error = e.into(), @@ -95,6 +106,20 @@ impl State { self.status = "".into(); } + Message::SelectionChanged(val) => self.selected_entry = val, + Message::SelectionUp => { + self.selected_entry = + dbg!((self.selected_entry.wrapping_sub(1)).clamp(0, self.db_view.len() - 1)) + } + Message::SelectionDown => { + self.selected_entry = if self.selected_entry == self.db_view.len() - 1 { + // essentially do a wrapping_add up to the highest index in the view + 0 + } else { + dbg!((self.selected_entry.wrapping_add(1)).clamp(0, self.db_view.len() - 1)) + } + } + Message::FocusNext => return widget::operation::focus_next(), Message::FocusPrevious => return widget::operation::focus_previous(), } @@ -102,21 +127,39 @@ impl State { Task::none() } fn view(&self) -> Element<'_> { - let main = if let Some(db) = self.db.as_ref() { - widget::scrollable(widget::row([ - widget::column(db.iter_all_entries().map(|entry| { - widget::text(entry.get_title().unwrap_or_default().to_string()).into() + let main = if !self.db_view.is_empty() { + widget::scrollable( + widget::grid(self.db_view.iter().enumerate().flat_map(|(i, entry)| { + [ + widget::button(widget::text(entry.get_title().unwrap_or_default())) + .style(if i == self.selected_entry { + widget::button::primary + } else { + widget::button::subtle + }) + .on_press(Message::SelectionChanged(i)) + .into(), + widget::button(widget::text(entry.get_username().unwrap_or_default())) + .style(if i == self.selected_entry { + widget::button::primary + } else { + widget::button::subtle + }) + .on_press(Message::SelectionChanged(i)) + .into(), + widget::button(widget::text(entry.get_url().unwrap_or_default())) + .style(if i == self.selected_entry { + widget::button::primary + } else { + widget::button::subtle + }) + .on_press(Message::SelectionChanged(i)) + .into(), + ] })) - .into(), - widget::column(db.iter_all_entries().map(|entry| { - widget::text(entry.get_username().unwrap_or_default().to_string()).into() - })) - .into(), - widget::column(db.iter_all_entries().map(|entry| { - widget::text(entry.get_url().unwrap_or_default().to_string()).into() - })) - .into(), - ])) + .height(Length::Shrink) + .columns(3), + ) .into() } else { widget::container( @@ -195,6 +238,13 @@ impl State { } Key::Character("p" | "f" | "i") if !db_loaded => Some(Message::PickDb), + Key::Character("k") | Key::Named(key::Named::ArrowUp) => { + Some(Message::SelectionUp) + } + Key::Character("j") | Key::Named(key::Named::ArrowDown) => { + Some(Message::SelectionDown) + } + // ignore unused keys _ => None, }