11 KiB
11 KiB
Driftwood UI/UX Overhaul Design
Context
Driftwood is a GTK4/libadwaita AppImage manager. The current UI is functional but visually plain - cards look like basic boxes, the list view resembles a settings page, and the detail view is a wall of ActionRows with no hierarchy. This design overhauls all three views plus adds a right-click context menu to make Driftwood feel like a first-class GNOME app.
Design Principles
- Use libadwaita's built-in style classes wherever possible instead of custom CSS
- Follow GNOME HIG spacing (6px grid, 12px padding baseline, 14px grid gaps)
- Only show the most important information at each level (card -> list -> detail = progressive disclosure)
- Actions belong in context menus and detail headers, not crammed into cards
1. Card View (Grid)
Current State
- 180px wide cards with 64px icons
- All badges shown (Wayland, FUSE, Update, Integration)
- Custom
.app-cardCSS duplicating libadwaita's.cardbehavior - FlowBox allows up to 6 columns (cards get too small)
New Design
+----------------------------------+
| |
| [72px icon] |
| (icon-dropshadow) |
| |
| App Name |
| (.title-3) |
| |
| 1.2.3 - 45 MiB |
| (.caption .dimmed .numeric) |
| |
| [single most important badge] |
+----------------------------------+
200px wide, 14px internal padding
Changes
- Card width: 200px (from 180px) for better breathing room
- Icon size: 72px (from 64px) with
.icon-dropshadowclass - App name:
.title-3(from.heading) for more visual weight - Version + size on one combined line using
.caption .dimmed .numeric - Single badge only - show the most important status using priority: Update > FUSE issue > Wayland issue. Integration is already shown via the icon corner emblem
- Replace custom
.app-cardCSS with libadwaita.card+.activatable- native hover, active, dark mode, and contrast states for free - FlowBox max 4 columns (from 6) so cards stay readable
- 14px row and column spacing (matching GNOME Software)
- Right-click context menu on each card (see Section 5)
Files Modified
src/ui/app_card.rs- card construction, badge logic, CSS classesdata/resources/style.css- remove.app-cardrules, add new sizingsrc/ui/library_view.rs- FlowBox max_children_per_line, context menu wiring
2. List View
Current State
- 40px icons, standard ActionRow
- Subtitle mashes version + size + description into one hyphenated string
- All badges shown in a suffix box
- Standard ListBox (no
.rich-list)
New Design
+--[48px icon]--+--Title--------------------------+--[badge]--[>]--+
| (rounded | App Name | |
| 8px clip) | Description or path (.dimmed) | [Update] |
| | 1.2.3 - 45 MiB (.caption) | |
+---------------+----------------------------------+---------------+
Changes
- Icon size: 48px (from 40px) with
border-radius: 8pxandoverflow: hiddenfor rounded clipping - Subtitle structured as two lines:
- Line 1: Description snippet or file path (dimmed)
- Line 2: Version + size (caption, dimmed, numeric)
- Use
subtitle-lines(2)on ActionRow to allow the two-line subtitle
.rich-liststyle class on the ListBox for taller rows- Single badge suffix - same priority logic as cards
- Remove integration badge from suffix - redundant with icon emblem
- Right-click context menu - same menu as card view
- Navigate arrow stays as rightmost suffix
Files Modified
src/ui/library_view.rs- list row construction, ListBox class, context menudata/resources/style.css- icon rounding class
3. Detail View (Tabbed)
Current State
- 64px icon in banner
- Single scrolling page with 3 PreferencesGroups
- 20+ rows all visible at once
- No visual hierarchy between sections
New Design
+--[< back]------- App Name --------[Update][Launch]--+
| |
| +--[96px icon]---+ App Name (.title-1) |
| | (icon- | 1.2.3 - x86_64 (.dimmed) |
| | dropshadow) | Short description (.body) |
| +----------------+ [Integrated] [Native Wayland] |
| |
| +---[Overview]--[System]--[Security]--[Storage]--+ |
| | | |
| | (active tab content below) | |
| | | |
| + + |
+------------------------------------------------------+
Hero Banner
- 96px icon (from 64px) with
.icon-dropshadow - App name in
.title-1(stays as-is) - Subtle gradient background:
linear-gradient(to bottom, alpha(@accent_bg_color, 0.08), transparent)behind the banner area - Key badges inline (stays as-is)
Tab System
adw::ViewStackcontains four pagesadw::ViewSwitcherwith.inlinestyle, placed between the banner and tab content (not in the header bar)- Header bar stays clean with just back button, app name title, Update button, Launch button
Tab 1: Overview (default)
Shows the most commonly needed information at a glance.
- Update method (update type or "no automatic updates")
- Update status (available/up-to-date) with version info
- Last checked date
- Total launches + last launched
- AppImage type + executable status
- File path with copy + open folder buttons
- First seen / last scanned dates
- Notes (if any)
Tab 2: System
All system integration and compatibility information.
- Desktop integration switch
- Desktop file path (if integrated)
- Wayland compatibility row + badge
- Analyze toolkit button
- Runtime display protocol (if available)
- FUSE status + badge
- Launch method
- Firejail sandbox switch + install hint
Tab 3: Security
Vulnerability scanning and integrity.
- Bundled libraries count
- Vulnerability summary with severity badge
- Scan button (with busy state)
- SHA256 checksum with copy button
Tab 4: Storage
Disk usage and data discovery.
- AppImage file size
- Total disk footprint (if discovered)
- Discover data paths button
- Individual discovered paths with type icons, confidence badges, sizes
Files Modified
src/ui/detail_view.rs- major restructure: banner upgrade, ViewStack/ViewSwitcher, redistribute rows across 4 tab pagesdata/resources/style.css- banner gradient, ViewSwitcher positioning
4. CSS & Visual Polish
Remove
.app-cardand.app-card:hoverand.app-card:activerules (replaced by libadwaita.card)- Dark mode
.app-cardoverrides (handled by.cardautomatically) - High contrast
.app-cardoverrides (handled by.cardautomatically)
Add
/* Rounded icon clipping for list view */
.icon-rounded {
border-radius: 8px;
overflow: hidden;
}
/* Detail banner gradient wash */
.detail-banner {
padding: 18px 0;
background-image: linear-gradient(
to bottom,
alpha(@accent_bg_color, 0.08),
transparent
);
border-radius: 12px;
margin-bottom: 6px;
}
Keep (unchanged)
- All status badge styling
- Integration emblem styling
- Letter-circle fallback icons
- All WCAG AAA styles (focus indicators, high contrast, reduced motion, target sizes)
- Compatibility warning banner
- Quick action pill styling
Style Classes Used (libadwaita built-in)
.card+.activatableon FlowBoxChild card boxes.icon-dropshadowon icons 48px+.rich-liston list view ListBox.numericon version/size labels.title-3on card app names.inlineon the detail ViewSwitcher.propertyon key-value ActionRows where subtitle is the main content (path, SHA256)
5. Right-Click Context Menu
Design
A GtkPopoverMenu built from a gio::Menu model, attached to each FlowBoxChild (card) and ListBox row. Triggered by secondary click (button 3) or long-press on touch.
+---------------------------+
| Launch |
+---------------------------+
| Check for Updates |
| Scan for Vulnerabilities |
+---------------------------+
| Integrate / Remove |
| Open Containing Folder |
+---------------------------+
| Copy Path |
+---------------------------+
Menu Items
| Label | Action | Notes |
|---|---|---|
| Launch | app.launch-appimage(id) |
Launches the AppImage |
| Check for Updates | app.check-update(id) |
Triggers update check, shows toast with result |
| Scan for Vulnerabilities | app.scan-security(id) |
Triggers security scan, shows toast |
| Integrate / Remove Integration | app.toggle-integration(id) |
Label changes based on current state |
| Open Containing Folder | app.open-folder(id) |
Opens file manager to the directory |
| Copy Path | app.copy-path(id) |
Copies full path, shows toast |
Implementation Approach
- Define actions at the window level with the record ID as parameter
- Build a
gio::Menuwith sections (separators between groups) - Attach
GtkPopoverMenuto each card/row - Wire
GtkGestureClickfor button 3 (right-click) andGtkGestureLongPressfor touch - Update the "Integrate/Remove" label dynamically based on
record.integrated
Files Modified
src/window.rs- define parameterized actionssrc/ui/library_view.rs- create menu model, attach to cards and rowssrc/ui/app_card.rs- gesture attachment on FlowBoxChild
6. Files Modified Summary
| File | Changes |
|---|---|
src/ui/app_card.rs |
72px icon, .title-3 name, single badge, .card class, gesture for context menu |
src/ui/library_view.rs |
FlowBox max 4 cols, .rich-list on ListBox, list row restructure, context menu creation and attachment |
src/ui/detail_view.rs |
96px icon, ViewStack/ViewSwitcher tabs, redistribute rows into 4 tab pages, banner gradient |
src/window.rs |
Parameterized actions for context menu (launch, update, scan, integrate, open-folder, copy-path) |
data/resources/style.css |
Remove .app-card rules, add .icon-rounded, update .detail-banner with gradient, keep all WCAG styles |
src/ui/widgets.rs |
Minor - ensure icon helper supports .icon-dropshadow |
Verification
After implementation:
cargo build- zero errors, zero warningscargo test- all 128+ tests pass- Visual verification of all three views in light + dark mode
- Right-click context menu works on cards and list rows
- Detail view tabs switch correctly, content is correctly distributed
- Keyboard navigation: Tab through cards, Enter to open, Escape to go back
- All WCAG AAA compliance preserved