use image::DynamicImage; use crate::{ core::{TextEdit, TextEditCtx}, layout::{ Layers, PainterCtx, Sensor, SensorMap, StaticWidgetId, TextData, TextureHandle, Textures, Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, }, util::{DynBorrower, HashMap, HashSet, Id, IdTracker}, }; use std::{ any::{Any, TypeId}, ops::{Index, IndexMut}, sync::mpsc::{Receiver, Sender, channel}, }; pub struct Ui { root: Option, pub(super) widgets: Widgets, updates: Vec, recv: Receiver, pub(super) send: Sender, size: Vec2, // TODO: make these non pub(crate) pub(crate) layers: Layers, pub(crate) textures: Textures, pub(crate) text: TextData, full_redraw: bool, pub(crate) active: ActiveWidgets, pub(super) sensor_map: SensorMap, } #[derive(Default)] pub struct Widgets { ids: IdTracker, map: HashMap, } pub struct WidgetData { pub widget: Box, pub label: String, pub sized_children: HashSet, /// dynamic borrow checking pub borrowed: bool, /// whether this widget has any sensors pub sensor: 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.widgets.data_mut(&id.id).unwrap().label = label; } pub fn label(&self, id: &WidgetId) -> &String { &self.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.widgets.insert(id.id.duplicate(), w); id } pub fn set(&mut self, id: &WidgetId, w: W) { self.widgets.insert(id.id.duplicate(), 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: &WidgetId) -> Option<&W> { self.widgets.get(id) } pub fn get_mut(&mut self, id: &WidgetId) -> Option<&mut W> { self.widgets.get_mut(id) } pub fn id(&mut self) -> WidgetId { WidgetId::new( self.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.textures.add(image) } pub fn resize(&mut self, size: impl Into) { self.size = size.into(); } pub fn redraw_all(&mut self) { self.active.clear(); // free before bc nothing should exist self.free(); let mut ctx = PainterCtx::new( &self.widgets, &mut self.layers, &mut self.active, &mut self.textures, &mut self.text, self.size, ); 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(); } } fn redraw_updates(&mut self) { let mut ctx = PainterCtx::new( &self.widgets, &mut self.layers, &mut self.active, &mut self.textures, &mut self.text, self.size, ); 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() { self.sensor_map.remove(&id); self.widgets.delete(id); } self.textures.free(); } pub fn needs_redraw(&self) -> bool { self.full_redraw || !self.updates.is_empty() } pub fn num_widgets(&self) -> usize { self.widgets.len() } pub fn active_widgets(&self) -> usize { self.active.len() } pub fn add_sensor(&mut self, id: &WidgetId, f: Sensor) { self.sensor_map .entry(id.id.duplicate()) .or_default() .sensors .push(f); self.widgets.data_mut(&id.id).unwrap().sensor = true; } pub fn text(&mut self, id: &WidgetId) -> TextEditCtx<'_> { self.updates.push(id.id.duplicate()); TextEditCtx { text: self.widgets.get_mut(id).unwrap(), font_system: &mut self.text.font_system, } } } 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.duplicate()); self.get_mut(id).unwrap() } } impl Index> for Ui { type Output = W; fn index(&self, id: StaticWidgetId) -> &Self::Output { self.widgets.get_static(&id).unwrap() } } impl IndexMut> for Ui { fn index_mut(&mut self, id: StaticWidgetId) -> &mut Self::Output { self.updates.push(id.id.id()); self.widgets.get_static_mut(&id).unwrap() } } impl Widgets { pub fn new() -> Self { Self { ids: IdTracker::default(), map: HashMap::new(), } } pub fn get_dyn(&self, id: &Id) -> Option<&dyn Widget> { Some(self.map.get(id)?.widget.as_ref()) } pub fn get_dyn_mut(&mut self, id: &Id) -> Option<&mut dyn Widget> { Some(self.map.get_mut(id)?.widget.as_mut()) } /// get_dyn but dynamic borrow checking of widgets /// lets you do recursive (tree) operations, like the painter does pub fn get_dyn_dynamic(&self, id: &Id) -> WidgetWrapper<'_> { // must guarantee no other mutable references to this widget exist // done through the borrow variable let data: &mut WidgetData = unsafe { std::mem::transmute(self.map.get(id)) }; if data.borrowed { panic!("tried to mutably borrow the same widget twice"); } WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed) } pub fn get_static(&self, id: &StaticWidgetId) -> Option<&W> { self.get_dyn(&id.id.id())?.as_any().downcast_ref() } pub fn get_static_mut(&mut self, id: &StaticWidgetId) -> Option<&mut W> { self.get_dyn_mut(&id.id.id())?.as_any_mut().downcast_mut() } pub fn get(&self, id: &WidgetId) -> Option<&W> { self.get_dyn(&id.id)?.as_any().downcast_ref() } pub fn get_mut(&mut self, id: &WidgetId) -> Option<&mut W> { self.get_dyn_mut(&id.id)?.as_any_mut().downcast_mut() } pub fn insert(&mut self, id: Id, widget: W) { self.insert_any(id, Box::new(widget), std::any::type_name::().to_string()); } pub fn data(&self, id: &Id) -> Option<&WidgetData> { self.map.get(id) } pub fn label(&self, id: &Id) -> &String { &self.data(id).unwrap().label } pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> { self.map.get_mut(id) } pub fn insert_any(&mut self, id: Id, widget: Box, label: String) { self.map.insert( id, WidgetData { widget, label, sized_children: HashSet::new(), borrowed: false, sensor: false, }, ); } pub fn delete(&mut self, id: Id) { self.map.remove(&id); self.ids.free(id); } pub fn reserve(&mut self) -> Id { self.ids.next() } pub fn len(&self) -> usize { self.map.len() } pub fn is_empty(&self) -> bool { self.map.is_empty() } } pub type WidgetWrapper<'a> = DynBorrower<'a, dyn Widget>; 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 { root: Default::default(), widgets: Widgets::new(), updates: Default::default(), layers: Default::default(), textures: Textures::new(), text: TextData::default(), full_redraw: false, active: Default::default(), sensor_map: Default::default(), send, recv, size: Vec2::ZERO, } } } pub type ActiveWidgets = HashMap;