summaryrefslogtreecommitdiffstats
path: root/winit/src/application.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winit/src/application.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/winit/src/application.rs b/winit/src/application.rs
new file mode 100644
index 00000000..418ee3c4
--- /dev/null
+++ b/winit/src/application.rs
@@ -0,0 +1,185 @@
+use crate::{
+ column, conversion, input::mouse, renderer::Windowed, Cache, Column,
+ Element, Event, Length, MouseCursor, UserInterface,
+};
+
+pub trait Application {
+ type Renderer: Windowed + column::Renderer;
+
+ type Message;
+
+ fn update(&mut self, message: Self::Message);
+
+ fn view(&mut self) -> Element<Self::Message, Self::Renderer>;
+
+ fn run(mut self)
+ where
+ Self: 'static + Sized,
+ {
+ use winit::{
+ event::{self, WindowEvent},
+ event_loop::{ControlFlow, EventLoop},
+ window::WindowBuilder,
+ };
+
+ let event_loop = EventLoop::new();
+
+ // TODO: Ask for window settings and configure this properly
+ let window = WindowBuilder::new()
+ .with_inner_size(winit::dpi::LogicalSize {
+ width: 1280.0,
+ height: 1024.0,
+ })
+ .build(&event_loop)
+ .expect("Open window");
+
+ let mut size: Size = window
+ .inner_size()
+ .to_physical(window.hidpi_factor())
+ .into();
+ let mut new_size: Option<Size> = None;
+
+ let mut renderer = Self::Renderer::new(&window);
+ let mut target = renderer.target(size.width, size.height);
+
+ let user_interface = UserInterface::build(
+ document(&mut self, size),
+ Cache::default(),
+ &renderer,
+ );
+
+ let mut primitive = user_interface.draw(&mut renderer);
+ let mut cache = Some(user_interface.into_cache());
+ let mut events = Vec::new();
+ let mut mouse_cursor = MouseCursor::OutOfBounds;
+
+ window.request_redraw();
+
+ event_loop.run(move |event, _, control_flow| match event {
+ event::Event::MainEventsCleared => {
+ // TODO: We should be able to keep a user interface alive
+ // between events once we remove state references.
+ //
+ // This will allow us to rebuild it only when a message is
+ // handled.
+ let mut user_interface = UserInterface::build(
+ document(&mut self, size),
+ cache.take().unwrap(),
+ &renderer,
+ );
+
+ let messages = user_interface.update(events.drain(..));
+
+ if messages.is_empty() {
+ primitive = user_interface.draw(&mut renderer);
+
+ cache = Some(user_interface.into_cache());
+ } else {
+ // When there are messages, we are forced to rebuild twice
+ // for now :^)
+ let temp_cache = user_interface.into_cache();
+
+ for message in messages {
+ log::debug!("Updating");
+
+ self.update(message);
+ }
+
+ let user_interface = UserInterface::build(
+ document(&mut self, size),
+ temp_cache,
+ &renderer,
+ );
+
+ primitive = user_interface.draw(&mut renderer);
+
+ cache = Some(user_interface.into_cache());
+ }
+
+ window.request_redraw();
+ }
+ event::Event::RedrawRequested(_) => {
+ if let Some(new_size) = new_size.take() {
+ target = renderer.target(new_size.width, new_size.height);
+ size = new_size;
+ }
+
+ let new_mouse_cursor = renderer.draw(&primitive, &mut target);
+
+ if new_mouse_cursor != mouse_cursor {
+ window.set_cursor_icon(conversion::mouse_cursor(
+ new_mouse_cursor,
+ ));
+
+ mouse_cursor = new_mouse_cursor;
+ }
+
+ // TODO: Handle animations!
+ // Maybe we can use `ControlFlow::WaitUntil` for this.
+ }
+ event::Event::WindowEvent {
+ event: window_event,
+ ..
+ } => match window_event {
+ WindowEvent::CursorMoved { position, .. } => {
+ let physical_position =
+ position.to_physical(window.hidpi_factor());
+
+ events.push(Event::Mouse(mouse::Event::CursorMoved {
+ x: physical_position.x as f32,
+ y: physical_position.y as f32,
+ }));
+ }
+ WindowEvent::MouseInput { button, state, .. } => {
+ events.push(Event::Mouse(mouse::Event::Input {
+ button: conversion::mouse_button(button),
+ state: conversion::button_state(state),
+ }));
+ }
+ WindowEvent::CloseRequested => {
+ *control_flow = ControlFlow::Exit;
+ }
+ WindowEvent::Resized(size) => {
+ new_size =
+ Some(size.to_physical(window.hidpi_factor()).into());
+
+ log::debug!("Resized: {:?}", new_size);
+ }
+ _ => {}
+ },
+ _ => {
+ *control_flow = ControlFlow::Wait;
+ }
+ })
+ }
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+struct Size {
+ width: u16,
+ height: u16,
+}
+
+impl From<winit::dpi::PhysicalSize> for Size {
+ fn from(physical_size: winit::dpi::PhysicalSize) -> Self {
+ Self {
+ width: physical_size.width.round() as u16,
+ height: physical_size.height.round() as u16,
+ }
+ }
+}
+
+fn document<Application>(
+ application: &mut Application,
+ size: Size,
+) -> Element<Application::Message, Application::Renderer>
+where
+ Application: self::Application,
+ Application::Message: 'static,
+{
+ Column::new()
+ .width(Length::Units(size.width))
+ .height(Length::Units(size.height))
+ .push(application.view())
+ .into()
+}