use eframe::egui;
#[derive(Default)]
struct ChaiTeaApp<M, Msg, Fupdate, Fview> {
model: M,
messages: Vec<Msg>,
update: Fupdate,
view: Fview,
}
pub fn run<M, Msg, Finit, Fupdate, Fview>(
title: &str,
init: Finit,
update: Fupdate,
view: Fview,
) -> eframe::Result<()>
where
M: Default + 'static,
Finit: Fn() -> M + 'static,
Fupdate: Fn(M, Msg) -> M + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
Msg: 'static,
{
let options = eframe::NativeOptions::default();
eframe::run_native(
title,
options,
Box::new(move |_cc| {
Ok(Box::new(ChaiTeaApp {
model: init(),
messages: Vec::new(),
update,
view,
}))
}),
)
}
#[inline(always)]
pub fn brew<M, Msg, Finit, Fupdate, Fview>(
title: &str,
init: Finit,
update: Fupdate,
view: Fview,
) -> eframe::Result<()>
where
M: Default + 'static,
Msg: 'static,
Finit: Fn() -> M + 'static,
Fupdate: Fn(M, Msg) -> M + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
{
run(title, init, update, view)
}
impl<M, Msg, Fupdate, Fview> eframe::App for ChaiTeaApp<M, Msg, Fupdate, Fview>
where
M: Default + 'static,
Msg: 'static,
Fupdate: Fn(M, Msg) -> M + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
{
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
(self.view)(ctx, &self.model, &mut self.messages);
let msgs: Vec<_> = self.messages.drain(..).collect();
for msg in msgs {
let old = std::mem::take(&mut self.model);
self.model = (self.update)(old, msg);
}
}
}
struct ChaiTeaAppAsync<M, S, Cmd, Msg, Fupdate, Fview, Fcmd> {
model: M,
sync_state: S,
messages: Vec<Msg>,
update: Fupdate,
view: Fview,
run_cmd: Fcmd,
chai_tx: ChaiSender<Msg>,
msg_rx: std::sync::mpsc::Receiver<Msg>,
_phantom_cmd: std::marker::PhantomData<Cmd>,
}
pub struct ChaiSender<T> {
tx: std::sync::mpsc::Sender<T>,
ctx: Option<egui::Context>,
}
impl<T> ChaiSender<T> {
pub fn new(tx: std::sync::mpsc::Sender<T>) -> Self {
Self { tx, ctx: None }
}
pub fn set_ctx(&mut self, ctx: &egui::Context) {
self.ctx = Some(ctx.clone());
}
pub fn send(&self, msg: T) -> Result<(), std::sync::mpsc::SendError<T>> {
if let Some(ctx) = &self.ctx {
ctx.request_repaint();
}
self.tx.send(msg)
}
#[inline(always)]
pub fn send_repaintless(&self, msg: T) -> Result<(), std::sync::mpsc::SendError<T>> {
self.tx.send(msg)
}
}
impl<T> std::ops::Deref for ChaiSender<T> {
type Target = std::sync::mpsc::Sender<T>;
fn deref(&self) -> &Self::Target {
&self.tx
}
}
impl<T> Clone for ChaiSender<T> {
fn clone(&self) -> Self {
Self {
tx: self.tx.clone(),
ctx: self.ctx.clone(),
}
}
}
#[inline(always)]
pub fn brew_async<M, S, Cmd, Msg, Finit, FsyncInit, Fupdate, Fview, Fcmd>(
title: &str,
init: Finit,
sync_state_init: FsyncInit,
update: Fupdate,
view: Fview,
run_cmd: Fcmd,
) -> eframe::Result<()>
where
M: Default + 'static,
S: 'static,
Cmd: 'static,
Finit: Fn() -> M + 'static,
FsyncInit: Fn() -> S + 'static,
Fupdate: Fn(M, Msg) -> (M, Option<Cmd>) + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
Fcmd: Fn(Cmd, &mut S, ChaiSender<Msg>) + Copy + Send + Sync + 'static,
Msg: 'static,
{
run_async(title, init, sync_state_init, update, view, run_cmd)
}
pub fn run_async<M, S, Cmd, Msg, Finit, FsyncInit, Fupdate, Fview, Fcmd>(
title: &str,
init: Finit,
sync_state_init: FsyncInit,
update: Fupdate,
view: Fview,
run_cmd: Fcmd,
) -> eframe::Result<()>
where
M: Default + 'static,
S: 'static,
Cmd: 'static,
Finit: Fn() -> M + 'static,
FsyncInit: Fn() -> S + 'static,
Fupdate: Fn(M, Msg) -> (M, Option<Cmd>) + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
Fcmd: Fn(Cmd, &mut S, ChaiSender<Msg>) + Copy + Send + Sync + 'static,
Msg: 'static,
{
let options = eframe::NativeOptions::default();
let (msg_tx, msg_rx) = std::sync::mpsc::channel();
let chai_tx = ChaiSender::new(msg_tx);
eframe::run_native(
title,
options,
Box::new(move |_cc| {
Ok(Box::new(ChaiTeaAppAsync {
model: init(),
sync_state: sync_state_init(),
messages: Vec::new(),
update,
view,
run_cmd,
chai_tx,
msg_rx,
_phantom_cmd: std::marker::PhantomData,
}))
}),
)
}
impl<M, S, Cmd, Msg, Fupdate, Fview, Fcmd> eframe::App
for ChaiTeaAppAsync<M, S, Cmd, Msg, Fupdate, Fview, Fcmd>
where
M: Default + 'static,
S: 'static,
Cmd: 'static,
Msg: 'static,
Fupdate: Fn(M, Msg) -> (M, Option<Cmd>) + Copy + 'static,
Fview: Fn(&egui::Context, &M, &mut Vec<Msg>) + Copy + 'static,
Fcmd: Fn(Cmd, &mut S, ChaiSender<Msg>) + Copy + Send + Sync + 'static,
{
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
self.chai_tx.set_ctx(ctx);
});
(self.view)(ctx, &self.model, &mut self.messages);
let mut msgs: Vec<_> = self.messages.drain(..).collect();
let mut cmds = Vec::<Cmd>::new();
while let Ok(msg) = self.msg_rx.try_recv() {
msgs.push(msg);
}
for msg in msgs {
let old = std::mem::take(&mut self.model);
let (new_model, cmd) = (self.update)(old, msg);
self.model = new_model;
if let Some(cmd) = cmd {
cmds.push(cmd);
}
}
for cmd in cmds {
let tx = ChaiSender::clone(&self.chai_tx);
(self.run_cmd)(cmd, &mut self.sync_state, tx);
}
}
}