use image::DynamicImage; use crate::{ layout::{ Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget, WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate, }, util::{HashSet, Id}, }; use std::sync::mpsc::{Receiver, channel}; pub struct Ui { // TODO: make this at least pub(super) pub(crate) data: PainterData, root: Option, updates: HashSet, free: Vec, recv: Receiver, full_redraw: bool, resized: bool, } impl Ui { pub fn add(&mut self, w: impl WidgetLike) -> WidgetRef { w.add(self) } pub fn add_widget(&mut self, w: W) -> WidgetRef { self.data.widgets.insert(w) } pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self).any()); self.full_redraw = true; } pub fn new() -> Self { Self::default() } pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle { self.data.textures.add(image) } pub fn register_event( &mut self, id: &WidgetRef, event: E, f: impl EventFn, ) { self.data .modules .get_mut::>() .register(id.id(), event, f); } pub fn resize(&mut self, size: impl Into) { self.data.output_size = size.into(); self.resized = true; } fn redraw_all(&mut self) { for (_, inst) in self.data.active.drain() { for m in self.data.modules.iter_mut() { m.on_undraw(&inst); } } // free before bc nothing should exist self.free(); if let Some(root) = &self.root { self.data.draw(root); } } pub fn update(&mut self) -> bool { for update in self.recv.try_iter() { match update { WidgetUpdate::Drop(id) => { self.free.push(id); } WidgetUpdate::Mutate(id) => { self.updates.insert(id); } } } if self.full_redraw { self.redraw_all(); self.full_redraw = false; true } else if self.resized { self.resized = false; self.redraw_all(); true } else if !self.updates.is_empty() { self.redraw_updates(); true } else { false } } fn redraw_updates(&mut self) { self.data.redraw(std::mem::take(&mut self.updates)); self.free(); } /// free any resources that don't have references anymore fn free(&mut self) { for id in self.free.drain(..) { for m in self.data.modules.iter_mut() { m.on_remove(&id); } self.data.widgets.delete(id); } self.data.textures.free(); } pub fn needs_redraw(&self) -> bool { self.full_redraw || !self.updates.is_empty() } pub fn num_widgets(&self) -> usize { self.data.widgets.len() } pub fn active_widgets(&self) -> usize { self.data.active.len() } pub fn debug_layers(&self) { for ((idx, depth), primitives) in self.data.layers.iter_depth() { let indent = " ".repeat(depth * 2); let len = primitives.instances().len(); print!("{indent}{idx}: {len} primitives"); if len >= 1 { print!(" ({})", primitives.instances()[0].binding); } println!(); } } pub fn window_region(&self, id: &impl IdLike) -> Option { let region = self.data.active.get(&id.id())?.region; Some(region.to_px(self.data.output_size)) } pub fn debug(&self, label: &str) -> impl Iterator { self.data.active.iter().filter_map(move |(id, inst)| { let widget = &self.data.widgets.get(*id); if widget.get_label().as_str() == label { Some(inst) } else { None } }) } } impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); Self { data: PainterData::new(send), root: Default::default(), updates: Default::default(), free: Default::default(), full_redraw: false, recv, resized: false, } } }