Files
iris/core/src/ui/mod.rs
2025-12-17 00:09:20 -05:00

218 lines
5.6 KiB
Rust

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<State> {
// TODO: edit visibilities
pub widgets: Widgets<State>,
pub events: EventManager<State>,
// retained painter state
pub active: HashMap<WidgetId, ActiveData>,
pub layers: PrimitiveLayers,
pub textures: Textures,
pub text: TextData,
output_size: Vec2,
pub masks: TrackedArena<Mask, u32>,
pub cache: Cache,
root: Option<WidgetHandle<State>>,
recv: Receiver<WidgetId>,
send: Sender<WidgetId>,
full_redraw: bool,
resized: bool,
}
pub trait HasUi: Sized {
fn ui_ref(&self) -> &Ui<Self>;
fn ui(&mut self) -> &mut Ui<Self>;
}
impl<State: 'static> Ui<State> {
pub fn add<W: Widget<State>, Tag>(
&mut self,
w: impl WidgetLike<State, Tag, Widget = W>,
) -> WidgetHandle<State, W> {
w.add(self)
}
/// useful for debugging
pub fn set_label(&mut self, id: impl IdLike<State>, label: String) {
self.widgets.data_mut(id.id()).unwrap().label = label;
}
pub fn label(&self, id: impl IdLike<State>) -> &String {
&self.widgets.data(id.id()).unwrap().label
}
pub fn add_widget<W: Widget<State>>(&mut self, w: W) -> WidgetHandle<State, W> {
WidgetHandle::new(self.widgets.add(w), self.send.clone())
}
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<State, Tag>) {
self.root = Some(w.add(self));
self.full_redraw = true;
}
pub fn new() -> Self {
Self::default()
}
pub fn get<I: IdLike<State>>(&self, id: &I) -> Option<&I::Widget>
where
I::Widget: Sized,
{
self.widgets.get(id)
}
pub fn get_mut<I: IdLike<State>>(&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<E: EventLike>(
&mut self,
id: impl IdLike<State>,
event: E,
f: impl for<'a> EventFn<State, <E::Event as Event>::Data<'a>>,
) {
self.events.register(id.id(), event, f);
self.widgets
.data_mut(id)
.unwrap()
.event_mgrs
.insert(EventManager::<State>::type_key::<E>());
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
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<State>) -> Option<PixelRegion> {
let region = self.active.get(&id.id())?.region;
Some(region.to_px(self.output_size))
}
pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> {
self.active.iter().filter_map(move |(&id, inst)| {
let l = self.widgets.label(id);
if l == label { Some(inst) } else { None }
})
}
}
impl<State: 'static, I: IdLike<State>> Index<I> for Ui<State>
where
I::Widget: Sized,
{
type Output = I::Widget;
fn index(&self, id: I) -> &Self::Output {
self.get(&id).unwrap()
}
}
impl<State: 'static, I: IdLike<State>> IndexMut<I> for Ui<State>
where
I::Widget: Sized,
{
fn index_mut(&mut self, id: I) -> &mut Self::Output {
self.get_mut(&id).unwrap()
}
}
impl<State: 'static> Default for Ui<State> {
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,
}
}
}