use std::cell::RefCell; use std::rc::Rc; use kahlo::{ math::{PixelBox, PixelPoint, PixelSize}, prelude::*, }; use crate::layout::{self, LayoutNode, LayoutNodeAccess, LayoutTreeNode}; use crate::widget::Widget; use crate::{component::Component, ui::UIHandle}; use crate::{ input::{InputState, MouseButton}, render::RenderTarget, }; pub type RenderFormat = kahlo::formats::Bgr32; #[derive(Debug)] pub enum WindowEvent { CloseRequested, Resized, } pub trait WindowComponent: 'static + Sized + Component { fn map_window_event(&self, we: WindowEvent) -> Option; type RootWidget: Widget; fn root_widget(&self) -> &Self::RootWidget; fn root_widget_mut(&mut self) -> &mut Self::RootWidget; } pub(crate) struct WindowState { render_generation: usize, wc: WC, root_node: LayoutNode, window: Rc, events: Vec, istate: InputState, surface: softbuffer::Surface, Rc>, bitmap: RefCell>>, } impl WindowState { fn redraw(&mut self, uih: &UIHandle) { let size = self.window.inner_size(); // if size.width != self.surface.buffer_mut().unwrap(). self.surface .resize( size.width.try_into().unwrap(), size.height.try_into().unwrap(), ) .unwrap(); let mut buf = self.surface.buffer_mut().unwrap(); self.root_node.set_behaviour(layout::NodeBehaviour::Fixed { area: PixelBox::from_size(PixelSize::new(size.width as i32, size.height as i32)), }); let layout = WindowLayoutHelper::new(&self.root_node, self.wc.root_widget().layout_node()); let mut bitmap = self.bitmap.borrow_mut(); if bitmap.as_ref().map(|v| v.size()) != Some(PixelSize::new(size.width as i32, size.height as i32)) { bitmap.take(); } let bitmap = bitmap .get_or_insert_with(|| kahlo::Bitmap::new(size.width as usize, size.height as usize)); let mut windowbuffer = kahlo::BitmapMut::::new( unsafe { std::slice::from_raw_parts_mut(buf[..].as_mut_ptr() as *mut u8, buf.len() * 4) }, size.width as usize, size.height as usize, ); layout::recalculate(LayoutNodeAccess::new(&layout)); // let before = std::time::Instant::now(); // save the layout tree for later dumping let mut pre_render_dump = format!("render generation: {}\n", self.render_generation); { self.render_generation += 1; crate::layout::dump_node_tree(LayoutNodeAccess::new(&layout), &mut pre_render_dump); } let mut bmap = bitmap.as_mut(); let mut render_target = RenderTarget::new(uih.theme(), &mut bmap); self.wc.root_widget().render(&mut render_target); windowbuffer.copy_from( bitmap, PixelBox::from_size(bitmap.size()), PixelPoint::zero(), ); // let after = std::time::Instant::now(); /* // put the render time on the screen // we're going to do this the very dumb way for now let render_time = format!( "time: {:.03}ms", (after - before).as_micros() as f32 / 1000.0 ); let (line, _) = uih.theme().make_line(render_time.as_str()).render_line(); windowbuffer.fill_region_masked( &line, PixelBox::from_size(line.size()), PixelPoint::new( size.width as i32 - line.size().width, size.height as i32 - line.size().height, ), uih.theme().foreground, kahlo::colour::BlendMode::Simple, );*/ // now put the pre-render layout dump on the window // again, we're doing this the dumb way /*let mut offset = 0; for text in pre_render_dump.split('\n') { if text.is_empty() { continue; } let line = uih.theme().make_line(text).render_line(); windowbuffer.fill_region_masked( &line, PixelBox::from_size(line.size()), PixelPoint::new(0, offset), uih.theme().foreground, kahlo::colour::BlendMode::Simple, ); offset += line.height() as i32 + 1; }*/ buf.present().unwrap(); } fn poll(&mut self, uih: &mut UIHandle) -> Vec { let mut ret = vec![]; for we in self.events.drain(..) { let mapped = self.wc.map_window_event(we); ret.extend(self.wc.process_all(mapped.into_iter()).into_iter()); } let evts = self.wc.root_widget_mut().poll(uih, Some(&self.istate)); ret.extend(self.wc.process_all(evts.into_iter())); self.istate.tick(); ret } } pub(crate) trait WindowStateAccess { fn notify_resize(&self, new_size: PixelSize); fn push_event(&self, we: WindowEvent); fn redraw(&self, uih: &UIHandle); fn request_redraw(&self); fn update_mouse_pos(&self, pos: PixelPoint); fn update_mouse_button(&self, which: MouseButton, to: bool); fn update_text_input(&self, what: &str); fn push_keypress(&self, key: winit::keyboard::Key); } impl WindowStateAccess for RefCell> { fn notify_resize(&self, new_size: PixelSize) { if Some(new_size) != self.borrow().bitmap.borrow().as_ref().map(|v| v.size()) { self.borrow_mut().bitmap.take(); self.borrow_mut() .wc .root_widget() .layout_node() .relayout_tree(); self.borrow_mut() .wc .root_widget() .layout_node() .render_needed(); } } fn push_event(&self, we: WindowEvent) { self.borrow_mut().events.push(we); } fn redraw(&self, uih: &UIHandle) { self.borrow_mut().redraw(uih); } fn request_redraw(&self) { self.borrow().window.request_redraw(); } fn update_mouse_pos(&self, pos: PixelPoint) { self.borrow_mut().istate.mouse.pos = pos; } fn update_mouse_button(&self, which: MouseButton, to: bool) { let is = &mut self.borrow_mut().istate; is.mouse.buttons.set_button(which, to); } fn update_text_input(&self, what: &str) { let is = &mut self.borrow_mut().istate; is.set_text_input(what.into()); } fn push_keypress(&self, key: winit::keyboard::Key) { let is = &mut self.borrow_mut().istate; is.keypress = Some(key); } } pub struct WindowBuilder<'r, 'l: 'r> { ui_handle: &'r mut UIHandle<'l>, } impl<'r, 'l: 'r> WindowBuilder<'r, 'l> { pub(crate) fn new(ui_handle: &'r mut UIHandle<'l>) -> Self { Self { ui_handle } } pub fn build(self, wc: impl FnOnce(&mut UIHandle) -> WC) -> Window { let window = Rc::new( self.ui_handle .eloop .create_window(winit::window::WindowAttributes::default()) .unwrap(), ); let wid = window.id(); let ctx = softbuffer::Context::new(window.clone()).unwrap(); let surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); let wc = wc(self.ui_handle); let wstate = Rc::new(RefCell::new(WindowState { render_generation: 0, wc, root_node: LayoutNode::new(self.ui_handle.state.layout_cache.clone()), window, events: Default::default(), istate: InputState::default(), surface, bitmap: None.into(), })); self.ui_handle.state.window_states.insert( wid, Rc::downgrade(&(wstate.clone() as Rc)), ); Window { state: wstate } } } pub struct Window { state: Rc>>, } impl Window { pub fn poll(&mut self, uih: &mut UIHandle) -> Vec { self.state.borrow_mut().poll(uih) } } struct WindowLayoutHelper<'l> { window_node: &'l LayoutNode, widget_node: LayoutNodeAccess<'l>, } impl<'l> WindowLayoutHelper<'l> { fn new(window: &'l LayoutNode, widget: LayoutNodeAccess<'l>) -> Self { Self { window_node: window, widget_node: widget, } } } impl<'l> LayoutTreeNode<()> for WindowLayoutHelper<'l> { fn current_node(&self) -> &LayoutNode { self.window_node } fn child(&self, ndx: usize) -> Option> { if ndx == 0 { Some(self.widget_node) } else { None } } fn child_count(&self) -> usize { 1 } }