historic , undo and redo

This commit is contained in:
2025-07-19 18:56:06 +02:00
parent be71a22248
commit bb15aeabb3
5 changed files with 206 additions and 23 deletions

View File

@@ -3,11 +3,11 @@ use crate::message::Message;
use iced::alignment::{Horizontal, Vertical}; use iced::alignment::{Horizontal, Vertical};
use iced::widget::{TextInput, column, text}; use iced::widget::{TextInput, column, text};
use iced::{Element, Theme};
use iced::{ use iced::{
Length, Padding, Length, Padding,
widget::{Column, Container, button, canvas, container, pick_list, row, scrollable, slider}, widget::{Column, Container, button, canvas, container, pick_list, row, scrollable, slider},
}; };
use iced::{Element, Theme};
use iced_aw::menu::{self, Item}; use iced_aw::menu::{self, Item};
use iced_aw::menu_bar; use iced_aw::menu_bar;
use iced_aw::widget::color_picker; use iced_aw::widget::color_picker;
@@ -115,7 +115,9 @@ pub fn music_view(app: &MyApp) -> iced::Element<Message> {
menu_tpl_1(vec![ menu_tpl_1(vec![
Item::new("󰐊 SPACE"), Item::new("󰐊 SPACE"),
Item::new("󰆓 CTRL+S"), Item::new("󰆓 CTRL+S"),
Item::new("Start ") Item::new("󰈼 ARROW UP"),
Item::new("󰕌 CTRL+Z"),
Item::new("󰑎 CTRL+Y"),
]) ])
)) ))
.draw_path(menu::DrawPath::Backdrop) .draw_path(menu::DrawPath::Backdrop)

53
src/history.rs Normal file
View File

@@ -0,0 +1,53 @@
use crate::{message::Message, music::Music};
#[derive(Clone, Debug)]
struct HistoricItem {
old: Message,
new: Message,
delta: f32,
}
#[derive(Debug)]
pub struct Historic {
past: Vec<HistoricItem>,
future: Vec<HistoricItem>,
pub ignore_add: bool,
}
impl Historic {
pub fn new() -> Self {
Historic {
past: vec![],
future: vec![],
ignore_add: false,
}
}
pub fn add(&mut self, old: Message, new: Message, delta: f32) {
if !self.ignore_add {
self.future = vec![];
self.past.push(HistoricItem {
old: old,
new: new,
delta: delta,
});
}
}
pub fn undo(&mut self) -> Option<(Message, f32)> {
if let Some(item) = self.past.pop() {
self.future.push(item.clone());
Some((item.old.clone(), item.delta))
} else {
None
}
}
pub fn redo(&mut self) -> Option<(Message, f32)> {
if let Some(item) = self.future.pop() {
self.past.push(item.clone());
Some((item.new.clone(), item.delta))
} else {
None
}
}
}

View File

@@ -5,6 +5,9 @@ use polygon_draw::Polygon;
mod music; mod music;
use music::Music; use music::Music;
mod history;
use history::Historic;
mod message; mod message;
use message::Message; use message::Message;
@@ -76,6 +79,7 @@ struct MyApp {
mode_file_load: bool, mode_file_load: bool,
file_name_to_creat: String, file_name_to_creat: String,
show_warning_message_creat: bool, show_warning_message_creat: bool,
historic: Historic,
} }
impl MyApp { impl MyApp {
@@ -83,8 +87,6 @@ impl MyApp {
let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default()) let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())
.expect("Error to load AudioManager"); .expect("Error to load AudioManager");
//let font_bytes = include_bytes!("../fonts/");
( (
Self { Self {
time_last_frame: Instant::now(), time_last_frame: Instant::now(),
@@ -101,6 +103,7 @@ impl MyApp {
mode_file_load: true, mode_file_load: true,
file_name_to_creat: "Default File Name".to_string(), file_name_to_creat: "Default File Name".to_string(),
show_warning_message_creat: false, show_warning_message_creat: false,
historic: Historic::new(),
}, },
Task::none(), Task::none(),
) )
@@ -108,8 +111,13 @@ impl MyApp {
fn update(&mut self, message: Message) { fn update(&mut self, message: Message) {
match message { match message {
Message::AddPolygon(s) => { Message::AddPolygon(s) => {
self.music.add_polygon(self.current_delta, s); self.music.add_polygon(self.current_delta, s.clone());
self.already_save = false; self.already_save = false;
self.historic.add(
Message::Remove(usize::MAX),
Message::AddPolygon(s),
self.current_delta,
);
} }
Message::Tick => { Message::Tick => {
if self.current_delta >= self.music.length { if self.current_delta >= self.music.length {
@@ -126,19 +134,36 @@ impl MyApp {
} }
} }
Message::Remove(i) => { Message::Remove(i) => {
self.music.remove_polygon(self.current_delta, i); let name = self.music.remove_polygon(self.current_delta, i);
self.already_save = false; self.already_save = false;
self.historic.add(
Message::AddPolygon(name),
Message::Remove(i),
self.current_delta,
);
} }
Message::ChangeTeta(i, teta) => { Message::ChangeTeta(i, teta) => {
self.music.set_teta(self.current_delta, i, teta); let old_teta = self.music.set_teta(self.current_delta, i, teta);
self.already_save = false; self.already_save = false;
self.historic.add(
Message::ChangeTeta(i, old_teta),
Message::ChangeTeta(i, teta),
self.current_delta,
);
} }
Message::ChangeSound(i, s) => { Message::ChangeSound(i, s) => {
let sound = StaticSoundData::from_file(format!("./assets/{s}")) let sound = StaticSoundData::from_file(format!("./assets/{s}"))
.expect("Fail to load audio"); .expect("Fail to load audio");
self.music.set_sound(self.current_delta, i, sound, s); let old_sound = self
.music
.set_sound(self.current_delta, i, sound, s.clone());
self.already_save = false; self.already_save = false;
self.historic.add(
Message::ChangeSound(i, old_sound),
Message::ChangeSound(i, s),
self.current_delta,
);
} }
Message::Save => { Message::Save => {
let json = serde_json::to_string_pretty(&self.music).unwrap(); let json = serde_json::to_string_pretty(&self.music).unwrap();
@@ -176,6 +201,11 @@ impl MyApp {
if is_delta_format_valid(&s) { if is_delta_format_valid(&s) {
let sec = str_to_sec(&s); let sec = str_to_sec(&s);
if sec > 0. { if sec > 0. {
self.historic.add(
Message::LengthChange(delta_to_string(self.music.length)),
Message::LengthChange(s.clone()),
self.current_delta,
);
self.music.length = sec; self.music.length = sec;
self.already_save = false; self.already_save = false;
} }
@@ -202,13 +232,24 @@ impl MyApp {
} }
self.str_time = s; self.str_time = s;
} }
Message::ReAddPoint => {
self.already_save = false;
self.music.add_point_old();
}
Message::AddPoint => { Message::AddPoint => {
self.already_save = false; self.already_save = false;
self.music.add_point(self.current_delta); self.music.add_point(self.current_delta);
self.historic
.add(Message::RemovePoint, Message::AddPoint, self.current_delta);
} }
Message::RemovePoint => { Message::RemovePoint => {
self.already_save = false; self.already_save = false;
self.music.remove_point(self.current_delta); self.music.remove_point(self.current_delta);
self.historic.add(
Message::ReAddPoint,
Message::RemovePoint,
self.current_delta,
);
} }
Message::ClickedOnTimeLine(f) => { Message::ClickedOnTimeLine(f) => {
self.update(Message::ChangeDelta(f)); self.update(Message::ChangeDelta(f));
@@ -218,12 +259,22 @@ impl MyApp {
self.music.slide_to_left(self.current_delta); self.music.slide_to_left(self.current_delta);
self.music.fix_teta(self.current_delta); self.music.fix_teta(self.current_delta);
self.update_canvas_if_paused(); self.update_canvas_if_paused();
self.historic.add(
Message::SlidePointRight,
Message::SlidePointLeft,
self.current_delta,
);
} }
Message::SlidePointRight => { Message::SlidePointRight => {
self.already_save = false; self.already_save = false;
self.music.slide_to_right(self.current_delta); self.music.slide_to_right(self.current_delta);
self.music.fix_teta(self.current_delta); self.music.fix_teta(self.current_delta);
self.update_canvas_if_paused(); self.update_canvas_if_paused();
self.historic.add(
Message::SlidePointLeft,
Message::SlidePointRight,
self.current_delta,
);
} }
Message::ChangeDegree(i, s) => { Message::ChangeDegree(i, s) => {
let mut mut_s = s; let mut mut_s = s;
@@ -247,6 +298,14 @@ impl MyApp {
Ok(val) => { Ok(val) => {
let val = (val * 10.).floor() / 10.; let val = (val * 10.).floor() / 10.;
if val >= 1. && val < 1000. { if val >= 1. && val < 1000. {
self.historic.add(
Message::ChangeNbPerSec(format!(
"{:.1} sec/rev",
self.music.nb_sec_for_rev
)),
Message::ChangeNbPerSec(s.clone()),
self.current_delta,
);
self.music.nb_sec_for_rev = val; self.music.nb_sec_for_rev = val;
self.already_save = false; self.already_save = false;
} }
@@ -267,14 +326,19 @@ impl MyApp {
self.music.set_color_picker(self.current_delta, i, true); self.music.set_color_picker(self.current_delta, i, true);
} }
Message::ChooseColor(i, color) => { Message::ChooseColor(i, color) => {
self.music.set_color(self.current_delta, i, color); let old_color = self.music.set_color(self.current_delta, i, color);
self.music.set_color_picker(self.current_delta, i, false); self.music.set_color_picker(self.current_delta, i, false);
self.can_unpaused = true; self.can_unpaused = true;
self.already_save = false; self.already_save = false;
self.historic.add(
Message::ChooseColor(i, old_color),
Message::ChooseColor(i, color),
self.current_delta,
);
} }
Message::None => {}
Message::GoToLoadView => { Message::GoToLoadView => {
if self.already_save { if self.already_save {
self.historic = Historic::new();
self.paused = true; self.paused = true;
self.file_name_to_creat = "Default File Name".to_string(); self.file_name_to_creat = "Default File Name".to_string();
self.show_warning_message_creat = false; self.show_warning_message_creat = false;
@@ -284,6 +348,23 @@ impl MyApp {
Message::ForceToQuit => { Message::ForceToQuit => {
self.already_save = true; self.already_save = true;
} }
Message::Undo => {
if let Some(item) = self.historic.undo() {
self.historic.ignore_add = true;
self.update(Message::ChangeDelta(item.1));
self.update(item.0);
self.historic.ignore_add = false;
}
}
Message::Redo => {
if let Some(item) = self.historic.redo() {
self.historic.ignore_add = true;
self.update(Message::ChangeDelta(item.1));
self.update(item.0);
self.historic.ignore_add = false;
}
}
Message::None => {}
} }
} }
@@ -297,6 +378,22 @@ impl MyApp {
fn subscription(&self) -> iced::Subscription<Message> { fn subscription(&self) -> iced::Subscription<Message> {
let subscr_key = event::listen_with(|event, status, _| match (event, status) { let subscr_key = event::listen_with(|event, status, _| match (event, status) {
(
Event::Keyboard(iced::keyboard::Event::KeyPressed {
key: Key::Character(c),
modifiers: Modifiers::CTRL,
..
}),
Status::Ignored,
) if c.as_ref() == "z" => Some(Message::Undo),
(
Event::Keyboard(iced::keyboard::Event::KeyPressed {
key: Key::Character(c),
modifiers: Modifiers::CTRL,
..
}),
Status::Ignored,
) if c.as_ref() == "y" => Some(Message::Redo),
( (
Event::Keyboard(iced::keyboard::Event::KeyPressed { Event::Keyboard(iced::keyboard::Event::KeyPressed {
key: Key::Character(c), key: Key::Character(c),

View File

@@ -7,27 +7,32 @@ pub enum Message {
SetFileNameCreat(String), SetFileNameCreat(String),
GoToLoadView, GoToLoadView,
ForceToQuit, ForceToQuit,
ChangeNbPerSec(String),
Tick, Tick,
AddPolygon(String),
ChangeTeta(usize, f32),
Remove(usize),
ChangeSound(usize, String),
Save, Save,
Load, Load,
FileNameChanged(String), FileNameChanged(String),
TogglePaused, TogglePaused,
LengthChange(String),
ChangeDelta(f32), ChangeDelta(f32),
AddPoint,
RemovePoint,
ClickedOnTimeLine(f32), ClickedOnTimeLine(f32),
ChangeDeltaString(String), ChangeDeltaString(String),
ChangeNbPerSec(String),
AddPolygon(String),
ChangeTeta(usize, f32),
Remove(usize),
ChangeSound(usize, String),
LengthChange(String),
AddPoint,
RemovePoint,
SlidePointLeft, SlidePointLeft,
SlidePointRight, SlidePointRight,
ChangeDegree(usize, String), ChangeDegree(usize, String),
ReAddPoint,
ChooseColor(usize, Color), ChooseColor(usize, Color),
CancelColor(usize), CancelColor(usize),
SubmitColor(usize), SubmitColor(usize),
Undo,
Redo,
} }

View File

@@ -31,6 +31,8 @@ pub struct Music {
teta: f32, teta: f32,
#[serde(skip)] #[serde(skip)]
pub current_delta: f32, pub current_delta: f32,
#[serde(skip)]
point_removed: Vec<(f32, PolygonFrame)>,
} }
impl Music { impl Music {
@@ -77,6 +79,7 @@ impl Music {
length: 60.0, length: 60.0,
teta: 0., teta: 0.,
current_delta: 0., current_delta: 0.,
point_removed: vec![],
} }
} }
@@ -116,19 +119,25 @@ impl Music {
index: usize, index: usize,
sound: StaticSoundData, sound: StaticSoundData,
sound_name: String, sound_name: String,
) { ) -> String {
let current_frame = self.find_poly_frame(delta); let current_frame = self.find_poly_frame(delta);
current_frame.polygons[index].sound = sound; current_frame.polygons[index].sound = sound;
let out = current_frame.polygons[index].sound_name.clone();
current_frame.polygons[index].sound_name = sound_name; current_frame.polygons[index].sound_name = sound_name;
out
} }
pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) { pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) -> f32 {
let out = self.find_poly_frame(delta).polygons[index].global_teta;
self.find_poly_frame(delta).polygons[index].global_teta = teta; self.find_poly_frame(delta).polygons[index].global_teta = teta;
out
} }
pub fn set_color(&mut self, delta: f32, index: usize, color: Color) { pub fn set_color(&mut self, delta: f32, index: usize, color: Color) -> Color {
let current_frame = self.find_poly_frame(delta); let current_frame = self.find_poly_frame(delta);
let out = current_frame.polygons[index].color.clone();
current_frame.polygons[index].color = color; current_frame.polygons[index].color = color;
out
} }
pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) { pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) {
@@ -146,9 +155,19 @@ impl Music {
self.poly_frame self.poly_frame
.insert(pos, (delta, self.current_frame(delta).clone())); .insert(pos, (delta, self.current_frame(delta).clone()));
} }
pub fn add_point_old(&mut self) {
if let Some(pair) = self.point_removed.pop() {
let pos = self
.poly_frame
.binary_search_by(|(d, _)| d.partial_cmp(&pair.0).unwrap())
.unwrap_or_else(|e| e);
self.poly_frame.insert(pos, pair);
}
}
pub fn remove_point(&mut self, delta: f32) { pub fn remove_point(&mut self, delta: f32) {
let i = self.find_index_frame(delta); let i = self.find_index_frame(delta);
if i != 0 { if i != 0 {
self.point_removed.push(self.poly_frame[i].clone());
self.poly_frame.remove(i); self.poly_frame.remove(i);
} }
} }
@@ -159,8 +178,15 @@ impl Music {
current_frame.polygons.push(poly); current_frame.polygons.push(poly);
} }
pub fn remove_polygon(&mut self, delta: f32, i: usize) { pub fn remove_polygon(&mut self, delta: f32, i: usize) -> String {
self.find_poly_frame(delta).polygons.remove(i); let pf = self.find_poly_frame(delta);
let mut i = i;
if i == usize::MAX {
i = pf.polygons.len() - 1
}
let out = pf.polygons[i].name.clone();
pf.polygons.remove(i);
out
} }
pub fn slide_to_left(&mut self, delta: f32) { pub fn slide_to_left(&mut self, delta: f32) {