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,
DbOpened(Result<keepass::Database, String>),
SelectionChanged(usize),
SelectionUp,
SelectionDown,
FocusNext,
FocusPrevious,
}
@ -33,6 +37,9 @@ struct State {
db: Option<keepass::Database>,
db_view: Vec<keepass::db::Entry>,
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::column(db.iter_all_entries().map(|entry| {
widget::text(entry.get_username().unwrap_or_default().to_string()).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::column(db.iter_all_entries().map(|entry| {
widget::text(entry.get_url().unwrap_or_default().to_string()).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(),
]))
]
}))
.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,
}