123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- 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<Self::Msg>;
- type RootWidget: Widget<Self>;
- fn root_widget(&self) -> &Self::RootWidget;
- fn root_widget_mut(&mut self) -> &mut Self::RootWidget;
- }
- pub(crate) struct WindowState<WC: WindowComponent> {
- render_generation: usize,
- wc: WC,
- root_node: LayoutNode,
- window: Rc<winit::window::Window>,
- events: Vec<WindowEvent>,
- istate: InputState,
- surface: softbuffer::Surface<Rc<winit::window::Window>, Rc<winit::window::Window>>,
- bitmap: RefCell<Option<kahlo::Bitmap<RenderFormat>>>,
- }
- impl<WC: WindowComponent> WindowState<WC> {
- 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::<kahlo::formats::Bgr32>::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<WC::ParentMsg> {
- 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<WC: WindowComponent> WindowStateAccess for RefCell<WindowState<WC>> {
- 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<WC: WindowComponent>(self, wc: impl FnOnce(&mut UIHandle) -> WC) -> Window<WC> {
- 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<dyn WindowStateAccess>)),
- );
- Window { state: wstate }
- }
- }
- pub struct Window<WC: WindowComponent> {
- state: Rc<RefCell<WindowState<WC>>>,
- }
- impl<WC: WindowComponent> Window<WC> {
- pub fn poll(&mut self, uih: &mut UIHandle) -> Vec<WC::ParentMsg> {
- 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<LayoutNodeAccess<'_>> {
- if ndx == 0 {
- Some(self.widget_node)
- } else {
- None
- }
- }
- fn child_count(&self) -> usize {
- 1
- }
- }
|