From f098d651e34c6ad7d8fe0557eb57d2027d7c88aa Mon Sep 17 00:00:00 2001 From: Dukantic Date: Sat, 19 Jul 2025 13:34:15 +0200 Subject: [PATCH] zoom on time line --- src/main.rs | 3 +- src/music.rs | 97 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index 80ee06e..0725f61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,12 +17,12 @@ mod gui; use gui::{load_file_view, music_view}; use iced::Font; +use iced::Theme; use iced::{ Color, Event, Task, event::{self, Status}, keyboard::{Key, key::Named}, }; -use iced::Theme; use std::time::Instant; @@ -190,7 +190,6 @@ impl MyApp { } Message::ChangeDelta(f) => { - self.already_save = false; self.current_delta = f; self.music.fix_teta(self.current_delta); // update the red dot on canvas diff --git a/src/music.rs b/src/music.rs index cba07ba..ae2d706 100644 --- a/src/music.rs +++ b/src/music.rs @@ -5,11 +5,14 @@ use serde::{Deserialize, Serialize}; use kira::{AudioManager, sound::static_sound::StaticSoundData}; -use iced::Vector; use iced::event::Status; use iced::mouse::Cursor; use iced::widget::canvas::{Event, Geometry}; -use iced::{Size, mouse}; +use iced::{ + Size, + mouse::{self, ScrollDelta}, +}; +use iced::{Vector, keyboard}; use iced::widget::canvas; use iced::widget::canvas::Stroke; @@ -175,13 +178,21 @@ impl Music { } } } +#[derive(Default, Debug)] +pub struct StateMusic { + mouse_left: bool, + ctrl: bool, + zoom: f32, + offset: f32, +} + impl canvas::Program for Music { // No internal state - type State = bool; + type State = StateMusic; fn draw( &self, - _state: &Self::State, + state: &Self::State, renderer: &Renderer, _theme: &Theme, bounds: Rectangle, @@ -190,22 +201,26 @@ impl canvas::Program for Music { let mut geo_small_frame: Vec = vec![]; let mut geo_cursor: Vec = vec![]; let frame = canvas::Frame::new(renderer, bounds.size()); + let global_width = bounds.width * state.zoom; let mut toggle_color = true; let padding = 8.; - let w = bounds.width - (padding * 2.); + let w = global_width - (padding * 2.); for (delta, polyframe) in &self.poly_frame { - let x = delta / self.length * w + 8.; + let x = delta / self.length * w + 8. - state.offset; let mut back_frame = canvas::Frame::new( renderer, Size { - width: bounds.width, + width: global_width.clamp(0.0, bounds.width), height: bounds.height, }, ); back_frame.fill_rectangle( iced::Point { x: x, y: 0.0 }, - frame.size(), + Size { + width: global_width, + height: bounds.height, + }, if toggle_color { Color::from_rgb8(27, 60, 83) } else { @@ -217,7 +232,7 @@ impl canvas::Program for Music { let mut small_frame = canvas::Frame::new( renderer, Size { - width: bounds.width, + width: global_width.clamp(0.0, bounds.width), height: bounds.height, }, ); @@ -236,7 +251,7 @@ impl canvas::Program for Music { geo_small_frame.push(small_frame.into_geometry()); } - let x = self.current_delta / self.length * w + 8.; + let x = self.current_delta / self.length * w + 8. - state.offset; let mut frame_cursor = canvas::Frame::new(renderer, bounds.size()); frame_cursor.stroke_rectangle( iced::Point::new(x, 0.), @@ -251,8 +266,14 @@ impl canvas::Program for Music { }, ); frame_cursor.stroke_rectangle( - iced::Point { x: 0.0, y: 0.0 }, - frame.size(), + iced::Point { + x: -state.offset, + y: 0.0, + }, + iced::Size { + width: global_width, + height: bounds.height, + }, Stroke { width: 16.0, style: Style::Solid(Color::from_rgb8(207, 74, 28)), @@ -276,12 +297,21 @@ impl canvas::Program for Music { cursor: Cursor, ) -> (Status, Option) { //eprintln!("event = {:?}", event); + if let Event::Keyboard(keyboard_event) = &event { + match keyboard_event { + keyboard::Event::ModifiersChanged(m) => { + state.ctrl = m.control(); + } + _ => (), + } + } if let Event::Mouse(mouse_event) = event { match mouse_event { mouse::Event::ButtonPressed(mouse::Button::Left) => { - *state = true; + state.mouse_left = true; if let Some(position) = cursor.position_in(bounds) { - let pos_x = (position.x - 8.0) / (bounds.width - 16.); + let pos_x = + (position.x + state.offset - 8.0) / (bounds.width * state.zoom - 16.); let delta = (pos_x * self.length).clamp(0., self.length); return ( Status::Captured, @@ -289,19 +319,52 @@ impl canvas::Program for Music { ); } } - mouse::Event::ButtonReleased(mouse::Button::Left) => *state = false, + mouse::Event::ButtonReleased(mouse::Button::Left) => state.mouse_left = false, mouse::Event::CursorMoved { position: _ } => { if let Some(position) = cursor.position_in(bounds) - && *state + && state.mouse_left { - let pos_x = (position.x - 8.0) / (bounds.width - 16.); + let pos_x = + (position.x + state.offset - 8.0) / (bounds.width * state.zoom - 16.); let delta = (pos_x * self.length).clamp(0., self.length); return (Status::Captured, Some(Message::ClickedOnTimeLine(delta))); } } + mouse::Event::WheelScrolled { delta: d } => { + if state.ctrl { + match d { + ScrollDelta::Lines { x: _, y } => { + let before = bounds.width * state.zoom; + state.zoom += y / 10.; + state.offset = state.offset * (bounds.width * state.zoom) / before; + } + ScrollDelta::Pixels { x: _, y } => { + let before = bounds.width * state.zoom; + state.zoom += y / 10.; + state.offset = state.offset * (bounds.width * state.zoom) / before; + } + } + } else { + match d { + ScrollDelta::Lines { x: _, y } => { + state.offset -= y * 32. * state.zoom; + } + ScrollDelta::Pixels { x: _, y } => { + state.offset -= y * 32. * state.zoom; + } + } + } + } _ => {} } } + state.zoom = state.zoom.clamp(1.0, 10.); + if state.offset + bounds.width >= bounds.width * state.zoom { + state.offset = bounds.width * state.zoom - bounds.width + } + if state.offset <= 0. { + state.offset = 0. + } (Status::Ignored, None) }