use image::DynamicImage; use crate::{ core::{TextEdit, TextEditCtx}, layout::{ IdLike, PainterCtx, PainterData, StaticWidgetId, TextureHandle, Vec2, Widget, WidgetId, WidgetLike, }, util::Id, }; use std::{ any::{Any, TypeId}, ops::{Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; pub struct Ui { // TODO: make this at least pub(super) pub(crate) data: PainterData, root: Option, updates: Vec, recv: Receiver, pub(super) send: Sender, full_redraw: bool, resized: bool, } impl Ui { pub fn add(&mut self, w: impl WidgetLike) -> WidgetId { w.add(self) } pub fn add_static( &mut self, w: impl WidgetLike, ) -> StaticWidgetId { let id = w.add(self); id.into_static() } /// useful for debugging pub fn set_label(&mut self, id: &WidgetId, label: String) { self.data.widgets.data_mut(&id.id).unwrap().label = label; } pub fn label(&self, id: &WidgetId) -> &String { &self.data.widgets.data(&id.id).unwrap().label } pub fn add_widget(&mut self, w: W) -> WidgetId { self.push(w) } pub fn push(&mut self, w: W) -> WidgetId { let id = self.id(); self.data.widgets.insert(id.id, w); id } pub fn set(&mut self, id: &WidgetId, w: W) { self.data.widgets.insert(id.id, 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 get(&self, id: &impl IdLike) -> Option<&W> { self.data.widgets.get(id) } pub fn get_mut(&mut self, id: &impl IdLike) -> Option<&mut W> { self.data.widgets.get_mut(id) } pub fn id(&mut self) -> WidgetId { WidgetId::new( self.data.widgets.reserve(), TypeId::of::(), self.send.clone(), false, ) } pub fn id_static(&mut self) -> StaticWidgetId { let id = self.id(); id.into_static() } pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle { self.data.textures.add(image) } pub fn resize(&mut self, size: impl Into) { self.data.output_size = size.into(); self.resized = true; } pub fn redraw_all(&mut self) { self.data.active.clear(); // free before bc nothing should exist self.free(); let mut ctx = PainterCtx::new(&mut self.data); if let Some(root) = &self.root { ctx.draw(root.id); } } pub fn update(&mut self) { if self.full_redraw { self.redraw_all(); self.full_redraw = false; } else if !self.updates.is_empty() { self.redraw_updates(); } if self.resized { self.resized = false; self.redraw_size(); } } fn redraw_size(&mut self) { // let mut ctx = PainterCtx::new(&mut self.data); // let dep = ctx.px_dependent.clone(); // for id in dep { // ctx.redraw(id); // } self.redraw_all(); } fn redraw_updates(&mut self) { // if self.updates.drain(..).next().is_some() { // self.redraw_all(); // } let mut ctx = PainterCtx::new(&mut self.data); for id in self.updates.drain(..) { ctx.redraw(id); } self.free(); } /// free any resources that don't have references anymore fn free(&mut self) { for id in self.recv.try_iter() { 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 text(&mut self, id: &impl IdLike) -> TextEditCtx<'_> { self.updates.push(id.id()); TextEditCtx { text: self.data.widgets.get_mut(id).unwrap(), font_system: &mut self.data.text.font_system, } } pub fn debug(&self, label: &str) { for (id, inst) in &self.data.active { let l = &self.data.widgets.data(id).unwrap().label; if l != label { continue; } println!("\"{label}\" {{"); println!(" region: {}", inst.region); println!(" desired_size: {}", inst.desired_size); println!("}}"); } } } impl Index<&WidgetId> for Ui { type Output = W; fn index(&self, id: &WidgetId) -> &Self::Output { self.get(id).unwrap() } } impl IndexMut<&WidgetId> for Ui { fn index_mut(&mut self, id: &WidgetId) -> &mut Self::Output { self.updates.push(id.id); self.get_mut(id).unwrap() } } impl Index> for Ui { type Output = W; fn index(&self, id: StaticWidgetId) -> &Self::Output { self.data.widgets.get(&id).unwrap() } } impl IndexMut> for Ui { fn index_mut(&mut self, id: StaticWidgetId) -> &mut Self::Output { self.updates.push(id.id); self.data.widgets.get_mut(&id).unwrap() } } impl dyn Widget { pub fn as_any(&self) -> &dyn Any { self } pub fn as_any_mut(&mut self) -> &mut dyn Any { self } } impl Default for Ui { fn default() -> Self { let (send, recv) = channel(); Self { data: PainterData::default(), root: Default::default(), updates: Default::default(), full_redraw: false, send, recv, resized: false, } } }