window.rs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. use std::cell::RefCell;
  2. use std::rc::Rc;
  3. use kahlo::{
  4. math::{PixelBox, PixelPoint, PixelSize},
  5. prelude::*,
  6. };
  7. use crate::layout::{self, LayoutNode, LayoutNodeAccess, LayoutTreeNode};
  8. use crate::widget::Widget;
  9. use crate::{component::Component, ui::UIHandle};
  10. use crate::{
  11. input::{InputState, MouseButton},
  12. render::RenderTarget,
  13. };
  14. pub type RenderFormat = kahlo::formats::Bgr32;
  15. #[derive(Debug)]
  16. pub enum WindowEvent {
  17. CloseRequested,
  18. Resized,
  19. }
  20. pub trait WindowComponent: 'static + Sized + Component {
  21. fn map_window_event(&self, we: WindowEvent) -> Option<Self::Msg>;
  22. type RootWidget: Widget<Self>;
  23. fn root_widget(&self) -> &Self::RootWidget;
  24. fn root_widget_mut(&mut self) -> &mut Self::RootWidget;
  25. }
  26. pub(crate) struct WindowState<WC: WindowComponent> {
  27. render_generation: usize,
  28. wc: WC,
  29. root_node: LayoutNode,
  30. window: Rc<winit::window::Window>,
  31. events: Vec<WindowEvent>,
  32. istate: InputState,
  33. surface: softbuffer::Surface<Rc<winit::window::Window>, Rc<winit::window::Window>>,
  34. bitmap: RefCell<Option<kahlo::Bitmap<RenderFormat>>>,
  35. }
  36. impl<WC: WindowComponent> WindowState<WC> {
  37. fn redraw(&mut self, uih: &UIHandle) {
  38. let size = self.window.inner_size();
  39. // if size.width != self.surface.buffer_mut().unwrap().
  40. self.surface
  41. .resize(
  42. size.width.try_into().unwrap(),
  43. size.height.try_into().unwrap(),
  44. )
  45. .unwrap();
  46. let mut buf = self.surface.buffer_mut().unwrap();
  47. self.root_node.set_behaviour(layout::NodeBehaviour::Fixed {
  48. area: PixelBox::from_size(PixelSize::new(size.width as i32, size.height as i32)),
  49. });
  50. let layout = WindowLayoutHelper::new(&self.root_node, self.wc.root_widget().layout_node());
  51. let mut bitmap = self.bitmap.borrow_mut();
  52. if bitmap.as_ref().map(|v| v.size())
  53. != Some(PixelSize::new(size.width as i32, size.height as i32))
  54. {
  55. bitmap.take();
  56. }
  57. let bitmap = bitmap
  58. .get_or_insert_with(|| kahlo::Bitmap::new(size.width as usize, size.height as usize));
  59. let mut windowbuffer = kahlo::BitmapMut::<kahlo::formats::Bgr32>::new(
  60. unsafe {
  61. std::slice::from_raw_parts_mut(buf[..].as_mut_ptr() as *mut u8, buf.len() * 4)
  62. },
  63. size.width as usize,
  64. size.height as usize,
  65. );
  66. layout::recalculate(LayoutNodeAccess::new(&layout));
  67. // let before = std::time::Instant::now();
  68. // save the layout tree for later dumping
  69. let mut pre_render_dump = format!("render generation: {}\n", self.render_generation);
  70. {
  71. self.render_generation += 1;
  72. crate::layout::dump_node_tree(LayoutNodeAccess::new(&layout), &mut pre_render_dump);
  73. }
  74. let mut bmap = bitmap.as_mut();
  75. let mut render_target = RenderTarget::new(uih.theme(), &mut bmap);
  76. self.wc.root_widget().render(&mut render_target);
  77. windowbuffer.copy_from(
  78. bitmap,
  79. PixelBox::from_size(bitmap.size()),
  80. PixelPoint::zero(),
  81. );
  82. // let after = std::time::Instant::now();
  83. /*
  84. // put the render time on the screen
  85. // we're going to do this the very dumb way for now
  86. let render_time = format!(
  87. "time: {:.03}ms",
  88. (after - before).as_micros() as f32 / 1000.0
  89. );
  90. let (line, _) = uih.theme().make_line(render_time.as_str()).render_line();
  91. windowbuffer.fill_region_masked(
  92. &line,
  93. PixelBox::from_size(line.size()),
  94. PixelPoint::new(
  95. size.width as i32 - line.size().width,
  96. size.height as i32 - line.size().height,
  97. ),
  98. uih.theme().foreground,
  99. kahlo::colour::BlendMode::Simple,
  100. );*/
  101. // now put the pre-render layout dump on the window
  102. // again, we're doing this the dumb way
  103. /*let mut offset = 0;
  104. for text in pre_render_dump.split('\n') {
  105. if text.is_empty() {
  106. continue;
  107. }
  108. let line = uih.theme().make_line(text).render_line();
  109. windowbuffer.fill_region_masked(
  110. &line,
  111. PixelBox::from_size(line.size()),
  112. PixelPoint::new(0, offset),
  113. uih.theme().foreground,
  114. kahlo::colour::BlendMode::Simple,
  115. );
  116. offset += line.height() as i32 + 1;
  117. }*/
  118. buf.present().unwrap();
  119. }
  120. fn poll(&mut self, uih: &mut UIHandle) -> Vec<WC::ParentMsg> {
  121. let mut ret = vec![];
  122. for we in self.events.drain(..) {
  123. let mapped = self.wc.map_window_event(we);
  124. ret.extend(self.wc.process_all(mapped.into_iter()).into_iter());
  125. }
  126. let evts = self.wc.root_widget_mut().poll(uih, Some(&self.istate));
  127. ret.extend(self.wc.process_all(evts.into_iter()));
  128. self.istate.tick();
  129. ret
  130. }
  131. }
  132. pub(crate) trait WindowStateAccess {
  133. fn notify_resize(&self, new_size: PixelSize);
  134. fn push_event(&self, we: WindowEvent);
  135. fn redraw(&self, uih: &UIHandle);
  136. fn request_redraw(&self);
  137. fn update_mouse_pos(&self, pos: PixelPoint);
  138. fn update_mouse_button(&self, which: MouseButton, to: bool);
  139. fn update_text_input(&self, what: &str);
  140. fn push_keypress(&self, key: winit::keyboard::Key);
  141. }
  142. impl<WC: WindowComponent> WindowStateAccess for RefCell<WindowState<WC>> {
  143. fn notify_resize(&self, new_size: PixelSize) {
  144. if Some(new_size) != self.borrow().bitmap.borrow().as_ref().map(|v| v.size()) {
  145. self.borrow_mut().bitmap.take();
  146. self.borrow_mut()
  147. .wc
  148. .root_widget()
  149. .layout_node()
  150. .relayout_tree();
  151. self.borrow_mut()
  152. .wc
  153. .root_widget()
  154. .layout_node()
  155. .render_needed();
  156. }
  157. }
  158. fn push_event(&self, we: WindowEvent) {
  159. self.borrow_mut().events.push(we);
  160. }
  161. fn redraw(&self, uih: &UIHandle) {
  162. self.borrow_mut().redraw(uih);
  163. }
  164. fn request_redraw(&self) {
  165. self.borrow().window.request_redraw();
  166. }
  167. fn update_mouse_pos(&self, pos: PixelPoint) {
  168. self.borrow_mut().istate.mouse.pos = pos;
  169. }
  170. fn update_mouse_button(&self, which: MouseButton, to: bool) {
  171. let is = &mut self.borrow_mut().istate;
  172. is.mouse.buttons.set_button(which, to);
  173. }
  174. fn update_text_input(&self, what: &str) {
  175. let is = &mut self.borrow_mut().istate;
  176. is.set_text_input(what.into());
  177. }
  178. fn push_keypress(&self, key: winit::keyboard::Key) {
  179. let is = &mut self.borrow_mut().istate;
  180. is.keypress = Some(key);
  181. }
  182. }
  183. pub struct WindowBuilder<'r, 'l: 'r> {
  184. ui_handle: &'r mut UIHandle<'l>,
  185. }
  186. impl<'r, 'l: 'r> WindowBuilder<'r, 'l> {
  187. pub(crate) fn new(ui_handle: &'r mut UIHandle<'l>) -> Self {
  188. Self { ui_handle }
  189. }
  190. pub fn build<WC: WindowComponent>(self, wc: impl FnOnce(&mut UIHandle) -> WC) -> Window<WC> {
  191. let window = Rc::new(
  192. self.ui_handle
  193. .eloop
  194. .create_window(winit::window::WindowAttributes::default())
  195. .unwrap(),
  196. );
  197. let wid = window.id();
  198. let ctx = softbuffer::Context::new(window.clone()).unwrap();
  199. let surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap();
  200. let wc = wc(self.ui_handle);
  201. let wstate = Rc::new(RefCell::new(WindowState {
  202. render_generation: 0,
  203. wc,
  204. root_node: LayoutNode::new(self.ui_handle.state.layout_cache.clone()),
  205. window,
  206. events: Default::default(),
  207. istate: InputState::default(),
  208. surface,
  209. bitmap: None.into(),
  210. }));
  211. self.ui_handle.state.window_states.insert(
  212. wid,
  213. Rc::downgrade(&(wstate.clone() as Rc<dyn WindowStateAccess>)),
  214. );
  215. Window { state: wstate }
  216. }
  217. }
  218. pub struct Window<WC: WindowComponent> {
  219. state: Rc<RefCell<WindowState<WC>>>,
  220. }
  221. impl<WC: WindowComponent> Window<WC> {
  222. pub fn poll(&mut self, uih: &mut UIHandle) -> Vec<WC::ParentMsg> {
  223. self.state.borrow_mut().poll(uih)
  224. }
  225. }
  226. struct WindowLayoutHelper<'l> {
  227. window_node: &'l LayoutNode,
  228. widget_node: LayoutNodeAccess<'l>,
  229. }
  230. impl<'l> WindowLayoutHelper<'l> {
  231. fn new(window: &'l LayoutNode, widget: LayoutNodeAccess<'l>) -> Self {
  232. Self {
  233. window_node: window,
  234. widget_node: widget,
  235. }
  236. }
  237. }
  238. impl<'l> LayoutTreeNode<()> for WindowLayoutHelper<'l> {
  239. fn current_node(&self) -> &LayoutNode {
  240. self.window_node
  241. }
  242. fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
  243. if ndx == 0 {
  244. Some(self.widget_node)
  245. } else {
  246. None
  247. }
  248. }
  249. fn child_count(&self) -> usize {
  250. 1
  251. }
  252. }