facett-grid/examples/grid_demo.rs
//! **grid_demo** — the flagship `facett-grid`, live: a **50,000-row** Arrow-backed
//! grid with a frozen sticky header, two pinned leading columns, a sticky Σ totals
//! footer, multi-column sort (shift-click a 2nd header), Excel-style cell
//! selection (click · shift-range · ctrl-multi · Ctrl-A), keyboard nav
//! (arrows/page/home/end), type-aware cells (grouped ints, float heat, enum chips,
//! booleans, in-cell progress bars, sparklines), conditional formatting, a density
//! toggle, a live filter, and a **palette switcher** proving every colour is
//! theme-driven. **Ctrl-C** copies the selection as TSV (routed exactly the way a
//! [`FacetDeck`] does).
//!
//! Run: `cargo run -p facett-grid --example grid_demo`
use std::sync::Arc;
use arrow_array::{BooleanArray, Date32Array, Float64Array, Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use eframe::egui;
use facett_core::{FacetDeck, Theme, clipboard, set_theme};
use facett_grid::{CondFmt, Density, Grid};
const ROWS: usize = 50_000;
fn demo_batch(n: usize) -> RecordBatch {
let id: Vec<i64> = (0..n as i64).map(|i| 100_000 + i).collect();
let name: Vec<String> = (0..n).map(|i| format!("vessel-{i:06}")).collect();
let region: Vec<&str> = (0..n).map(|i| ["Nordsjøn", "Skagerrak", "Kattegat", "Østersjön", "Barents"][i % 5]).collect();
let class: Vec<&str> = (0..n).map(|i| ["cargo", "tanker", "fishing", "passenger"][i % 4]).collect();
let speed: Vec<f64> = (0..n).map(|i| ((i * 53) % 320) as f64 / 10.0).collect();
let risk: Vec<f64> = (0..n).map(|i| ((i * 71 + 13) % 100) as f64).collect();
let progress: Vec<f64> = (0..n).map(|i| ((i * 29) % 100) as f64 / 100.0).collect();
let trend: Vec<f64> = (0..n).map(|i| (i as f64 * 0.3).sin() * 40.0 + 50.0).collect();
let flagged: Vec<bool> = (0..n).map(|i| i % 7 == 0).collect();
let seen: Vec<i32> = (0..n).map(|i| 19000 + (i as i32 % 365)).collect();
RecordBatch::try_new(
Arc::new(Schema::new(vec![
Field::new("id", DataType::Int64, false),
Field::new("name", DataType::Utf8, false),
Field::new("region", DataType::Utf8, false),
Field::new("class", DataType::Utf8, false),
Field::new("speed_kn", DataType::Float64, false),
Field::new("risk", DataType::Float64, false),
Field::new("scan", DataType::Float64, false),
Field::new("trend", DataType::Float64, false),
Field::new("flagged", DataType::Boolean, false),
Field::new("last_seen", DataType::Date32, false),
])),
vec![
Arc::new(Int64Array::from(id)),
Arc::new(StringArray::from(name)),
Arc::new(StringArray::from(region)),
Arc::new(StringArray::from(class)),
Arc::new(Float64Array::from(speed)),
Arc::new(Float64Array::from(risk)),
Arc::new(Float64Array::from(progress)),
Arc::new(Float64Array::from(trend)),
Arc::new(BooleanArray::from(flagged)),
Arc::new(Date32Array::from(seen)),
],
)
.unwrap()
}
fn build_grid() -> Grid {
let mut g = Grid::from_record_batch(format!("{ROWS} vessels"), &demo_batch(ROWS))
.pin_columns(2) // freeze id + name
.with_totals(true)
.with_density(Density::Comfortable)
.with_cond_fmt(5, CondFmt::ColorScale) // risk → heat
.with_progress(6) // scan → in-cell bar
.with_sparkline(7); // trend → sparkline
// Pre-seed a multi-sort so the header shows ▲1 / ▲2.
g.click_header(2, false); // region asc
g.click_header(5, true); // then risk asc
g
}
struct DemoApp {
deck: FacetDeck,
}
impl Default for DemoApp {
fn default() -> Self {
let deck = FacetDeck::new(vec![Box::new(build_grid())]).with_fx(Default::default());
Self { deck }
}
}
impl eframe::App for DemoApp {
fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
let ctx = root.ctx().clone();
// Apply the deck's chosen palette (the picker below pins it), else a
// striking default so the demo opens looking great.
if let Some(t) = self.deck.fx().theme() {
set_theme(&ctx, t);
} else {
set_theme(&ctx, Theme::nordic_aurora());
}
egui::Panel::top("bar").show(root, |ui| {
ui.horizontal(|ui| {
ui.heading("facett-grid");
ui.separator();
self.deck.palette_picker(ui);
});
ui.label("click=cell · shift=range · ctrl=multi · Ctrl-A=all · Ctrl-C=copy TSV · arrows/Page/Home/End=nav");
});
egui::CentralPanel::default().show(root, |ui| {
// The deck routes Ctrl-C / scale shortcuts to the grid for free.
self.deck.ui(ui);
});
// Make the routed Ctrl-C visible in the terminal too.
for action in clipboard::poll(&ctx) {
if let clipboard::ClipAction::Copy = action {
eprintln!("grid_demo: Ctrl-C → selection copied to clipboard as TSV");
}
}
}
}
fn main() -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([1280.0, 760.0]),
..Default::default()
};
eframe::run_native("facett-grid demo", options, Box::new(|_cc| Ok(Box::<DemoApp>::default())))
}