Initial import
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
# Design System — MangoTune GTK4 / libadwaita
|
||||
|
||||
## Guiding Principles
|
||||
|
||||
1. **Libadwaita first** — use `Adw::*` widgets wherever they exist before falling back to GTK4.
|
||||
This ensures correct dark/light mode, accent color, and GNOME HIG compliance automatically.
|
||||
2. **Every field validates on change** — instant inline feedback, never wait for save.
|
||||
3. **Save button state is truth** — it is only sensitive when all fields are valid AND there
|
||||
are unsaved changes. It is insensitive otherwise. Never disable fields; always show why.
|
||||
4. **Contextual help is inline** — use `subtitle` on `AdwActionRow` for brief descriptions.
|
||||
Longer explanations go in an `AdwTooltip`. No separate help dialogs.
|
||||
5. **Destructive actions require confirmation** — deleting a config file uses `AdwAlertDialog`.
|
||||
|
||||
---
|
||||
|
||||
## Main Window Structure
|
||||
|
||||
```
|
||||
AdwApplicationWindow "MangoTune"
|
||||
AdwToolbarView
|
||||
┌── [top] AdwHeaderBar
|
||||
│ Title: "MangoTune"
|
||||
│ Start: AdwSplitButton "Save" (primary action)
|
||||
│ End: menu button (gear icon → preferences, about)
|
||||
│
|
||||
├── [top] ConfigBarWidget (custom, below header)
|
||||
│ Shows: current file being edited
|
||||
│ Dropdown: select from all discovered config layers
|
||||
│ Conflict indicator: if any conflicts detected
|
||||
│
|
||||
└── [content] AdwOverlaySplitView
|
||||
Sidebar: NavigationSidebar (AdwNavigationSidebar or custom ListBox)
|
||||
Content: AdwNavigationView (manages page stack)
|
||||
```
|
||||
|
||||
## Header Bar
|
||||
|
||||
- Title: "MangoTune"
|
||||
- Subtitle: name of current config file being edited (short path)
|
||||
- Primary button: `AdwSplitButton` labeled "Save"
|
||||
- Main click: save current file
|
||||
- Dropdown arrow: "Save As…", "Revert to Saved", "Create Backup"
|
||||
- End: `Gtk::MenuButton` with gear icon
|
||||
- Menu items: Preferences, Keyboard Shortcuts, About MangoTune
|
||||
|
||||
## Config File Selector Bar
|
||||
|
||||
Custom widget rendered between HeaderBar and the sidebar/content split.
|
||||
Appearance: an `AdwBanner` variant or custom `GtkBox` with background `@card_bg_color`.
|
||||
|
||||
Contents (left to right):
|
||||
- Icon indicating layer type (globe for global, app icon for per-app, warning for env)
|
||||
- Dropdown (`GtkDropDown`) showing all discovered layers with their priority
|
||||
- Conflict badge: `GtkLabel` with `.error` or `.warning` CSS class if conflicts exist
|
||||
- Right side: "View All Layers" button → navigates to Conflicts page
|
||||
|
||||
Layer display format in dropdown:
|
||||
```
|
||||
[●] ~/.config/MangoHud/MangoHud.conf (global)
|
||||
[◉] ~/.config/MangoHud/cs2.conf (per-app: cs2) ← currently editing
|
||||
[⚠] $MANGOHUD_CONFIG (env override — read only)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sidebar Navigation
|
||||
|
||||
Use `AdwNavigationSidebar` if available in libadwaita 1.4+, otherwise `GtkListBox` with
|
||||
`.navigation-sidebar` CSS class.
|
||||
|
||||
Sections (use `GtkSeparator` between groups):
|
||||
|
||||
**Config**
|
||||
- Overview (house icon)
|
||||
- Layer Conflicts (warning icon, badge with conflict count if > 0)
|
||||
|
||||
**Display**
|
||||
- Performance (speedometer icon)
|
||||
- GPU (chip icon)
|
||||
- CPU (cpu icon)
|
||||
- Memory (memory icon)
|
||||
- I/O & Network (network icon)
|
||||
- Media Player (music note icon)
|
||||
- Battery (battery icon)
|
||||
|
||||
**Appearance**
|
||||
- Layout & Position (layout icon)
|
||||
- Colors & Theme (palette icon)
|
||||
- Typography (text icon)
|
||||
|
||||
**Behavior**
|
||||
- Keybindings (keyboard icon)
|
||||
- FPS Limits (gauge icon)
|
||||
- Logging (file icon)
|
||||
- Blacklist (block icon)
|
||||
|
||||
**Advanced**
|
||||
- OpenGL Quirks (warning icon)
|
||||
- Raw Editor (code icon)
|
||||
|
||||
**Tools**
|
||||
- Test Launcher (play icon)
|
||||
- Integrations (plugin icon)
|
||||
|
||||
---
|
||||
|
||||
## Page Layout Pattern
|
||||
|
||||
Every config page follows this structure:
|
||||
|
||||
```
|
||||
AdwPreferencesPage
|
||||
title: "GPU Metrics"
|
||||
icon-name: "processor-symbolic" (or custom)
|
||||
|
||||
AdwPreferencesGroup
|
||||
title: "GPU Statistics"
|
||||
description: "Core GPU monitoring options"
|
||||
|
||||
AdwSwitchRow ← for Flag/Bool options
|
||||
AdwSpinRow ← for Int options
|
||||
AdwEntryRow ← for String/Path options
|
||||
AdwComboRow ← for Enum options
|
||||
AdwExpanderRow ← for groups with sub-options (e.g. load color thresholds)
|
||||
└── nested rows inside
|
||||
|
||||
AdwPreferencesGroup
|
||||
title: "Advanced"
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Widget Patterns Per Option Type
|
||||
|
||||
### Flag / Bool → `AdwSwitchRow`
|
||||
```
|
||||
AdwSwitchRow {
|
||||
title: "GPU Temperature",
|
||||
subtitle: "Show GPU core temperature (gpu_temp)",
|
||||
active: <bool>,
|
||||
}
|
||||
```
|
||||
On toggle: validate, update model, check dependencies.
|
||||
|
||||
### Int with range → `AdwSpinRow`
|
||||
```
|
||||
AdwSpinRow {
|
||||
title: "Font Size",
|
||||
subtitle: "font_size — valid range: 8–72",
|
||||
value: 24.0,
|
||||
adjustment: Gtk::Adjustment { lower: 8, upper: 72, step-increment: 1 },
|
||||
}
|
||||
```
|
||||
|
||||
### Float with range → `AdwSpinRow` (digits: 2 or 3)
|
||||
|
||||
### Enum → `AdwComboRow`
|
||||
```
|
||||
AdwComboRow {
|
||||
title: "HUD Position",
|
||||
subtitle: "position",
|
||||
model: StringList ["top-left", "top-right", "bottom-left", ...],
|
||||
}
|
||||
```
|
||||
|
||||
### String (free text) → `AdwEntryRow`
|
||||
```
|
||||
AdwEntryRow {
|
||||
title: "Custom GPU Label",
|
||||
text: "",
|
||||
// validation on ::changed signal
|
||||
}
|
||||
```
|
||||
Validation error: add `.error` CSS class to the row, set subtitle to error message.
|
||||
|
||||
### Path → `AdwEntryRow` + browse button
|
||||
```
|
||||
AdwActionRow {
|
||||
title: "Font File",
|
||||
AdwEntryRow + GtkButton "Browse…"
|
||||
}
|
||||
```
|
||||
Browse opens `GtkFileDialog` filtered to `.ttf,.otf`.
|
||||
Validate path exists after selection.
|
||||
|
||||
### Color → `AdwActionRow` with color swatch button
|
||||
```
|
||||
AdwActionRow {
|
||||
title: "GPU Color",
|
||||
subtitle: "gpu_color — hex RRGGBB",
|
||||
[suffix] GtkButton (color swatch, shows current color)
|
||||
→ opens AdwDialog with color picker
|
||||
→ also shows a GtkEntry for manual hex input
|
||||
}
|
||||
```
|
||||
|
||||
### Hotkey / Keybind → Custom `KeybindRow` widget
|
||||
```
|
||||
AdwActionRow {
|
||||
title: "Toggle HUD",
|
||||
subtitle: "toggle_hud",
|
||||
[suffix] GtkShortcutLabel (shows current binding)
|
||||
[suffix] GtkButton "Edit" → opens capture dialog
|
||||
}
|
||||
```
|
||||
Capture dialog: fullscreen-ish `AdwDialog`, listens for keypress, shows "Press a key combination…",
|
||||
captures and validates the combination, shows preview, OK/Cancel.
|
||||
|
||||
### CommaSeparatedStrings (controlled set) → `AdwExpanderRow` with checkboxes
|
||||
Example: `graphs`, `font_glyph_ranges`, `device_battery`
|
||||
```
|
||||
AdwExpanderRow {
|
||||
title: "Graphs",
|
||||
subtitle: "Select which graphs to display",
|
||||
[child per valid value] AdwSwitchRow or CheckButton row
|
||||
}
|
||||
```
|
||||
|
||||
### CommaSeparatedStrings (free) → `AdwEntryRow` with validation
|
||||
Example: `blacklist`, `network`
|
||||
|
||||
### FpsLimitList → Custom widget
|
||||
A `GtkFlowBox` of chips showing current FPS values (0, 30, 60, etc.)
|
||||
with + button to add and × to remove each. Each value validated as non-negative int.
|
||||
|
||||
---
|
||||
|
||||
## Inline Validation Display
|
||||
|
||||
When a field has an error:
|
||||
1. The `AdwActionRow` or `AdwEntryRow` gets `.error` CSS class applied.
|
||||
2. The row's subtitle changes to the error message (red text via `.error` on a child label).
|
||||
3. A validation summary appears at top of page: `AdwBanner` with "N fields have errors — fix to enable saving".
|
||||
4. The Save button in the header becomes insensitive.
|
||||
|
||||
When a dependency warning fires (e.g. user enables `gpu_mem_clock` without `vram`):
|
||||
1. Show `AdwAlertDialog`: "Enabling 'GPU Memory Clock' also requires 'VRAM display' to be enabled. Enable it now?"
|
||||
2. Buttons: "Enable Both" (suggested-action), "Cancel".
|
||||
|
||||
---
|
||||
|
||||
## Conflict/Layer Cascade Page
|
||||
|
||||
This is the most distinctive page in the app.
|
||||
|
||||
Layout: vertical stack of `AdwPreferencesGroup` cards, one per discovered layer,
|
||||
ordered top-to-bottom = highest-to-lowest priority.
|
||||
|
||||
Each layer card header shows:
|
||||
- Priority badge (e.g. "ENV", "PER-APP", "GLOBAL") with color coding
|
||||
- File path or env var name
|
||||
- "Edit" button (disabled for env layers)
|
||||
- "Open in Files" button (for file layers)
|
||||
|
||||
Inside each layer card: a `GtkListBox` showing every option set in that layer.
|
||||
Options that are shadowed by a higher-priority layer:
|
||||
- Shown with strikethrough text
|
||||
- A label "overridden by {LAYER}" in muted color
|
||||
|
||||
Options that are unique to this layer (no conflict): normal display.
|
||||
Options that this layer wins on (it overrides lower layers): bold text.
|
||||
|
||||
Filter bar at top of page:
|
||||
- "Show all options" / "Show conflicts only" / "Show shadowed only" toggle buttons
|
||||
|
||||
---
|
||||
|
||||
## Test Launcher Panel
|
||||
|
||||
Shown as a persistent bottom bar (collapsed by default) OR as a dedicated page.
|
||||
Decision: dedicated page (cleaner, avoids layout complications).
|
||||
|
||||
Layout:
|
||||
```
|
||||
AdwPreferencesPage "Test Launcher"
|
||||
AdwPreferencesGroup "Quick Test"
|
||||
description: "Launch a test application with MangoHud active to preview your config"
|
||||
|
||||
AdwActionRow "vkcube (Vulkan)"
|
||||
subtitle: "vulkan-tools — tests Vulkan overlay"
|
||||
[suffix] status: "installed" / "not found"
|
||||
[suffix] GtkButton "Launch"
|
||||
|
||||
AdwActionRow "glxgears (OpenGL)"
|
||||
subtitle: "mesa-utils — tests OpenGL overlay"
|
||||
[suffix] status indicator
|
||||
[suffix] GtkButton "Launch"
|
||||
|
||||
AdwActionRow "Custom Application"
|
||||
subtitle: "Launch any app with MangoHud injected"
|
||||
[suffix] GtkEntry (command)
|
||||
[suffix] GtkButton "Launch"
|
||||
|
||||
AdwPreferencesGroup "Launch Options"
|
||||
AdwSwitchRow "Auto-reload config on save"
|
||||
subtitle: "Sends SIGUSR1 to running MangoHud processes on save"
|
||||
AdwSwitchRow "Show terminal output"
|
||||
subtitle: "Opens a terminal window showing app stdout/stderr"
|
||||
|
||||
AdwPreferencesGroup "Running Process"
|
||||
(only visible when a test process is active)
|
||||
AdwActionRow showing process name + PID
|
||||
[suffix] GtkButton "Stop"
|
||||
```
|
||||
|
||||
When Launch is clicked:
|
||||
1. Check tool is installed (which vkcube, which glxgears).
|
||||
2. If not found: `AdwToast` "vkcube not found. Install vulkan-tools package."
|
||||
3. If found: spawn process with `MANGOHUD=1 MANGOHUD_CONFIGFILE={current_path} {command}`.
|
||||
4. Show running process row.
|
||||
5. Monitor process — remove row when it exits.
|
||||
|
||||
---
|
||||
|
||||
## Theming
|
||||
|
||||
- Follow system theme (light/dark) automatically via libadwaita.
|
||||
- Do NOT hardcode colors. Use only named GTK/Adwaita CSS variables:
|
||||
`@accent_color`, `@destructive_color`, `@warning_color`, `@success_color`,
|
||||
`@card_bg_color`, `@window_bg_color`, `@headerbar_bg_color`, etc.
|
||||
- MangoTune-specific CSS: only for the cascade view layer badges and color swatch button.
|
||||
Place in `data/style.css`, loaded at runtime via `GtkCssProvider`.
|
||||
|
||||
---
|
||||
|
||||
## Accessibility
|
||||
|
||||
- All interactive widgets must have accessible labels.
|
||||
- Color information must never be the sole indicator of state (always pair with icon or text).
|
||||
- Keyboard navigation must work for all pages (GTK4 handles most of this by default).
|
||||
- Use `gtk_accessible_update_property` where needed for dynamic content.
|
||||
|
||||
---
|
||||
|
||||
## Window Size & Responsiveness
|
||||
|
||||
- Default: 1200 × 780
|
||||
- Minimum: 900 × 600
|
||||
- The `AdwOverlaySplitView` collapses the sidebar at narrow widths (< 980px) automatically.
|
||||
- Persist window size via GSettings `window-width` / `window-height`.
|
||||
Reference in New Issue
Block a user