use crate::{ Event, EventFn, EventLike, EventManager, IdLike, Mask, PixelRegion, PrimitiveLayers, TextData, TextureHandle, Textures, Widget, WidgetHandle, WidgetId, WidgetLike, Widgets, ui::draw_state::DrawState, util::{HashMap, TrackedArena, Vec2}, }; use image::DynamicImage; use std::{ ops::{Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; mod active; mod cache; mod draw_state; mod painter; mod size; mod state; pub use active::*; use cache::*; pub use painter::Painter; pub use size::*; pub struct Ui { // TODO: edit visibilities pub widgets: Widgets, pub events: EventManager, // retained painter state pub active: HashMap, pub layers: PrimitiveLayers, pub textures: Textures, pub text: TextData, output_size: Vec2, pub masks: TrackedArena, pub cache: Cache, root: Option>, recv: Receiver, send: Sender, full_redraw: bool, resized: bool, } pub trait HasUi: Sized { fn ui_ref(&self) -> &Ui; fn ui(&mut self) -> &mut Ui; } impl Ui { pub fn add, Tag>( &mut self, w: impl WidgetLike, ) -> WidgetHandle { w.add(self) } /// useful for debugging pub fn set_label(&mut self, id: impl IdLike, label: String) { self.widgets.data_mut(id.id()).unwrap().label = label; } pub fn label(&self, id: impl IdLike) -> &String { &self.widgets.data(id.id()).unwrap().label } pub fn add_widget>(&mut self, w: W) -> WidgetHandle { WidgetHandle::new(self.widgets.add(w), self.send.clone()) } pub fn set_root(&mut self, w: impl WidgetLike) { self.root = Some(w.add(self)); self.full_redraw = true; } pub fn new() -> Self { Self::default() } pub fn get>(&self, id: &I) -> Option<&I::Widget> where I::Widget: Sized, { self.widgets.get(id) } pub fn get_mut>(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized, { self.widgets.get_mut(id) } pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle { self.textures.add(image) } pub fn register_event( &mut self, id: impl IdLike, event: E, f: impl for<'a> EventFn::Data<'a>>, ) { self.events.register(id.id(), event, f); self.widgets .data_mut(id) .unwrap() .event_mgrs .insert(EventManager::::type_key::()); } pub fn resize(&mut self, size: impl Into) { self.output_size = size.into(); self.resized = true; } pub fn update(&mut self) { if self.full_redraw { DrawState::new(self).redraw_all(); self.full_redraw = false; } else if self.widgets.has_updates() { DrawState::new(self).redraw_updates(); } if self.resized { self.resized = false; DrawState::new(self).redraw_all(); } } /// free any resources that don't have references anymore fn free(&mut self) { for id in self.recv.try_iter() { self.events.remove(id); self.widgets.delete(id); } self.textures.free(); } pub fn needs_redraw(&self) -> bool { self.full_redraw || self.widgets.has_updates() } pub fn num_widgets(&self) -> usize { self.widgets.len() } pub fn active_widgets(&self) -> usize { self.active.len() } pub fn debug_layers(&self) { for ((idx, depth), primitives) in self.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.active.get(&id.id())?.region; Some(region.to_px(self.output_size)) } pub fn debug(&self, label: &str) -> impl Iterator { self.active.iter().filter_map(move |(&id, inst)| { let l = self.widgets.label(id); if l == label { Some(inst) } else { None } }) } } impl> Index for Ui where I::Widget: Sized, { type Output = I::Widget; fn index(&self, id: I) -> &Self::Output { self.get(&id).unwrap() } } impl> IndexMut for Ui where I::Widget: Sized, { fn index_mut(&mut self, id: I) -> &mut Self::Output { self.get_mut(&id).unwrap() } } impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); Self { widgets: Default::default(), events: Default::default(), active: Default::default(), layers: Default::default(), masks: Default::default(), text: Default::default(), textures: Default::default(), cache: Default::default(), output_size: Vec2::ZERO, root: Default::default(), full_redraw: false, send, recv, resized: false, } } }