From 66fd9ba7667ea6fdb3b0c588239ad1253ba8aad7 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Sun, 26 Nov 2023 19:31:03 -0800 Subject: [PATCH] Finished mouse handling for tile map --- examples/tilemap.rs | 21 +++++-------- src/tick.rs | 47 ++++++++++++++++++++++------ src/widgets/tilemap.rs | 70 +++++++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 40 deletions(-) diff --git a/examples/tilemap.rs b/examples/tilemap.rs index f00f928..28938f5 100644 --- a/examples/tilemap.rs +++ b/examples/tilemap.rs @@ -67,7 +67,7 @@ fn main() -> gooey::Result { let one_second_movement = direction * TILE_SIZE.into_float(); - let cursor_pos = input.mouse.pos; + let cursor_pos = input.mouse.as_ref().map(|mouse| mouse.position); layers.map_mut(|layers| { let pos = &mut layers.1[myself].position; @@ -76,21 +76,16 @@ fn main() -> gooey::Result { one_second_movement.y * elapsed.as_secs_f32(), ); - let rect = Rect::new(*pos, Size::new(16., 16.)); - let color = match rect.cast().contains(cursor_pos) { - true => Color::RED, - false => Color::BLUE, - }; + let rect = Rect::new(*pos - Size::squared(8.), Size::squared(16.)); + layers.1[myself].color = + match cursor_pos.map_or(false, |cursor_pos| rect.cast().contains(cursor_pos)) { + true => Color::RED, + false => Color::BLUE, + }; }); })); - Stack::rows( - tilemap - .debug_output(debug_message.clone()) - .expand() - .and(Label::new(debug_message)), - ) - .run() + Stack::rows(tilemap.expand().and(Label::new(debug_message))).run() } #[derive(Debug)] diff --git a/src/tick.rs b/src/tick.rs index 6a16be2..e910c9e 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -3,10 +3,11 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, Instant}; -use kludgine::app::winit::event::KeyEvent; +use intentional::Assert; +use kludgine::app::winit::event::{ElementState, KeyEvent, MouseButton}; use kludgine::app::winit::keyboard::Key; -use kludgine::figures::Point; use kludgine::figures::units::Px; +use kludgine::figures::Point; use crate::context::WidgetContext; use crate::utils::{IgnorePoison, UnwindsafeCondvar}; @@ -51,9 +52,38 @@ impl Tick { } } - pub fn set_cursor_position(&self, pos: Point) { + /// Sets the cursor position. + pub fn set_cursor_position(&self, pos: Option>) { let mut state = self.data.state(); - state.input.mouse.pos = pos; + match pos { + Some(pos) => { + if state.input.mouse.is_none() { + state.input.mouse = Some(Mouse::default()); + } + + state + .input + .mouse + .as_mut() + .assert("always initialized") + .position = pos; + } + None => { + state.input.mouse = None; + } + } + } + + /// Processes a mouse button event. + pub fn mouse_button(&self, button: MouseButton, button_state: ElementState) { + let mut state = self.data.state(); + if let Some(mouse) = &mut state.input.mouse { + if button_state.is_pressed() { + mouse.buttons.insert(button); + } else { + mouse.buttons.remove(&button); + } + } } /// Returns a new tick that invokes `tick`, aiming to repeat at the given @@ -70,7 +100,6 @@ impl Tick { keep_running: true, frame: 0, input: InputState::default(), - mouse: None, }), period: tick_every, sync: UnwindsafeCondvar::new(), @@ -119,15 +148,16 @@ impl Tick { pub struct InputState { /// A collection of all keys currently pressed. pub keys: HashSet, - pub mouse: Mouse, + /// The state of the mouse cursor and any buttons pressed. + pub mouse: Option, } #[derive(Debug, Default)] pub struct Mouse { - pub pos: Point, + pub position: Point, + pub buttons: HashSet, } - #[derive(Debug)] struct TickData { state: Mutex, @@ -150,7 +180,6 @@ struct TickState { keep_running: bool, frame: usize, input: InputState, - mouse: Option, } fn tick_loop(data: &TickData, mut tick: F) diff --git a/src/widgets/tilemap.rs b/src/widgets/tilemap.rs index f662e7d..3a75e72 100644 --- a/src/widgets/tilemap.rs +++ b/src/widgets/tilemap.rs @@ -1,6 +1,8 @@ use std::fmt::Debug; use intentional::Cast; +use kludgine::app::winit::event::ElementState; +use kludgine::app::winit::window::CursorIcon; use kludgine::figures::units::Px; use kludgine::figures::Point; @@ -23,7 +25,6 @@ pub struct TileMap { focus: Value, zoom: f32, tick: Option, - debug_output: Option>, } impl TileMap { @@ -33,15 +34,9 @@ impl TileMap { focus: Value::default(), zoom: 1., tick: None, - debug_output: None, } } - pub fn debug_output(mut self, message: Dynamic) -> Self { - self.debug_output = Some(message); - self - } - /// Returns a new tilemap that contains dynamic layers. pub fn dynamic(layers: Dynamic) -> Self { Self::construct(Value::Dynamic(layers)) @@ -126,20 +121,32 @@ where HANDLED } - fn hover(&mut self, local: Point, context: &mut EventContext<'_, '_>) { - let Some(size) = context.last_layout().map(|rect| rect.size) else { return }; - - let offset = self.layers.map(|layers| self.focus.get().world_coordinate(layers)); - - let scale = context.kludgine.scale(); - let zoom = self.zoom; - let world = tilemap::translate_coordinates(local, offset, scale, zoom, size); - + fn hover( + &mut self, + local: Point, + context: &mut EventContext<'_, '_>, + ) -> Option { if let Some(tick) = &self.tick { - tick.set_cursor_position(world); + let Some(size) = context.last_layout().map(|rect| rect.size) else { + return None; + }; + + let world = + tilemap::translate_coordinates(local, context.kludgine.scale(), self.zoom, size); + let offset = self + .layers + .map(|layers| self.focus.get().world_coordinate(layers)); + + tick.set_cursor_position(Some(world + offset)); } - self.debug_output.as_ref().unwrap().set(format!("world: {world:?} | local: {local:?}")); + None + } + + fn unhover(&mut self, _context: &mut EventContext<'_, '_>) { + if let Some(tick) = &self.tick { + tick.set_cursor_position(None); + } } fn keyboard_input( @@ -155,4 +162,31 @@ where IGNORED } + + fn mouse_down( + &mut self, + _location: Point, + _device_id: DeviceId, + button: kludgine::app::winit::event::MouseButton, + _context: &mut EventContext<'_, '_>, + ) -> EventHandling { + if let Some(tick) = &self.tick { + tick.mouse_button(button, ElementState::Pressed); + HANDLED + } else { + IGNORED + } + } + + fn mouse_up( + &mut self, + _location: Option>, + _device_id: DeviceId, + button: kludgine::app::winit::event::MouseButton, + _context: &mut EventContext<'_, '_>, + ) { + if let Some(tick) = &self.tick { + tick.mouse_button(button, ElementState::Released); + } + } }