Initial import

This commit is contained in:
2026-03-30 22:51:56 -04:00
commit 08e2910b9d
103 changed files with 35475 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
# 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