use std::cell::RefCell;
use std::rc::Rc;
use guinea_app::app::{GuineaApp, install_runtime, shutdown_current};
use guinea_core::actor::UiThreadToken;
use guinea_core::guard::Ask;
use guinea_router::router::{NavigateHandle, RouteChain, RouteSink, Router};
use iced::widget::{button, center, column, container, opaque, row, text};
use crate::{Envelope, Iced, Nodes, dispatcher, envelope, nav};
pub const MAIN: &str = "main";
pub struct Shell {
router: Rc<Router<Iced>>,
nodes: Nodes,
}
pub fn run<R>(
app: GuineaApp,
title: &str,
window: iced::window::Settings,
initial: impl FnOnce() -> R,
) -> anyhow::Result<()>
where
R: RouteChain<Iced> + 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::<Iced>::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)?;
let mut nodes = Nodes::default();
if let Some(chain) = router.active_chain() {
nodes.sync(chain);
}
let nodes = RefCell::new(Some(nodes));
let title = title.to_string();
let boot = {
let router = router.clone();
move || Shell {
router: router.clone(),
nodes: nodes
.borrow_mut()
.take()
.expect("iced boots an application once"),
}
};
let outcome = iced::application(boot, update, view)
.title(move |_: &Shell| title.clone())
.subscription(subscription)
.window(window)
.run();
dispatcher::close();
nav::clear();
shutdown_current();
outcome.map_err(|e| anyhow::anyhow!("iced: {e}"))
}
fn update(shell: &mut Shell, message: Envelope) {
envelope::settle(&shell.router, &mut shell.nodes, message);
}
fn view(shell: &Shell) -> iced::Element<'_, Envelope> {
let tree = shell.router.render(&shell.nodes);
match shell.router.pending() {
None => tree,
Some(ask) => iced::widget::stack![tree, opaque(question(ask))].into(),
}
}
#[derive(Clone, Copy)]
enum Answer {
Yes,
No,
}
fn question(ask: Ask) -> iced::Element<'static, Envelope> {
let buttons = row![
button(text(ask.cancel)).on_press(Answer::No),
button(text(ask.confirm))
.style(button::danger)
.on_press(Answer::Yes),
]
.spacing(8);
let dialog = center(
container(column![text(ask.text), buttons].spacing(16))
.padding(20)
.style(container::bordered_box),
)
.style(|theme: &iced::Theme| container::Style {
background: Some(
theme
.extended_palette()
.background
.base
.color
.scale_alpha(0.8)
.into(),
),
..container::Style::default()
});
iced::Element::from(dialog).map(|answer| Envelope::answer(matches!(answer, Answer::Yes)))
}
fn subscription(_shell: &Shell) -> iced::Subscription<Envelope> {
iced::Subscription::run(dispatcher::wakeups)
}