Przeglądaj źródła

feat: y-axis zoom event on heatmap chart via mouse drag alongside the wheel scroll

Berke 1 rok temu
rodzic
commit
663dad6792
4 zmienionych plików z 70 dodań i 51 usunięć
  1. 54 23
      src/charts.rs
  2. 2 8
      src/charts/candlestick.rs
  3. 2 8
      src/charts/footprint.rs
  4. 12 12
      src/charts/heatmap.rs

+ 54 - 23
src/charts.rs

@@ -19,7 +19,7 @@ pub enum Message {
     AutoscaleToggle,
     CrosshairToggle,
     CrosshairMoved(Point),
-    YScaling(f32),
+    YScaling(f32, bool),
 }
 struct CommonChartData {
     main_cache: Cache,
@@ -86,8 +86,7 @@ trait Chart {
 #[derive(Debug, Clone, Copy)]
 pub enum Interaction {
     None,
-    Drawing,
-    Erasing,
+    Zoomin { last_position: Point },
     Panning { translation: Vector, start: Point },
 }
 impl Default for Interaction {
@@ -117,7 +116,7 @@ fn chart_button(theme: &Theme, _status: button::Status, is_active: bool) -> butt
     }
 }
 
-// price steps, to be used for y-axis labels on all charts
+// price steps, to be used for y-axis labels across all charts
 const PRICE_STEPS: [f32; 15] = [
     1000.0,
     500.0,
@@ -391,9 +390,8 @@ impl canvas::Program<Message> for AxisLabelXCanvas<'_> {
         cursor: mouse::Cursor,
     ) -> mouse::Interaction {
         match interaction {
-            Interaction::Drawing => mouse::Interaction::Crosshair,
-            Interaction::Erasing => mouse::Interaction::Crosshair,
-            Interaction::Panning { .. } => mouse::Interaction::ResizingHorizontally,
+            Interaction::Panning { .. } => mouse::Interaction::None,
+            Interaction::Zoomin { .. } => mouse::Interaction::ResizingHorizontally,
             Interaction::None if cursor.is_over(bounds) => {
                 mouse::Interaction::ResizingHorizontally
             }
@@ -415,28 +413,62 @@ impl canvas::Program<Message> for AxisLabelYCanvas<'_> {
 
     fn update(
         &self,
-        _interaction: &mut Interaction,
+        interaction: &mut Interaction,
         event: Event,
         bounds: Rectangle,
         cursor: mouse::Cursor,
     ) -> (event::Status, Option<Message>) {
-
-        if cursor.is_over(bounds) {
-            match event {
-                Event::Mouse(mouse::Event::WheelScrolled { delta }) => {
-                    match delta {
-                        mouse::ScrollDelta::Lines { y, .. } => {
-                            let y_scaling = 1.0 + y * 0.1;
-                    
-                            return (event::Status::Captured, Some(Message::YScaling(y_scaling)));
+        if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
+            *interaction = Interaction::None;
+            return (event::Status::Ignored, None);
+        }
+    
+        let Some(cursor_position) = cursor.position_in(bounds) else {
+            return (event::Status::Ignored, None);
+        };
+    
+        if !cursor.is_over(bounds) {
+            return (event::Status::Ignored, None);
+        }
+    
+        match event {
+            Event::Mouse(mouse_event) => match mouse_event {
+                mouse::Event::ButtonPressed(button) => {
+                    if let mouse::Button::Left = button {
+                        *interaction = Interaction::Zoomin {
+                            last_position: cursor_position,
+                        };
+                        return (event::Status::Captured, None);
+                    }
+                }
+                mouse::Event::CursorMoved { .. } => {
+                    if let Interaction::Zoomin { ref mut last_position } = *interaction {
+                        let difference_y = last_position.y - cursor_position.y;
+    
+                        if difference_y.abs() > 1.0 {
+                            let y_scaling = 1.0 + (difference_y / bounds.height);
+                            *last_position = cursor_position;
+                            return (
+                                event::Status::Captured,
+                                Some(Message::YScaling((y_scaling * 1000.0).round() / 1000.0, false)),
+                            );
                         }
-                        _ => {}
+                    }
+                }
+                mouse::Event::WheelScrolled { delta } => {
+                    if let mouse::ScrollDelta::Lines { y, .. } = delta {
+                        let y_scaling = 1.0 + y * 0.1;
+                        return (
+                            event::Status::Captured,
+                            Some(Message::YScaling((y_scaling * 1000.0).round() / 1000.0, true)),
+                        );
                     }
                 }
                 _ => {}
-            }
+            },
+            _ => {}
         }
-
+    
         (event::Status::Ignored, None)
     }
     
@@ -523,9 +555,8 @@ impl canvas::Program<Message> for AxisLabelYCanvas<'_> {
         cursor: mouse::Cursor,
     ) -> mouse::Interaction {
         match interaction {
-            Interaction::Drawing => mouse::Interaction::Crosshair,
-            Interaction::Erasing => mouse::Interaction::Crosshair,
-            Interaction::Panning { .. } => mouse::Interaction::ResizingVertically,
+            Interaction::Zoomin { .. } => mouse::Interaction::ResizingVertically,
+            Interaction::Panning { .. } => mouse::Interaction::None,
             Interaction::None if cursor.is_over(bounds) => {
                 mouse::Interaction::ResizingVertically
             }

+ 2 - 8
src/charts/candlestick.rs

@@ -268,10 +268,6 @@ impl canvas::Program<Message> for CandlestickChart {
             Event::Mouse(mouse_event) => match mouse_event {
                 mouse::Event::ButtonPressed(button) => {
                     let message = match button {
-                        mouse::Button::Right => {
-                            *interaction = Interaction::Drawing;
-                            None
-                        }
                         mouse::Button::Left => {
                             *interaction = Interaction::Panning {
                                 translation: chart_state.translation,
@@ -286,8 +282,6 @@ impl canvas::Program<Message> for CandlestickChart {
                 }
                 mouse::Event::CursorMoved { .. } => {
                     let message = match *interaction {
-                        Interaction::Drawing => None,
-                        Interaction::Erasing => None,
                         Interaction::Panning { translation, start } => {
                             Some(Message::Translated(
                                 translation
@@ -301,6 +295,7 @@ impl canvas::Program<Message> for CandlestickChart {
                             } else {
                                 None
                             },
+                        _ => None,
                     };
 
                     let event_status = match interaction {
@@ -547,9 +542,8 @@ impl canvas::Program<Message> for CandlestickChart {
         cursor: mouse::Cursor,
     ) -> mouse::Interaction {
         match interaction {
-            Interaction::Drawing => mouse::Interaction::Crosshair,
-            Interaction::Erasing => mouse::Interaction::Crosshair,
             Interaction::Panning { .. } => mouse::Interaction::Grabbing,
+            Interaction::Zoomin { .. } => mouse::Interaction::ZoomIn,
             Interaction::None if cursor.is_over(bounds) => {
                 if self.chart.crosshair {
                     mouse::Interaction::Crosshair

+ 2 - 8
src/charts/footprint.rs

@@ -366,10 +366,6 @@ impl canvas::Program<Message> for FootprintChart {
             Event::Mouse(mouse_event) => match mouse_event {
                 mouse::Event::ButtonPressed(button) => {
                     let message = match button {
-                        mouse::Button::Right => {
-                            *interaction = Interaction::Drawing;
-                            None
-                        }
                         mouse::Button::Left => {
                             *interaction = Interaction::Panning {
                                 translation: chart_state.translation,
@@ -384,8 +380,6 @@ impl canvas::Program<Message> for FootprintChart {
                 }
                 mouse::Event::CursorMoved { .. } => {
                     let message = match *interaction {
-                        Interaction::Drawing => None,
-                        Interaction::Erasing => None,
                         Interaction::Panning { translation, start } => {
                             Some(Message::Translated(
                                 translation
@@ -399,6 +393,7 @@ impl canvas::Program<Message> for FootprintChart {
                             } else {
                                 None
                             },
+                        _ => None,
                     };
 
                     let event_status = match interaction {
@@ -680,9 +675,8 @@ impl canvas::Program<Message> for FootprintChart {
         cursor: mouse::Cursor,
     ) -> mouse::Interaction {
         match interaction {
-            Interaction::Drawing => mouse::Interaction::Crosshair,
-            Interaction::Erasing => mouse::Interaction::Crosshair,
             Interaction::Panning { .. } => mouse::Interaction::Grabbing,
+            Interaction::Zoomin { .. } => mouse::Interaction::ZoomIn,
             Interaction::None if cursor.is_over(bounds) => {
                 if self.chart.crosshair {
                     mouse::Interaction::Crosshair

+ 12 - 12
src/charts/heatmap.rs

@@ -308,18 +308,24 @@ impl HeatmapChart {
                     chart.x_crosshair_cache.clear();
                 }
             },
-            Message::YScaling(delta) => {
+            Message::YScaling(delta, is_wheel_scroll) => {
                 if self.chart.autoscale {
                     self.chart.autoscale = false;
                 }
 
+                let scaling_factor = if *is_wheel_scroll {
+                    10.0
+                } else {
+                    2.0
+                };
+
                 if *delta < 1.0 {
                     if self.y_scaling < 200 {
-                        self.y_scaling = (self.y_scaling + (delta * 10.0) as i32).min(200);
+                        self.y_scaling = (self.y_scaling + (delta * scaling_factor) as i32).min(200);
                     }
                 } else {
-                    if self.y_scaling > 10 {
-                        self.y_scaling = (self.y_scaling - (delta * 10.0) as i32).max(10);
+                    if self.y_scaling > 20 {
+                        self.y_scaling = (self.y_scaling - (delta * scaling_factor) as i32).max(20);
                     }
                 }
             },
@@ -437,10 +443,6 @@ impl canvas::Program<Message> for HeatmapChart {
             Event::Mouse(mouse_event) => match mouse_event {
                 mouse::Event::ButtonPressed(button) => {
                     let message = match button {
-                        mouse::Button::Right => {
-                            *interaction = Interaction::Drawing;
-                            None
-                        }
                         mouse::Button::Left => {
                             *interaction = Interaction::Panning {
                                 translation: chart_state.translation,
@@ -455,8 +457,6 @@ impl canvas::Program<Message> for HeatmapChart {
                 }
                 mouse::Event::CursorMoved { .. } => {
                     let message = match *interaction {
-                        Interaction::Drawing => None,
-                        Interaction::Erasing => None,
                         Interaction::Panning { translation, start } => {
                             Some(
                                 Message::Translated(
@@ -470,6 +470,7 @@ impl canvas::Program<Message> for HeatmapChart {
                             } else {
                                 None
                             },
+                        _ => None,
                     };
 
                     let event_status = match interaction {
@@ -784,9 +785,8 @@ impl canvas::Program<Message> for HeatmapChart {
         cursor: mouse::Cursor,
     ) -> mouse::Interaction {
         match interaction {
-            Interaction::Drawing => mouse::Interaction::Crosshair,
-            Interaction::Erasing => mouse::Interaction::Crosshair,
             Interaction::Panning { .. } => mouse::Interaction::Grabbing,
+            Interaction::Zoomin { .. } => mouse::Interaction::ZoomIn,
             Interaction::None if cursor.is_over(bounds) => {
                 if self.chart.crosshair {
                     mouse::Interaction::Crosshair