use std::collections::HashMap; use crate::{ component::Component, layout::{LayoutCache, LayoutNode}, text::TextRenderer, window::{Window, WindowBuilder, WindowComponent, WindowEvent, WindowStateAccess} }; #[derive(Debug)] pub enum UIControlMsg { Terminate, } pub trait UIComponent: Sized + Component { fn init(&mut self, ui_handle: UIHandle); fn poll(&mut self) -> Vec; } pub(crate) struct UIState { pub(crate) layout_cache: std::rc::Rc, pub(crate) window_states: HashMap>, pub(crate) text_renderer: TextRenderer, } pub struct UIHandle<'l> { pub(crate) state: &'l mut UIState, pub(crate) eloop: &'l winit::event_loop::ActiveEventLoop, } impl<'l> UIHandle<'l> { pub fn window_builder<'r>(&'r mut self) -> WindowBuilder<'r, 'l> { WindowBuilder::new(self) } pub fn new_layout_node(&self) -> LayoutNode { LayoutNode::new(self.state.layout_cache.clone()) } } pub struct UI { state: UIState, event_loop: Option>, ui_component: UIC, autoclose: bool, initialized: bool, } impl UI { pub fn new(uic: UIC) -> Self { let lcache = LayoutCache::new(); let eloop = winit::event_loop::EventLoop::builder().build().unwrap(); eloop.set_control_flow(winit::event_loop::ControlFlow::Wait); Self { state: UIState { layout_cache: lcache, window_states: Default::default(), text_renderer: TextRenderer::new(), }, event_loop: Some(eloop), ui_component: uic, autoclose: true, initialized: false, } } pub fn disable_autoclose(mut self) -> Self { self.autoclose = false; self } pub fn ui_component(&self) -> &UIC { &self.ui_component } pub fn ui_component_mut(&mut self) -> &mut UIC { &mut self.ui_component } /// Public helper function, delivers a message to the UI component and then processes whatever /// control messages result. pub fn deliver_msg(&mut self, msg: UIC::Msg) { let cmsgs = self.ui_component.process(msg); self.process_control_msgs(cmsgs.into_iter()); } fn process_control_msgs(&mut self, msgs: impl Iterator) { for cmsg in msgs { match cmsg { UIControlMsg::Terminate => { todo!() // self.ui_running = false; } } } } pub fn run(mut self) { let eloop = self.event_loop.take().unwrap(); eloop.run_app(&mut self).unwrap(); } fn pump_events(&mut self, eloop: &winit::event_loop::ActiveEventLoop) { let evts = self.ui_component.poll(); for evt in evts.into_iter() { match evt { UIControlMsg::Terminate => eloop.exit(), } } } } impl winit::application::ApplicationHandler<()> for UI { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { if !self.initialized { self.ui_component.init(UIHandle { eloop: event_loop, state: &mut self.state, }); self.initialized = true; } } fn window_event( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, window_id: winit::window::WindowId, event: winit::event::WindowEvent, ) { let Some(wsa) = self .state .window_states .get(&window_id) .map(std::rc::Weak::upgrade) .flatten() else { println!("superfluous event: {event:?}"); return; }; match event { winit::event::WindowEvent::CloseRequested => { wsa.push_event(WindowEvent::CloseRequested); self.pump_events(event_loop); } winit::event::WindowEvent::Destroyed => { self.state.window_states.remove(&window_id); } winit::event::WindowEvent::RedrawRequested => { wsa.request_redraw(); self.pump_events(event_loop); } _ => (), } } } pub struct OpinionatedUIComponent<'l, WC: WindowComponent> { window: Option>, builder: Option WC + 'l>>, } impl<'l, WC: WindowComponent> OpinionatedUIComponent<'l, WC> { } impl<'l, WC: WindowComponent> Component for OpinionatedUIComponent<'l, WC> { type ParentMsg = UIControlMsg; type Msg = UIControlMsg; fn process(&mut self, msg: Self::Msg) -> Vec { vec![msg] } } impl<'l, WC: WindowComponent> UIComponent for OpinionatedUIComponent<'l, WC> { fn init(&mut self, mut ui_handle: UIHandle) { if let Some(builder) = self.builder.take() { self.window = Some(ui_handle.window_builder().build(builder)); } } fn poll(&mut self) -> Vec { if let Some(window) = self.window.as_mut() { window.poll() } else { vec![] } } } pub fn make_opinionated_ui<'l, WC: WindowComponent>(wc: impl 'l + FnOnce(&UIHandle) -> WC) -> UI> { let ouic = OpinionatedUIComponent { window: None, builder: Some(Box::new(wc)), }; let ui = UI::new(ouic); ui }