feat: basic entry selection

This commit is contained in:
electria 2026-08-26 09:42:10 -07:00
commit 95bfede1e0
Signed by: electria
SSH key fingerprint: SHA256:8LlB3ucPbBHqozqkhsNbaV5oG3SlzzqUj8FZDL6IPQs

View file

@ -22,6 +22,10 @@ enum Message {
OpenDb, OpenDb,
DbOpened(Result<keepass::Database, String>), DbOpened(Result<keepass::Database, String>),
SelectionChanged(usize),
SelectionUp,
SelectionDown,
FocusNext, FocusNext,
FocusPrevious, FocusPrevious,
} }
@ -33,6 +37,9 @@ struct State {
db: Option<keepass::Database>, db: Option<keepass::Database>,
db_view: Vec<keepass::db::Entry>,
selected_entry: usize,
password: String, password: String,
is_secure: bool, is_secure: bool,
@ -88,6 +95,10 @@ impl State {
match result { match result {
Ok(db) => { Ok(db) => {
self.error = "".into(); self.error = "".into();
self.db_view = db
.iter_all_entries()
.map(|entry_ref| entry_ref.clone())
.collect();
self.db = Some(db) self.db = Some(db)
} }
Err(e) => self.error = e.into(), Err(e) => self.error = e.into(),
@ -95,6 +106,20 @@ impl State {
self.status = "".into(); 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::FocusNext => return widget::operation::focus_next(),
Message::FocusPrevious => return widget::operation::focus_previous(), Message::FocusPrevious => return widget::operation::focus_previous(),
} }
@ -102,21 +127,39 @@ impl State {
Task::none() Task::none()
} }
fn view(&self) -> Element<'_> { fn view(&self) -> Element<'_> {
let main = if let Some(db) = self.db.as_ref() { let main = if !self.db_view.is_empty() {
widget::scrollable(widget::row([ widget::scrollable(
widget::column(db.iter_all_entries().map(|entry| { widget::grid(self.db_view.iter().enumerate().flat_map(|(i, entry)| {
widget::text(entry.get_title().unwrap_or_default().to_string()).into() [
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(), .height(Length::Shrink)
widget::column(db.iter_all_entries().map(|entry| { .columns(3),
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(),
]))
.into() .into()
} else { } else {
widget::container( widget::container(
@ -195,6 +238,13 @@ impl State {
} }
Key::Character("p" | "f" | "i") if !db_loaded => Some(Message::PickDb), 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 // ignore unused keys
_ => None, _ => None,
} }