use egui::{Context, Id};
use std::{
any::Any,
collections::HashMap,
sync::{Arc, Mutex},
thread::JoinHandle,
};
#[derive(Default)]
pub struct FlowManager {
egui_ctx: Option<Context>,
flows: HashMap<Id, Flow>,
}
impl FlowManager {
pub fn set_repaint_on_finish(&mut self, ctx: Option<Context>) {
self.egui_ctx = ctx;
}
pub fn get_repaint_on_finish(&self) -> bool {
self.egui_ctx.is_some()
}
pub fn start<T, F, Args>(&mut self, id: impl Into<Id>, args: Args, func: F)
where
T: Send + 'static,
F: FnOnce(Args) -> T + Send + 'static,
Args: Send + 'static,
{
let flow = Flow::new(func, args, self.egui_ctx.clone());
let id = id.into();
self.flows.insert(id, flow);
#[cfg(debug_assertions)]
println!("Flow added: {id:?}");
}
pub fn start_simple<T, F>(&mut self, id: impl Into<Id>, func: F)
where
T: Send + 'static,
F: FnOnce() -> T + Send + 'static,
{
let flow = Flow::new_simple(func, self.egui_ctx.clone());
let id = id.into();
if self.flows.insert(id, flow).is_some() {
panic!("flow with id {id:?} already exists");
}
#[cfg(debug_assertions)]
println!("Flow added: {id:?}");
}
pub fn poll<T: 'static>(&mut self, id: impl Into<Id>) -> Option<T> {
let id = id.into();
let result = self.flows.get(&id)?.take_result::<T>();
if result.is_some() {
let flow = self.flows.remove(&id).unwrap();
flow.thread.join().unwrap();
#[cfg(debug_assertions)]
println!("Flow removed: {:?}", id);
}
result
}
pub fn is_active(&self, id: impl Into<Id>) -> bool {
self.flows.contains_key(&id.into())
}
pub fn get_active_flow_ids(&self) -> Vec<Id> {
self.flows.keys().copied().collect()
}
}
struct Flow {
thread: JoinHandle<()>,
result: Arc<Mutex<Option<Box<dyn Any + Send>>>>,
}
impl Flow {
fn new<T, F, Args>(func: F, args: Args, ctx: Option<Context>) -> Self
where
T: Send + 'static,
F: FnOnce(Args) -> T + Send + 'static,
Args: Send + 'static,
{
let result: Arc<Mutex<Option<Box<dyn Any + Send>>>> = Arc::new(Mutex::new(None));
let result_clone = result.clone();
let thread = std::thread::spawn(move || {
*result_clone.lock().unwrap() = Some(Box::new(func(args)) as Box<dyn Any + Send>);
if let Some(ctx) = ctx {
ctx.request_repaint();
}
});
Self { thread, result }
}
fn new_simple<T, F>(func: F, ctx: Option<Context>) -> Self
where
T: Send + 'static,
F: FnOnce() -> T + Send + 'static,
{
let result: Arc<Mutex<Option<Box<dyn Any + Send>>>> = Arc::new(Mutex::new(None));
let result_clone = Arc::clone(&result);
let thread = std::thread::spawn(move || {
*result_clone.lock().unwrap() = Some(Box::new(func()) as Box<dyn Any + Send>);
if let Some(ctx) = ctx { ctx.request_repaint(); }
});
Self { thread, result }
}
fn take_result<T: 'static>(&self) -> Option<T> {
self.result
.lock().unwrap()
.take()
.and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
}
}