Add zoom buttons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

author
Marijn Besseling <njirambem@gmail.com> · 2026-10-24 21:57 UTC
commit
8c5f119097f93dcb5ce1661b9f9cb7c51029dc79
parent
fac8d7efb5
tree
browse at this commit

3 files changed +54 -4

src/actions.rs +9 -1

@@ -20,9 +20,11 @@ pub enum Action {
20 20 Cancel,
21 21 NewWorld,
22 22 ToggleUi,
23 + ZoomIn,
24 + ZoomOut,
23 25 }
24 26
25 -pub const KEYBINDS: [(KeyCode, Action); 10] = [
27 +pub const KEYBINDS: [(KeyCode, Action); 12] = [
26 28 (KeyCode::Space, Action::TogglePause),
27 29 (KeyCode::Digit1, Action::SetSpeed(SimSpeed::Normal)),
28 30 (KeyCode::Digit2, Action::SetSpeed(SimSpeed::Fast)),
@@ -33,6 +35,8 @@ pub const KEYBINDS: [(KeyCode, Action); 10] = [
33 35 (KeyCode::Escape, Action::Cancel),
34 36 (KeyCode::KeyR, Action::NewWorld),
35 37 (KeyCode::KeyH, Action::ToggleUi),
38 + (KeyCode::Equal, Action::ZoomIn),
39 + (KeyCode::Minus, Action::ZoomOut),
36 40 ];
37 41
38 42 impl Action {
@@ -48,6 +52,8 @@ impl Action {
48 52 Action::Cancel => "Cancel",
49 53 Action::NewWorld => "New world",
50 54 Action::ToggleUi => "Hide UI",
55 + Action::ZoomIn => "+",
56 + Action::ZoomOut => "-",
51 57 }
52 58 }
53 59
@@ -62,6 +68,8 @@ impl Action {
62 68 match self.key() {
63 69 Some(KeyCode::Space) => "Space".into(),
64 70 Some(KeyCode::Escape) => "Esc".into(),
71 + Some(KeyCode::Equal) => "=".into(),
72 + Some(KeyCode::Minus) => "-".into(),
65 73 Some(key) => format!("{key:?}")
66 74 .trim_start_matches("Digit")
67 75 .trim_start_matches("Key")

src/camera.rs +42 -2

@@ -5,6 +5,7 @@ use bevy::{
5 5 };
6 6
7 7 use crate::{
8 + actions::Action,
8 9 world::WorldMap,
9 10 worldgen::{WorldGenerated, spawn_tiles},
10 11 };
@@ -22,6 +23,7 @@ impl Plugin for CameraPlugin {
22 23 keyboard_pan,
23 24 drag_pan,
24 25 scroll_zoom,
26 + button_zoom,
25 27 clamp_to_world,
26 28 )
27 29 .chain(),
@@ -50,6 +52,30 @@ impl Default for CameraController {
50 52 }
51 53 }
52 54
55 +const BUTTON_ZOOM_LINES: f32 = 2.0;
56 +
57 +impl CameraController {
58 + pub fn zoomed(&self, scale: f32, lines: f32) -> f32 {
59 + (scale * (1.0 - lines * self.zoom_per_line)).clamp(self.min_scale, self.max_scale)
60 + }
61 +}
62 +
63 +fn button_zoom(
64 + mut actions: MessageReader<Action>,
65 + camera: Single<(&mut Projection, &CameraController)>,
66 +) {
67 + let (mut projection, controller) = camera.into_inner();
68 + let projection = ortho(&mut projection);
69 + for action in actions.read() {
70 + let lines = match action {
71 + Action::ZoomIn => BUTTON_ZOOM_LINES,
72 + Action::ZoomOut => -BUTTON_ZOOM_LINES,
73 + _ => continue,
74 + };
75 + projection.scale = controller.zoomed(projection.scale, lines);
76 + }
77 +}
78 +
53 79 fn spawn_camera(mut commands: Commands, map: Res<WorldMap>) {
54 80 commands.spawn((
55 81 Camera2d,
@@ -144,8 +170,7 @@ fn scroll_zoom(
144 170 let (camera, global, mut transform, mut projection, controller) = camera.into_inner();
145 171 let projection = ortho(&mut projection);
146 172 let old_scale = projection.scale;
147 - let new_scale = (old_scale * (1.0 - lines * controller.zoom_per_line))
148 - .clamp(controller.min_scale, controller.max_scale);
173 + let new_scale = controller.zoomed(old_scale, lines);
149 174 projection.scale = new_scale;
150 175
151 176 let focus = window
@@ -188,6 +213,21 @@ mod tests {
188 213 }
189 214
190 215 #[test]
216 + fn zoom_buttons_zoom_in_steps_within_limits() {
217 + let controller = CameraController::default();
218 + assert!(controller.zoomed(1.0, BUTTON_ZOOM_LINES) < 1.0);
219 + assert!(controller.zoomed(1.0, -BUTTON_ZOOM_LINES) > 1.0);
220 + assert_eq!(
221 + controller.zoomed(controller.min_scale, 10.0),
222 + controller.min_scale
223 + );
224 + assert_eq!(
225 + controller.zoomed(controller.max_scale, -10.0),
226 + controller.max_scale
227 + );
228 + }
229 +
230 + #[test]
191 231 fn zooming_keeps_the_point_under_the_cursor_fixed() {
192 232 let camera = Vec2::new(100.0, 50.0);
193 233 let cursor_offset = Vec2::new(200.0, -120.0);

src/ui.rs +3 -1

@@ -34,7 +34,7 @@ impl Plugin for HudPlugin {
34 34 }
35 35 }
36 36
37 -const TOOLBAR: [Action; 10] = [
37 +const TOOLBAR: [Action; 12] = [
38 38 Action::TogglePause,
39 39 Action::SetSpeed(SimSpeed::Normal),
40 40 Action::SetSpeed(SimSpeed::Fast),
@@ -44,6 +44,8 @@ const TOOLBAR: [Action; 10] = [
44 44 Action::ToggleFarmTool,
45 45 Action::Cancel,
46 46 Action::NewWorld,
47 + Action::ZoomIn,
48 + Action::ZoomOut,
47 49 Action::ToggleUi,
48 50 ];
49 51