rvlib/tools/
always_active_zoom.rs

1use std::fmt::Debug;
2
3use rvimage_domain::{BbF, BbI, PtF, ShapeF, ShapeI};
4
5use crate::{
6    events::{Events, KeyCode, ZoomAmount},
7    history::History,
8    make_tool_transform,
9    tools::core::Manipulate,
10    world::World,
11};
12
13use super::{core::Mover, zoom::move_zoom_box};
14
15fn event_move_zoom_box(events: &Events) -> bool {
16    events.held_ctrl() && (events.pressed(KeyCode::MouseLeft) || events.held(KeyCode::MouseLeft))
17}
18fn zoom_box_mouse_wheel(
19    zoom_box: Option<BbF>,
20    shape_orig: ShapeI,
21    ui_image_rect: Option<ShapeF>,
22    amount: ZoomAmount,
23    mouse_pos: Option<PtF>,
24) -> BbF {
25    let current_zb = if let Some(zb) = zoom_box {
26        zb
27    } else {
28        BbI::from_arr(&[0, 0, shape_orig.w, shape_orig.h]).into()
29    };
30    let clip_val = 1.0;
31    let factor = match amount {
32        ZoomAmount::Delta(y_delta) => {
33            let y_delta_clipped = if y_delta > 0.0 {
34                y_delta.min(clip_val)
35            } else {
36                y_delta.max(-clip_val)
37            };
38            1.0 - y_delta_clipped * 0.1
39        }
40        ZoomAmount::Factor(factor) => factor,
41    };
42    let vis_im_shape = current_zb.shape();
43    let ar = |s: ShapeF| s.h / s.w;
44    let (x_factor, y_factor) = match ui_image_rect {
45        Some(uir) => {
46            let diff2off = |ar_diff: f64| (ar_diff / 5.0).abs().min(0.1);
47            let ar_diff = ar(uir) - ar(vis_im_shape);
48            let ar_diff_tolerance = 0.0001;
49            let (xoff, yoff) = if ar_diff > ar_diff_tolerance {
50                let off = diff2off(ar_diff);
51                (off, -off)
52            } else if ar_diff < -ar_diff_tolerance {
53                let off = diff2off(ar_diff);
54                (-off, off)
55            } else {
56                (0.0, 0.0)
57            };
58            if factor < 1.0 {
59                ((factor - xoff).max(0.02), (factor - yoff).max(0.02))
60            } else {
61                (factor, factor)
62            }
63        }
64        None => (factor, factor),
65    };
66    current_zb.center_scale(x_factor, y_factor, shape_orig, mouse_pos)
67}
68
69#[derive(Clone, Debug)]
70pub struct AlwaysActiveZoom {
71    mover: Mover,
72}
73impl AlwaysActiveZoom {
74    fn mouse_pressed(
75        &mut self,
76        events: &Events,
77        world: World,
78        history: History,
79    ) -> (World, History) {
80        if event_move_zoom_box(events) {
81            self.mover.move_mouse_pressed(events.mouse_pos_on_view);
82        }
83        (world, history)
84    }
85
86    fn mouse_held(
87        &mut self,
88        events: &Events,
89        mut world: World,
90        history: History,
91    ) -> (World, History) {
92        if event_move_zoom_box(events) {
93            (self.mover, world) = move_zoom_box(self.mover, world, events.mouse_pos_on_view);
94            (world, history)
95        } else {
96            (world, history)
97        }
98    }
99
100    fn key_released(
101        &mut self,
102        events: &Events,
103        mut world: World,
104        history: History,
105    ) -> (World, History) {
106        if events.held_ctrl() {
107            let zb = if events.released(KeyCode::Key0) {
108                None
109            } else if events.released(KeyCode::PlusEquals) {
110                Some(zoom_box_mouse_wheel(
111                    *world.zoom_box(),
112                    world.shape_orig(),
113                    world.ui_image_rect(),
114                    ZoomAmount::Delta(1.0),
115                    None,
116                ))
117            } else if events.released(KeyCode::Minus) {
118                Some(zoom_box_mouse_wheel(
119                    *world.zoom_box(),
120                    world.shape_orig(),
121                    world.ui_image_rect(),
122                    ZoomAmount::Delta(-1.0),
123                    None,
124                ))
125            } else {
126                *world.zoom_box()
127            };
128            world.set_zoom_box(zb);
129        }
130        (world, history)
131    }
132}
133impl Manipulate for AlwaysActiveZoom {
134    fn new() -> AlwaysActiveZoom {
135        AlwaysActiveZoom {
136            mover: Mover::new(),
137        }
138    }
139    fn has_been_used(&self, events: &Events) -> Option<bool> {
140        let zoomed = events.zoom().is_some()
141            || events.held_ctrl()
142                && (events.released(KeyCode::Key0)
143                    || events.released(KeyCode::PlusEquals)
144                    || events.released(KeyCode::Minus));
145        Some(zoomed || event_move_zoom_box(events))
146    }
147    fn events_tf(
148        &mut self,
149        mut world: World,
150        history: History,
151        events: &Events,
152    ) -> (World, History) {
153        let zoom_factor = events.zoom();
154        if let Some(z) = zoom_factor {
155            let zb = zoom_box_mouse_wheel(
156                *world.zoom_box(),
157                world.shape_orig(),
158                world.ui_image_rect(),
159                z,
160                events.mouse_pos_on_orig,
161            );
162            world.set_zoom_box(Some(zb));
163        }
164        make_tool_transform!(
165            self,
166            world,
167            history,
168            events,
169            [
170                (pressed, KeyCode::MouseLeft, mouse_pressed),
171                (held, KeyCode::MouseLeft, mouse_held),
172                (released, KeyCode::Key0, key_released),
173                (released, KeyCode::PlusEquals, key_released), // Plus is equals
174                (released, KeyCode::Minus, key_released)
175            ]
176        )
177    }
178}
179
180#[test]
181fn test_zb() {
182    fn test(zb: Option<BbF>, y_delta: f64, reference_coords: &[u32; 4]) {
183        println!("y_delta {}", y_delta);
184        let shape = ShapeI::new(200, 100);
185        let zb_new = zoom_box_mouse_wheel(zb, shape, None, ZoomAmount::Delta(y_delta), None);
186        assert_eq!(zb_new, BbI::from_arr(reference_coords).into());
187    }
188    test(None, 1.0, &[10, 5, 180, 90]);
189    test(None, -1.0, &[0, 0, 200, 100]);
190}