Add complete keyboard navigation.

This commit is contained in:
2026-08-07 17:00:49 +02:00
parent 87a161058f
commit 5709c80209
38 changed files with 1943 additions and 355 deletions

View File

@@ -1,7 +1,9 @@
use crate::components::keyboard;
use iced::widget::button;
use std::path::{Path, PathBuf};
use iced::widget::text::Shaping;
use iced::widget::{Space, button, column, container, image, row, scrollable, text, text_input};
use iced::widget::{Space, column, container, image, row, scrollable, text, text_input};
use iced::{Background, Border, Color, Element, Length, Theme};
use bds_core::i18n::UiLocale;
@@ -301,10 +303,10 @@ fn row_delete_style(_theme: &Theme, status: button::Status) -> button::Style {
/// Per sidebar_views.allium *ListItemEntry RowLayout: right-aligned, visible
/// only on row hover, routed to the row's delete message.
fn with_row_delete(
open_button: iced::widget::Button<'static, Message>,
open_button: impl Into<Element<'static, Message>>,
on_delete: Message,
) -> Element<'static, Message> {
let delete_button = button(
let delete_button = keyboard::button(
text("\u{2715}") // ✕
.size(11)
.shaping(Shaping::Advanced),
@@ -313,7 +315,7 @@ fn with_row_delete(
.padding([2, 6])
.style(row_delete_style);
iced::widget::hover(
open_button,
open_button.into(),
container(delete_button)
.width(Length::Fill)
.height(Length::Fill)
@@ -362,7 +364,7 @@ fn calendar_widget(
} else {
calendar_style
};
let year_btn = button(text(label).size(11).shaping(Shaping::Advanced))
let year_btn = keyboard::button(text(label).size(11).shaping(Shaping::Advanced))
.on_press(if year_selected {
on_year_clone(None)
} else {
@@ -384,7 +386,7 @@ fn calendar_widget(
} else {
calendar_style
};
let month_btn = button(text(label).size(10).shaping(Shaping::Advanced))
let month_btn = keyboard::button(text(label).size(10).shaping(Shaping::Advanced))
.on_press(if month_selected {
on_month_clone(None)
} else {
@@ -428,7 +430,7 @@ fn chip_selector(
} else {
chip_style
};
let chip = button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
let chip = keyboard::button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
.on_press(on_toggle_clone(tag_clone))
.padding([2, 6])
.style(style_fn);
@@ -476,7 +478,7 @@ fn single_select_chip_selector(
};
let value = value.clone();
let on_toggle = on_toggle.clone();
button(text(display.clone()).size(10).shaping(Shaping::Advanced))
keyboard::button(text(display.clone()).size(10).shaping(Shaping::Advanced))
.on_press(if is_selected {
on_toggle(None)
} else {
@@ -599,7 +601,7 @@ fn post_filter_panel(
// Clear all filters button
if filter.has_active_filters() {
sections.push(
button(
keyboard::button(
text(t(locale, "sidebar.filter.clearAll"))
.size(10)
.shaping(Shaping::Advanced),
@@ -645,7 +647,7 @@ fn media_filter_panel(filter: &MediaFilter, locale: UiLocale) -> Element<'static
if filter.has_active_filters() {
sections.push(
button(
keyboard::button(
text(t(locale, "sidebar.filter.clearAll"))
.size(10)
.shaping(Shaping::Advanced),
@@ -711,7 +713,7 @@ pub fn view(
row![
header,
Space::with_width(Length::Fill),
button(
keyboard::button(
text(t(locale, "common.add"))
.size(11)
.shaping(Shaping::Advanced)
@@ -752,10 +754,11 @@ pub fn view(
} else {
"\u{25BC}" // ▼ toggle icon
};
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
.on_press(Message::TogglePostFilterPanel)
.padding([4, 6])
.style(toggle_style);
let filter_toggle =
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
.on_press(Message::TogglePostFilterPanel)
.padding([4, 6])
.style(toggle_style);
top_items.push(row![search, filter_toggle].spacing(4).into());
@@ -817,7 +820,7 @@ pub fn view(
} else {
item_style
};
button(
keyboard::button(
container(column![label_text, date_text].spacing(1))
.width(Length::Fill)
.clip(true),
@@ -901,10 +904,11 @@ pub fn view(
} else {
"\u{25BC}" // ▼ toggle icon
};
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
.on_press(Message::ToggleMediaFilterPanel)
.padding([4, 6])
.style(toggle_style);
let filter_toggle =
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
.on_press(Message::ToggleMediaFilterPanel)
.padding([4, 6])
.style(toggle_style);
top_items.push(row![search, filter_toggle].spacing(4).into());
@@ -995,7 +999,7 @@ pub fn view(
.spacing(8)
.align_y(iced::Alignment::Center);
button(container(content).width(Length::Fill).clip(true))
keyboard::button(container(content).width(Length::Fill).clip(true))
.on_press(Message::OpenTab(Tab {
id: m.id.clone(),
tab_type: TabType::Media,
@@ -1046,7 +1050,7 @@ pub fn view(
} else {
item_style
};
let open_button = button(
let open_button = keyboard::button(
container(column![label_text, date_text].spacing(1))
.width(Length::Fill)
.clip(true),
@@ -1097,7 +1101,7 @@ pub fn view(
} else {
item_style
};
let open_button = button(
let open_button = keyboard::button(
container(column![label_text, date_text].spacing(1))
.width(Length::Fill)
.clip(true),
@@ -1145,7 +1149,7 @@ pub fn view(
.size(10)
.shaping(Shaping::Advanced)
.color(muted);
let open_button = button(
let open_button = keyboard::button(
container(
column![
text(definition_name).size(12).shaping(Shaping::Advanced),
@@ -1197,7 +1201,7 @@ pub fn view(
.size(10)
.shaping(Shaping::Advanced)
.color(muted);
let open_button = button(
let open_button = keyboard::button(
container(
column![
text(conversation.title.clone())
@@ -1257,7 +1261,7 @@ pub fn view(
is_dirty: false,
})
};
button(container(label_text).width(Length::Fill))
keyboard::button(container(label_text).width(Length::Fill))
.on_press(msg)
.padding([5, 8])
.width(Length::Fill)
@@ -1291,7 +1295,7 @@ pub fn view(
.map(|(key, section)| {
let label = t(locale, key);
let label_text = text(label).size(12).shaping(Shaping::Advanced);
button(container(label_text).width(Length::Fill))
keyboard::button(container(label_text).width(Length::Fill))
.on_press(Message::OpenTagsSection(*section))
.padding([5, 8])
.width(Length::Fill)