From 2d8674255a739c99b1d871e7af20480199fb95c8 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Thu, 18 Jan 2024 23:20:48 +0100 Subject: add scrolling to iced demo --- src/main.rs | 151 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 8876f16..90fc7ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ pub mod math; +use iced::event; use iced::executor; use iced::mouse; +use iced::mouse::Interaction; use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke}; use iced::widget::column; use iced::widget::text_input; @@ -27,9 +29,6 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Graph { graph: Cache, - center_x: f32, - center_y: f32, - scale: f32, func: ExpressionFunction, ctx: Context, input_state: String, @@ -38,16 +37,7 @@ struct Graph { #[derive(Debug, Clone)] enum Message { InputChanged(String), - FunctionRun, -} - -impl Graph { - fn map_coords(&self, x: f32, y: f32) -> Point { - Point::new( - (self.center_x + x) * self.scale, - (self.center_y - y) * self.scale, - ) - } + UpdateScreen, } impl Application for Graph { @@ -62,7 +52,6 @@ impl Application for Graph { input_state: "f(x) = x^2".to_string(), func: ExpressionFunction::from_string("f(x) = x^2".to_string()), ctx: Context::commonsense(), - scale: 50.0, ..Default::default() }, Command::none(), @@ -78,7 +67,7 @@ impl Application for Graph { Message::InputChanged(s) => { self.input_state = s; } - Message::FunctionRun => { + Message::UpdateScreen => { self.func = ExpressionFunction::from_string(self.input_state.clone()); self.graph.clear(); } @@ -89,7 +78,7 @@ impl Application for Graph { fn view(&self) -> Element { let input = text_input("x", &self.input_state) .on_input(Message::InputChanged) - .on_submit(Message::FunctionRun) + .on_submit(Message::UpdateScreen) .padding(15) .size(30); let canvas = canvas(self as &Self) @@ -105,12 +94,40 @@ impl Application for Graph { } } -impl canvas::Program for Graph { - type State = (); +#[derive(Copy, Clone)] +struct MouseState { + interaction: Interaction, + center: Point, + last_position: Option, + scale: f32, +} + +impl MouseState { + fn map_coords(&self, x: f32, y: f32) -> Point { + Point::new( + (self.center.x + x) * self.scale, + (self.center.y - y) * self.scale, + ) + } +} + +impl Default for MouseState { + fn default() -> Self { + Self { + interaction: Interaction::default(), + center: Point::default(), + last_position: None, + scale: 50.0 + } + } +} + +impl canvas::Program for Graph { + type State = MouseState; fn draw( &self, - _state: &Self::State, + state: &Self::State, renderer: &Renderer, _theme: &Theme, bounds: Rectangle, @@ -149,65 +166,65 @@ impl canvas::Program for Graph { }); frame.with_save(|frame| { - let start_y = ((self.center_y - frame.height() / 2.0) / self.scale) as i64 - * self.scale as i64; + let start_y = ((state.center.y - frame.height() / 2.0) / state.scale) as i64 + * state.scale as i64; let start_x = - ((self.center_x - frame.width() / 2.0) / self.scale) as i64 * self.scale as i64; - let end_y = ((self.center_y + frame.height() / 2.0) / self.scale) as i64 - * self.scale as i64; + ((state.center.x - frame.width() / 2.0) / state.scale) as i64 * state.scale as i64; + let end_y = ((state.center.y + frame.height() / 2.0) / state.scale) as i64 + * state.scale as i64; let end_x = - ((self.center_x + frame.width() / 2.0) / self.scale) as i64 * self.scale as i64; + ((state.center.x + frame.width() / 2.0) / state.scale) as i64 * state.scale as i64; - for y in (start_y..end_y + self.scale as i64).step_by(1) { + for y in (start_y..end_y + state.scale as i64).step_by(1) { let line = Path::line( - self.map_coords(start_x as f32, y as f32), - self.map_coords(end_x as f32, y as f32), + state.map_coords(start_x as f32, y as f32), + state.map_coords(end_x as f32, y as f32), ); frame.stroke(&line, unitline_whole.clone()); } - for x in (start_x..end_x + self.scale as i64).step_by(1) { + for x in (start_x..end_x + state.scale as i64).step_by(1) { let line = Path::line( - self.map_coords(x as f32, start_y as f32), - self.map_coords(x as f32, end_y as f32), + state.map_coords(x as f32, start_y as f32), + state.map_coords(x as f32, end_y as f32), ); frame.stroke(&line, unitline_whole.clone()); } - let start_y_100 = ((self.center_y - frame.height() / 2.0) / self.scale * 10.0) + let start_y_100 = ((state.center.y - frame.height() / 2.0) / state.scale * 10.0) as i64 * 10 - * self.scale as i64; - let start_x_100 = ((self.center_x - frame.width() / 2.0) / self.scale * 10.0) + * state.scale as i64; + let start_x_100 = ((state.center.x - frame.width() / 2.0) / state.scale * 10.0) as i64 * 10 - * self.scale as i64; + * state.scale as i64; for y in (start_y_100..end_y).step_by(10) { let line = Path::line( - self.map_coords(start_x as f32, y as f32), - self.map_coords(end_x as f32, y as f32), + state.map_coords(start_x as f32, y as f32), + state.map_coords(end_x as f32, y as f32), ); frame.stroke(&line, unitline.clone()); } for x in (start_x_100..end_x).step_by(10) { let line = Path::line( - self.map_coords(x as f32, start_y as f32), - self.map_coords(x as f32, end_y as f32), + state.map_coords(x as f32, start_y as f32), + state.map_coords(x as f32, end_y as f32), ); frame.stroke(&line, unitline.clone()); } let line = Path::line( - self.map_coords(0.0, start_y as f32), - self.map_coords(0.0, end_y as f32), + state.map_coords(0.0, start_y as f32), + state.map_coords(0.0, end_y as f32), ); frame.stroke(&line, zeroline.clone()); let line = Path::line( - self.map_coords(start_x as f32, 0.0), - self.map_coords(end_x as f32, 0.0), + state.map_coords(start_x as f32, 0.0), + state.map_coords(end_x as f32, 0.0), ); frame.stroke(&line, zeroline.clone()); @@ -233,7 +250,7 @@ impl canvas::Program for Graph { .unwrap() .real as f32; if y1.is_finite() && !y1.is_nan() && y2.is_finite() && !y2.is_nan() { - let line = Path::line(self.map_coords(x1, y1), self.map_coords(x2, y2)); + let line = Path::line(state.map_coords(x1, y1), state.map_coords(x2, y2)); frame.stroke(&line, graphline.clone()) } } @@ -243,4 +260,50 @@ impl canvas::Program for Graph { vec![graph] } + + fn update( + &self, + state: &mut Self::State, + event: canvas::Event, + _bounds: Rectangle, + cursor: iced::advanced::mouse::Cursor, + ) -> (canvas::event::Status, Option) { + if let canvas::Event::Mouse(mouse::Event::ButtonPressed(_)) = event { + state.interaction = Interaction::Grabbing; + state.last_position = cursor.position(); + return (event::Status::Captured, Some(Message::UpdateScreen)); + } + + if let canvas::Event::Mouse(mouse::Event::CursorMoved { position }) = event { + if state.interaction == Interaction::Grabbing { + if let Some(lp) = state.last_position { + state.center.x += (position.x - lp.x) / state.scale; + state.center.y += (position.y - lp.y) / state.scale; + } + state.last_position = Some(position); + return (event::Status::Captured, Some(Message::UpdateScreen)); + } + } + + if let canvas::Event::Mouse(mouse::Event::ButtonReleased(_)) = event { + state.interaction = Interaction::Pointer; + } + (event::Status::Ignored, None) + } + + fn mouse_interaction( + &self, + state: &Self::State, + _bounds: Rectangle, + _cursor: mouse::Cursor, + ) -> mouse::Interaction { + match state.interaction { + Interaction::Grabbing => { + mouse::Interaction::Grabbing + }, + _ => { + mouse::Interaction::Grab + } + } + } } -- cgit v1.2.3-70-g09d2