Initial import
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user