/* * Copyright (C) Oscar Shrimpton 2020 * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ use crate::Renderer; use egui::Context; use legion::systems::Runnable; use log::debug; use std::sync::Arc; use stockton_levels::prelude::{MinBspFeatures, VulkanSystem}; use egui::{Output, PaintJobs, Pos2, RawInput, Ui}; use stockton_input::{Action as KBAction, InputManager, Mouse}; use winit::event::{ ElementState, Event as WinitEvent, MouseButton, WindowEvent as WinitWindowEvent, }; use winit::event_loop::ControlFlow; #[derive(Debug, Clone, Copy)] pub enum WindowEvent { SizeChanged(u32, u32), CloseRequested, KeyboardAction(KBAction), MouseAction(KBAction), MouseMoved(f32, f32), MouseLeft, } impl WindowEvent { pub fn from(winit_event: &WinitEvent<()>) -> Option { // TODO match winit_event { WinitEvent::WindowEvent { event, .. } => match event { WinitWindowEvent::CloseRequested => Some(WindowEvent::CloseRequested), WinitWindowEvent::Resized(size) => { Some(WindowEvent::SizeChanged(size.width, size.height)) } WinitWindowEvent::KeyboardInput { input, .. } => match input.state { ElementState::Pressed => Some(WindowEvent::KeyboardAction(KBAction::KeyPress( input.scancode, ))), ElementState::Released => Some(WindowEvent::KeyboardAction( KBAction::KeyRelease(input.scancode), )), }, WinitWindowEvent::CursorMoved { position, .. } => Some(WindowEvent::MouseMoved( position.x as f32, position.y as f32, )), WinitWindowEvent::CursorLeft { .. } => Some(WindowEvent::MouseLeft), WinitWindowEvent::MouseInput { button, state, .. } => { let mb: stockton_input::MouseButton = match button { MouseButton::Left => stockton_input::MouseButton::Left, MouseButton::Right => stockton_input::MouseButton::Right, MouseButton::Middle => stockton_input::MouseButton::Middle, MouseButton::Other(x) => stockton_input::MouseButton::Other(*x), }; match state { ElementState::Pressed => { Some(WindowEvent::MouseAction(KBAction::MousePress(mb))) } ElementState::Released => { Some(WindowEvent::MouseAction(KBAction::MouseRelease(mb))) } } } _ => None, }, _ => None, } } } pub struct UiState { pub(crate) ctx: Arc, pub(crate) raw_input: RawInput, ui: Option, pub(crate) last_tex_ver: u64, } impl UiState { pub fn ui(&mut self) -> &mut Ui { if self.ui.is_none() { self.ui = Some(self.begin_frame()); } self.ui.as_mut().unwrap() } fn begin_frame(&mut self) -> Ui { self.ctx.begin_frame(self.raw_input.take()) } pub fn end_frame(&mut self) -> (Output, PaintJobs) { self.ui = None; self.ctx.end_frame() } fn set_mouse_pos(&mut self, x: f32, y: f32) { self.raw_input.mouse_pos = Some(Pos2 { x, y }) } fn set_mouse_left(&mut self) { self.raw_input.mouse_pos = None; } fn set_dimensions(&mut self, w: u32, h: u32) { self.raw_input.screen_size = egui::math::Vec2 { x: w as f32, y: h as f32, } } fn set_pixels_per_point(&mut self, ppp: Option) { self.raw_input.pixels_per_point = ppp; } pub fn dimensions(&self) -> egui::math::Vec2 { self.raw_input.screen_size } fn handle_action(&mut self, action: KBAction) { // TODO match action { KBAction::MousePress(stockton_input::MouseButton::Left) => { self.raw_input.mouse_down = true; } KBAction::MouseRelease(stockton_input::MouseButton::Right) => { self.raw_input.mouse_down = false; } _ => (), } } pub fn new>(renderer: &Renderer) -> Self { let mut state = UiState { ctx: Context::new(), raw_input: RawInput::default(), ui: None, last_tex_ver: 0, }; let props = &renderer.context.target_chain.properties; state.set_dimensions(props.extent.width, props.extent.height); state.set_pixels_per_point(Some(renderer.context.pixels_per_point)); debug!("{:?}", state.raw_input); state } } #[system] /// A system to process the window events sent to renderer by the winit event loop. pub fn _process_window_events< T: 'static + InputManager, M: 'static + MinBspFeatures, >( #[resource] renderer: &mut Renderer<'static, M>, #[resource] manager: &mut T, #[resource] mouse: &mut Mouse, #[resource] ui_state: &mut UiState, #[state] actions_buf: &mut Vec, ) { let mut actions_buf_cursor = 0; let mut mouse_delta = mouse.abs; while let Ok(event) = renderer.window_events.try_recv() { match event { WindowEvent::SizeChanged(w, h) => { renderer.resize(); ui_state.set_dimensions(w, h); } WindowEvent::CloseRequested => { let mut flow = renderer.update_control_flow.write().unwrap(); // TODO: Let everything know this is our last frame *flow = ControlFlow::Exit; } WindowEvent::KeyboardAction(action) => { if actions_buf_cursor >= actions_buf.len() { actions_buf.push(action); } else { actions_buf[actions_buf_cursor] = action; } actions_buf_cursor += 1; ui_state.handle_action(action); } WindowEvent::MouseMoved(x, y) => { mouse_delta.x = x; mouse_delta.y = y; ui_state.set_mouse_pos(x, y); } WindowEvent::MouseLeft => { ui_state.set_mouse_left(); } WindowEvent::MouseAction(action) => { if actions_buf_cursor >= actions_buf.len() { actions_buf.push(action); } else { actions_buf[actions_buf_cursor] = action; } actions_buf_cursor += 1; ui_state.handle_action(action); } }; } mouse.handle_frame(mouse_delta); manager.handle_frame(&actions_buf[0..actions_buf_cursor]); } pub fn process_window_events_system< T: 'static + InputManager, M: 'static + MinBspFeatures, >() -> impl Runnable { _process_window_events_system::(Vec::with_capacity(4)) }