Add UX enhancements: carousel, filter chips, command palette, and more

- Replace featured section Stack with AdwCarousel + indicator dots
- Convert category grid to horizontal scrollable filter chips
- Add grid/list view toggle for catalog with compact row layout
- Add quick launch button on library list rows
- Add stale catalog banner when data is older than 7 days
- Add command palette (Ctrl+K) for quick app search and launch
- Show specific app names in update notifications
- Add per-app auto-update toggle (skip updates switch)
- Add keyboard shortcut hints to button tooltips
- Add source trust badges (AppImageHub/Community) on catalog tiles
- Add undo-based uninstall with toast and record restoration
- Add type-to-search in library view
- Use human-readable catalog source labels
- Show Launch button for installed apps in catalog detail
- Replace external browser link with inline AppImage explainer dialog
This commit is contained in:
lashman
2026-03-01 00:39:43 +02:00
parent 4b939f044a
commit d11546efc6
25 changed files with 1711 additions and 481 deletions

View File

@@ -25,7 +25,7 @@ pub fn build_updates_view(db: &Rc<Database>) -> adw::ToolbarView {
// Check Now button
let check_btn = gtk::Button::builder()
.icon_name("view-refresh-symbolic")
.tooltip_text(&i18n("Check for updates"))
.tooltip_text(&i18n("Check for updates (Ctrl+U)"))
.build();
header.pack_end(&check_btn);
@@ -86,6 +86,18 @@ pub fn build_updates_view(db: &Rc<Database>) -> adw::ToolbarView {
.build();
updates_content.append(&last_checked_label);
// "What will happen" explanation
let explanation = gtk::Label::builder()
.label(&i18n("Each app will be downloaded fresh. Your settings and data are kept. You can choose to keep old versions as backup in Preferences."))
.css_classes(["caption", "dim-label"])
.wrap(true)
.xalign(0.0)
.margin_start(18)
.margin_end(18)
.margin_top(6)
.build();
updates_content.append(&explanation);
let clamp = adw::Clamp::builder()
.maximum_size(800)
.tightening_threshold(600)
@@ -255,10 +267,15 @@ fn populate_update_list(state: &Rc<UpdatesState>) {
.activatable(false)
.build();
// Show version info: current -> latest
// Show version info: current -> latest (with size if available)
let current = record.app_version.as_deref().unwrap_or("unknown");
let latest = record.latest_version.as_deref().unwrap_or("unknown");
row.set_subtitle(&format!("{} -> {}", current, latest));
let subtitle = if record.size_bytes > 0 {
format!("{} -> {} ({})", current, latest, widgets::format_size(record.size_bytes))
} else {
format!("{} -> {}", current, latest)
};
row.set_subtitle(&subtitle);
// App icon
let icon = widgets::app_icon(record.icon_path.as_deref(), name, 32);