Files
mangotune/docs/plan/phase_04.md
T
2026-03-30 23:06:06 -04:00

269 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 04 — GTK4 App Skeleton & Main Window
## Goal
Build the complete application shell: window, header bar, config selector bar,
sidebar navigation, and empty page placeholders. All navigation must work.
No option editing yet — pages show "Coming soon" content.
## Files to implement
- `src/app.rs` (replace stub)
- `src/window.rs` (replace stub)
- `src/ui/mod.rs`
- `src/ui/pages/mod.rs`
- `src/ui/widgets/mod.rs`
- `data/style.css`
---
## src/app.rs
```rust
use gtk4::prelude::*;
use libadwaita::prelude::*;
use crate::window::MainWindow;
use crate::system::detect;
pub struct MangoTuneApp {
app: libadwaita::Application,
}
impl MangoTuneApp {
pub fn new() -> Self {
let app = libadwaita::Application::builder()
.application_id("com.mangotune.MangoTune")
.flags(gio::ApplicationFlags::FLAGS_NONE)
.build();
let app_clone = app.clone();
app.connect_activate(move |_| {
// Load CSS
let provider = gtk4::CssProvider::new();
provider.load_from_data(include_str!("../data/style.css"));
gtk4::style_context_add_provider_for_display(
&gdk::Display::default().expect("No display"),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
// Run system detection async, then build window
let ctx = glib::MainContext::default();
ctx.spawn_local(async move {
let system_info = detect::detect_system().await
.unwrap_or_else(|_| detect::SystemInfo::unknown());
let window = MainWindow::new(&app_clone, system_info);
window.present();
});
});
MangoTuneApp { app }
}
pub fn run(&self) -> i32 {
self.app.run().into()
}
}
```
---
## src/window.rs
```rust
use gtk4::prelude::*;
use libadwaita::prelude::*;
use crate::system::detect::SystemInfo;
use crate::ui::pages;
pub struct MainWindow {
pub window: libadwaita::ApplicationWindow,
}
impl MainWindow {
pub fn new(app: &libadwaita::Application, system_info: SystemInfo) -> Self {
let window = libadwaita::ApplicationWindow::builder()
.application(app)
.title("MangoTune")
.default_width(1200)
.default_height(780)
.build();
// Restore window size from GSettings
// Build layout:
// AdwToolbarView
// top: AdwHeaderBar
// top: ConfigBarWidget (custom)
// content: AdwOverlaySplitView
// sidebar: navigation list
// content: AdwNavigationView (page stack)
let toolbar_view = libadwaita::ToolbarView::new();
let header = build_header_bar();
let config_bar = build_config_bar(&system_info);
let split_view = build_split_view(&system_info);
toolbar_view.add_top_bar(&header);
toolbar_view.add_top_bar(&config_bar);
toolbar_view.set_content(Some(&split_view));
window.set_content(Some(&toolbar_view));
MainWindow { window }
}
pub fn present(&self) { self.window.present(); }
}
```
### Header Bar implementation
```
AdwHeaderBar
title-widget: AdwWindowTitle { title: "MangoTune", subtitle: "No config loaded" }
start: AdwSplitButton "Save" (insensitive by default)
dropdown items: "Save As…", "Revert to Saved", "Create Backup"
end: GtkMenuButton (gear icon)
popover menu: "Preferences", "Keyboard Shortcuts", "About MangoTune"
```
### Config Bar implementation
Custom `GtkBox` with `@card_bg_color` background:
```
GtkBox (horizontal, spacing=8, margin=6)
GtkImage (config type icon — globe/per-app/warning)
GtkLabel "Editing:"
GtkDropDown ← lists all discovered config layers
model: StringList populated from resolver
GtkLabel "⚠ 2 conflicts" (hidden if no conflicts)
GtkButton "View All Layers" → navigate to conflicts page
```
### Split View implementation
```
AdwOverlaySplitView
sidebar-width-fraction: 0.22
min-sidebar-width: 180
max-sidebar-width: 260
show-sidebar: true
collapsed at width < 980
sidebar: NavigationSidebar (GtkListBox with .navigation-sidebar CSS class)
content: AdwNavigationView ← all pages pushed here
```
### Navigation Sidebar
The sidebar is a `GtkListBox` with rows grouped by sections.
See docs/design_system.md → Sidebar Navigation for the full list of sections and items.
Each row stores the page ID as data. On row activation:
- Call `navigation_view.push_by_tag(page_id)`
- Highlight the active row
Section headers: `GtkLabel` with `.heading` CSS class, not selectable.
### Page Stubs (all pages in this phase return placeholder content)
Create all page files listed in `src/ui/pages/` with a stub that returns:
```rust
pub fn build_page() -> libadwaita::PreferencesPage {
let page = libadwaita::PreferencesPage::new();
page.set_title("Page Name");
let group = libadwaita::PreferencesGroup::new();
group.set_title("Coming Soon");
group.set_description(Some("This page will be implemented in a future phase."));
page.add(&group);
page
}
```
### About Dialog
`AdwAboutDialog` with:
```
application-name: "MangoTune"
application-icon: "com.mangotune.MangoTune"
version: env!("CARGO_PKG_VERSION")
comments: "A modern, accurate MangoHud configurator for Linux"
license-type: Gtk::License::Gpl30
website: "https://github.com/your-org/mangotune"
issue-url: "https://github.com/your-org/mangotune/issues"
developers: ["MangoTune Contributors"]
```
---
## data/style.css
Only custom styles that libadwaita/GTK4 don't provide natively.
```css
/* Config layer priority badges */
.layer-badge-env {
background-color: @destructive_color;
color: @destructive_fg_color;
border-radius: 4px;
padding: 2px 6px;
font-size: 0.75em;
font-weight: bold;
}
.layer-badge-perapp {
background-color: @warning_color;
color: @warning_fg_color;
border-radius: 4px;
padding: 2px 6px;
font-size: 0.75em;
font-weight: bold;
}
.layer-badge-global {
background-color: @success_color;
color: @success_fg_color;
border-radius: 4px;
padding: 2px 6px;
font-size: 0.75em;
font-weight: bold;
}
/* Shadowed option text in cascade view */
.option-shadowed {
text-decoration: line-through;
opacity: 0.5;
}
/* Color swatch button */
.color-swatch-button {
min-width: 28px;
min-height: 28px;
border-radius: 4px;
border: 1px solid @borders;
padding: 0;
}
/* Config bar */
.config-bar {
background-color: @card_bg_color;
border-bottom: 1px solid @borders;
padding: 6px 12px;
}
/* Conflict count badge */
.conflict-badge {
background-color: @destructive_color;
color: @destructive_fg_color;
border-radius: 8px;
padding: 1px 6px;
font-size: 0.75em;
font-weight: bold;
}
```
---
## Acceptance Criteria
- [ ] App launches without crashing
- [ ] Window appears at 1200×780
- [ ] Header bar shows "MangoTune" title and (insensitive) Save button
- [ ] Config bar renders below header
- [ ] Sidebar shows all navigation sections with correct icons
- [ ] Clicking each sidebar item navigates to its placeholder page
- [ ] About dialog opens from gear menu
- [ ] System detection runs on startup (check tracing log output)
- [ ] App responds to window resize (sidebar collapses at narrow width)
- [ ] No GTK warnings or critical messages in stderr on launch