facett-app/examples/starter_app.rs
//! **The facett-app starter** — a complete, minimal app you can copy.
//!
//! `cargo run -p facett-app --example starter_app`
//!
//! Shows the whole pattern end-to-end: define your own `Facet`, build a
//! `NavModel`, mount panes in an `AppShell`, and run it full-window. Native +
//! wasm (canvas) from one code path. This file IS the `create-facett-app` template
//! until a cargo-generate scaffold lands.
use eframe::egui;
use facett_app::{AppShell, NavItem, NavModel, Stepper, Toasts, Level};
use facett_core::{Facet, FacetCaps};
// 1) Your own component — anything implementing `Facet` can be a page.
struct CounterPage {
title: String,
stepper: Stepper,
toasts: Toasts,
}
impl CounterPage {
fn new() -> Self {
Self { title: "counter".into(), stepper: Stepper::new(0).range(0, 20), toasts: Toasts::new() }
}
}
impl Facet for CounterPage {
fn title(&self) -> &str {
&self.title
}
fn ui(&mut self, ui: &mut egui::Ui) {
ui.heading("Counter");
ui.label("A page is just a Facet. This one owns a Stepper + a toast queue.");
if let Some(v) = self.stepper.show(ui) {
self.toasts.push(if v == 20 { Level::Warn } else { Level::Info }, format!("count = {v}"));
}
self.toasts.show(ui);
}
fn state_json(&self) -> serde_json::Value {
serde_json::json!({ "stepper": self.stepper.state_json() })
}
fn caps(&self) -> FacetCaps {
FacetCaps::NONE.themeable()
}
}
// 2) The app shell wraps your pages with nav + chrome.
struct App {
shell: AppShell,
}
impl Default for App {
fn default() -> Self {
let model = NavModel::new([
NavItem::new("home", "Home").icon("🏠"),
NavItem::new("counter", "Counter").icon("🔢"),
NavItem::new("about", "About").icon("ℹ"),
]);
let shell = AppShell::new("My facett app", model)
.with_body("home", "Welcome! Edit examples/starter_app.rs to make it yours.")
.with_pane("counter", Box::new(CounterPage::new()))
.with_body("about", "Built with facett-app — an egui app framework. Native + wasm from one codebase.")
.with_status("ready");
Self { shell }
}
}
impl eframe::App for App {
fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
// 3) The shell is the whole window.
egui::CentralPanel::default().show(root, |ui| self.shell.ui(ui));
}
}
fn main() -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size(egui::Vec2::new(820.0, 600.0)),
..Default::default()
};
eframe::run_native("facett-app · starter", options, Box::new(|_cc| Ok(Box::<App>::default())))
}