use std::cell::RefCell;
use std::rc::Rc;
use guinea_app::app::{GuineaApp, install_runtime, shutdown_current};
use guinea_core::actor::UiThreadToken;
use guinea_router::router::{NavigateHandle, RouteChain, RouteSink, Router};
use crate::{Egui, dispatcher, nav};
pub const MAIN: &str = "main";
pub fn run<R>(
app: GuineaApp,
title: &str,
options: eframe::NativeOptions,
initial: impl FnOnce() -> R,
) -> anyhow::Result<()>
where
R: RouteChain<Egui> + Clone + PartialEq + 'static,
{
dispatcher::install();
let token = UiThreadToken::dangerously_create_token_unchecked();
install_runtime(app.install(token.clone())?);
let initial = initial();
let router = Rc::new(Router::<Egui>::new(token));
guinea_app::app::roots::set_label(router.root(), MAIN);
let route = Rc::new(RefCell::new(initial.clone()));
nav::install(NavigateHandle::new(router.clone(), {
let route = route.clone();
RouteSink::new(move |next: R| *route.borrow_mut() = next)
}));
router.navigate(initial.clone())?;
let front = Frontend {
router: router.clone(),
};
let outcome = eframe::run_native(
title,
options,
Box::new(|cc| {
dispatcher::wake_with(cc.egui_ctx.clone());
Ok(Box::new(front))
}),
);
dispatcher::forget_waker();
nav::clear();
shutdown_current();
outcome.map_err(|e| anyhow::anyhow!("eframe: {e}"))
}
struct Frontend {
router: Rc<Router<Egui>>,
}
impl eframe::App for Frontend {
fn logic(&mut self, _ctx: &egui::Context, _frame: &mut eframe::Frame) {
guinea_core::devtools::profiling::frame_done();
dispatcher::drain();
}
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
let asking = self.router.pending();
{
let _drawing = self.router.drawing();
ui.add_enabled_ui(asking.is_none(), |ui| self.router.render(&()).draw(ui));
}
match self.router.settle() {
Ok(true) => ui.ctx().request_repaint(),
Ok(false) => {}
Err(error) => tracing::error!(%error, "a navigation asked for while drawing failed"),
}
if let Some(ask) = asking {
question(ui, &ask, &self.router);
}
}
}
fn question(ui: &mut egui::Ui, ask: &guinea_core::guard::Ask, router: &Router<Egui>) {
egui::Window::new("?")
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
.show(ui.ctx(), |ui| {
ui.label(&ask.text);
ui.add_space(8.0);
ui.horizontal(|ui| {
if ui.button(&ask.cancel).clicked() {
router.answer(false);
}
if ui.button(&ask.confirm).clicked() {
router.answer(true);
}
});
});
}