zoom on time line

This commit is contained in:
2025-07-19 13:34:15 +02:00
parent ad6d09b8a5
commit d841c9fdb4
2 changed files with 81 additions and 19 deletions

View File

@@ -17,12 +17,12 @@ mod gui;
use gui::{load_file_view, music_view}; use gui::{load_file_view, music_view};
use iced::Font; use iced::Font;
use iced::Theme;
use iced::{ use iced::{
Color, Event, Task, Color, Event, Task,
event::{self, Status}, event::{self, Status},
keyboard::{Key, key::Named}, keyboard::{Key, key::Named},
}; };
use iced::Theme;
use std::time::Instant; use std::time::Instant;
@@ -190,7 +190,6 @@ impl MyApp {
} }
Message::ChangeDelta(f) => { Message::ChangeDelta(f) => {
self.already_save = false;
self.current_delta = f; self.current_delta = f;
self.music.fix_teta(self.current_delta); self.music.fix_teta(self.current_delta);
// update the red dot on canvas // update the red dot on canvas

View File

@@ -5,11 +5,14 @@ use serde::{Deserialize, Serialize};
use kira::{AudioManager, sound::static_sound::StaticSoundData}; use kira::{AudioManager, sound::static_sound::StaticSoundData};
use iced::Vector;
use iced::event::Status; use iced::event::Status;
use iced::mouse::Cursor; use iced::mouse::Cursor;
use iced::widget::canvas::{Event, Geometry}; 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;
use iced::widget::canvas::Stroke; 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<Message> for Music { impl canvas::Program<Message> for Music {
// No internal state // No internal state
type State = bool; type State = StateMusic;
fn draw( fn draw(
&self, &self,
_state: &Self::State, state: &Self::State,
renderer: &Renderer, renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
@@ -190,22 +201,26 @@ impl canvas::Program<Message> for Music {
let mut geo_small_frame: Vec<Geometry> = vec![]; let mut geo_small_frame: Vec<Geometry> = vec![];
let mut geo_cursor: Vec<Geometry> = vec![]; let mut geo_cursor: Vec<Geometry> = vec![];
let frame = canvas::Frame::new(renderer, bounds.size()); let frame = canvas::Frame::new(renderer, bounds.size());
let global_width = bounds.width * state.zoom;
let mut toggle_color = true; let mut toggle_color = true;
let padding = 8.; let padding = 8.;
let w = bounds.width - (padding * 2.); let w = global_width - (padding * 2.);
for (delta, polyframe) in &self.poly_frame { 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( let mut back_frame = canvas::Frame::new(
renderer, renderer,
Size { Size {
width: bounds.width, width: global_width.clamp(0.0, bounds.width),
height: bounds.height, height: bounds.height,
}, },
); );
back_frame.fill_rectangle( back_frame.fill_rectangle(
iced::Point { x: x, y: 0.0 }, iced::Point { x: x, y: 0.0 },
frame.size(), Size {
width: global_width,
height: bounds.height,
},
if toggle_color { if toggle_color {
Color::from_rgb8(27, 60, 83) Color::from_rgb8(27, 60, 83)
} else { } else {
@@ -217,7 +232,7 @@ impl canvas::Program<Message> for Music {
let mut small_frame = canvas::Frame::new( let mut small_frame = canvas::Frame::new(
renderer, renderer,
Size { Size {
width: bounds.width, width: global_width.clamp(0.0, bounds.width),
height: bounds.height, height: bounds.height,
}, },
); );
@@ -236,7 +251,7 @@ impl canvas::Program<Message> for Music {
geo_small_frame.push(small_frame.into_geometry()); 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()); let mut frame_cursor = canvas::Frame::new(renderer, bounds.size());
frame_cursor.stroke_rectangle( frame_cursor.stroke_rectangle(
iced::Point::new(x, 0.), iced::Point::new(x, 0.),
@@ -251,8 +266,14 @@ impl canvas::Program<Message> for Music {
}, },
); );
frame_cursor.stroke_rectangle( frame_cursor.stroke_rectangle(
iced::Point { x: 0.0, y: 0.0 }, iced::Point {
frame.size(), x: -state.offset,
y: 0.0,
},
iced::Size {
width: global_width,
height: bounds.height,
},
Stroke { Stroke {
width: 16.0, width: 16.0,
style: Style::Solid(Color::from_rgb8(207, 74, 28)), style: Style::Solid(Color::from_rgb8(207, 74, 28)),
@@ -276,12 +297,21 @@ impl canvas::Program<Message> for Music {
cursor: Cursor, cursor: Cursor,
) -> (Status, Option<Message>) { ) -> (Status, Option<Message>) {
//eprintln!("event = {:?}", event); //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 { if let Event::Mouse(mouse_event) = event {
match mouse_event { match mouse_event {
mouse::Event::ButtonPressed(mouse::Button::Left) => { mouse::Event::ButtonPressed(mouse::Button::Left) => {
*state = true; state.mouse_left = true;
if let Some(position) = cursor.position_in(bounds) { 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); let delta = (pos_x * self.length).clamp(0., self.length);
return ( return (
Status::Captured, Status::Captured,
@@ -289,19 +319,52 @@ impl canvas::Program<Message> for Music {
); );
} }
} }
mouse::Event::ButtonReleased(mouse::Button::Left) => *state = false, mouse::Event::ButtonReleased(mouse::Button::Left) => state.mouse_left = false,
mouse::Event::CursorMoved { position: _ } => { mouse::Event::CursorMoved { position: _ } => {
if let Some(position) = cursor.position_in(bounds) 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); let delta = (pos_x * self.length).clamp(0., self.length);
return (Status::Captured, Some(Message::ClickedOnTimeLine(delta))); 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) (Status::Ignored, None)
} }