From c84ff988f3c03320f758b58366de95208580a9d3 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Fri, 27 Oct 2023 15:28:51 -0700 Subject: [PATCH] Added TileMap widget --- src/widgets.rs | 2 + src/widgets/tilemap.rs | 133 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/widgets/tilemap.rs diff --git a/src/widgets.rs b/src/widgets.rs index eaeeb04..6332ac7 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -4,9 +4,11 @@ mod canvas; mod input; mod label; mod style; +mod tilemap; pub use button::Button; pub use canvas::Canvas; pub use input::Input; pub use label::Label; pub use style::Style; +pub use tilemap::TileMap; diff --git a/src/widgets/tilemap.rs b/src/widgets/tilemap.rs new file mode 100644 index 0000000..5c3b6cb --- /dev/null +++ b/src/widgets/tilemap.rs @@ -0,0 +1,133 @@ +use std::fmt::Debug; +use std::panic::UnwindSafe; + +use kludgine::figures::utils::lossy_f64_to_f32; + +use crate::context::{EventContext, GraphicsContext}; +use crate::dynamic::Dynamic; +use crate::kludgine::app::winit::event::{DeviceId, KeyEvent, MouseScrollDelta, TouchPhase}; +use crate::kludgine::app::winit::keyboard::Key; +use crate::kludgine::figures::units::UPx; +use crate::kludgine::figures::Size; +use crate::kludgine::tilemap; +use crate::kludgine::tilemap::TileMapFocus; +use crate::widget::{Callback, EventHandling, IntoValue, Value, Widget, HANDLED, UNHANDLED}; +use crate::ConstraintLimit; + +#[derive(Debug)] +#[must_use] +pub struct TileMap { + layers: Value, + focus: Value, + key: Option>, + zoom: f32, +} + +impl TileMap { + fn construct(layers: Value) -> Self { + Self { + layers, + focus: Value::Constant(TileMapFocus::default()), + zoom: 1., + key: None, + } + } + + pub fn dynamic(layers: Dynamic) -> Self { + Self::construct(Value::Dynamic(layers)) + } + + pub fn new(layers: Layers) -> Self { + Self::construct(Value::Constant(layers)) + } + + pub fn focus_on(mut self, focus: impl IntoValue) -> Self { + self.focus = focus.into_value(); + self + } + + pub fn on_key(mut self, key: F) -> Self + where + F: FnMut(Key) -> EventHandling + Send + UnwindSafe + 'static, + { + self.key = Some(Callback::new(key)); + self + } +} + +impl Widget for TileMap +where + Layers: tilemap::Layers, +{ + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + self.focus.redraw_when_changed(context); + self.layers.redraw_when_changed(context); + + let focus = self.focus.get(); + self.layers + .map(|layers| tilemap::draw(layers, focus, self.zoom, &mut context.graphics)); + } + + fn measure( + &mut self, + available_space: Size, + _context: &mut GraphicsContext<'_, '_, '_, '_, '_>, + ) -> Size { + Size::new(available_space.width.max(), available_space.height.max()) + } + + fn mouse_wheel( + &mut self, + _device_id: DeviceId, + delta: MouseScrollDelta, + _phase: TouchPhase, + context: &mut EventContext<'_, '_>, + ) -> EventHandling { + let amount = match delta { + MouseScrollDelta::LineDelta(_, lines) => lines, + MouseScrollDelta::PixelDelta(px) => lossy_f64_to_f32(px.y) / 16.0, + }; + + self.zoom += self.zoom * 0.1 * amount; + + context.set_needs_redraw(); + HANDLED + } + + fn keyboard_input( + &mut self, + _device_id: DeviceId, + input: KeyEvent, + _is_synthetic: bool, + context: &mut EventContext<'_, '_>, + ) -> EventHandling { + if !input.state.is_pressed() { + return UNHANDLED; + } + if let Some(on_key) = &mut self.key { + on_key.invoke(input.logical_key.clone())?; + } + self.focus.map_mut(|focus| { + if let TileMapFocus::Point(focus) = focus { + match input.logical_key { + Key::ArrowLeft => { + focus.x -= 1; + } + Key::ArrowRight => { + focus.x += 1; + } + Key::ArrowUp => { + focus.y -= 1; + } + Key::ArrowDown => { + focus.y += 1; + } + _ => {} + } + } + }); + + context.set_needs_redraw(); + HANDLED + } +}