Skip to content

Commit e547119

Browse files
committed
[*] refactor: video editor
1 parent 0ece496 commit e547119

File tree

12 files changed

+440
-118
lines changed

12 files changed

+440
-118
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/video-editor/examples/undo_redo_demo.rs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,8 @@ fn main() -> Result<()> {
104104
// 撤销添加轨道
105105
println!("\n操作: 撤销 2 次");
106106
for _i in 0..2 {
107-
if let Some(desc) = history.undo(&mut manager)? {
108-
println!(" ↻ 撤销: {}", desc);
109-
}
107+
let result = history.undo(&mut manager)?;
108+
println!(" ↻ 撤销: {}", result.description);
110109
}
111110

112111
println!("\n当前状态:");
@@ -115,9 +114,8 @@ fn main() -> Result<()> {
115114

116115
// 重做
117116
println!("\n操作: 重做 1 次");
118-
if let Some(desc) = history.redo(&mut manager)? {
119-
println!(" ↻ 重做: {}", desc);
120-
}
117+
let result = history.redo(&mut manager)?;
118+
println!(" ↻ 重做: {}", result.description);
121119

122120
println!("\n当前状态:");
123121
println!(" 轨道数: {}", manager.len());
@@ -164,19 +162,17 @@ fn main() -> Result<()> {
164162

165163
// 撤销和重做
166164
println!("\n操作: 撤销移除");
167-
if let Some(desc) = history.undo(&mut manager)? {
168-
println!(" ↻ 撤销: {}", desc);
169-
}
165+
let result = history.undo(&mut manager)?;
166+
println!(" ↻ 撤销: {}", result.description);
170167

171168
println!("\n当前状态:");
172169
if let Some(Track::Video(track)) = manager.get(0) {
173170
println!(" 轨道 0 片段数: {}", track.track.segments.len());
174171
}
175172

176173
println!("\n操作: 重做");
177-
if let Some(desc) = history.redo(&mut manager)? {
178-
println!(" ↻ 重做: {}", desc);
179-
}
174+
let result = history.redo(&mut manager)?;
175+
println!(" ↻ 重做: {}", result.description);
180176

181177
println!("\n当前状态:");
182178
if let Some(Track::Video(track)) = manager.get(0) {
@@ -236,9 +232,8 @@ fn main() -> Result<()> {
236232

237233
// 撤销
238234
println!("\n操作: 撤销隐藏");
239-
if let Some(desc) = history.undo(&mut manager)? {
240-
println!(" ↻ 撤销: {}", desc);
241-
}
235+
let result = history.undo(&mut manager)?;
236+
println!(" ↻ 撤销: {}", result.description);
242237

243238
println!("\n当前轨道状态:");
244239
if let Some(track) = manager.get(0) {

lib/video-editor/src/commands.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ pub mod subtitle;
88
pub mod track;
99

1010
pub use batch::BatchCommand;
11-
pub use command::Command;
11+
pub use command::{AffectedSegment, AffectedSegments, Command};
1212
pub use export::*;
1313
pub use filter::*;
14-
pub use history::HistoryManager;
14+
pub use history::{ExecuteResult, HistoryManager, UndoRedoResult};
1515
pub use segment::*;
1616
pub use subtitle::*;
1717
pub use track::*;

lib/video-editor/src/commands/batch.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::command::Command;
1+
use super::command::{AffectedSegments, Command};
22
use crate::{Result, tracks::manager::Manager};
33

44
// 批量命令 - 将多个命令组合成一个原子操作
@@ -64,4 +64,20 @@ impl Command for BatchCommand {
6464
fn can_undo(&self) -> bool {
6565
self.commands.iter().all(|c| c.can_undo())
6666
}
67+
68+
fn affected_segments_after_execute(&self) -> AffectedSegments {
69+
let mut result = AffectedSegments::new();
70+
for command in &self.commands {
71+
result.merge(command.affected_segments_after_execute());
72+
}
73+
result
74+
}
75+
76+
fn affected_segments_after_undo(&self) -> AffectedSegments {
77+
let mut result = AffectedSegments::new();
78+
for command in &self.commands {
79+
result.merge(command.affected_segments_after_undo());
80+
}
81+
result
82+
}
6783
}
Lines changed: 129 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,118 @@
11
use crate::{Result, tracks::manager::Manager};
22

3+
#[derive(Clone, Debug, Default, derive_setters::Setters)]
4+
#[setters(prefix = "with_")]
5+
pub struct AffectedSegment {
6+
pub track_index: usize,
7+
pub segment_index: usize,
8+
// (left_thumbnail, right_thumbnail) - which thumbnails need refresh
9+
pub update_thumbnail: (bool, bool),
10+
// Whether audio sample preview needs refresh
11+
pub update_audio_sample: bool,
12+
}
13+
14+
impl AffectedSegment {
15+
pub fn new(track_index: usize, segment_index: usize) -> Self {
16+
Self {
17+
track_index,
18+
segment_index,
19+
update_thumbnail: (false, false),
20+
update_audio_sample: false,
21+
}
22+
}
23+
24+
pub fn with_left_thumbnail(track_index: usize, segment_index: usize) -> Self {
25+
Self {
26+
track_index,
27+
segment_index,
28+
update_thumbnail: (true, false),
29+
update_audio_sample: true,
30+
}
31+
}
32+
33+
pub fn with_right_thumbnail(track_index: usize, segment_index: usize) -> Self {
34+
Self {
35+
track_index,
36+
segment_index,
37+
update_thumbnail: (false, true),
38+
update_audio_sample: true,
39+
}
40+
}
41+
42+
pub fn with_both_thumbnails(track_index: usize, segment_index: usize) -> Self {
43+
Self {
44+
track_index,
45+
segment_index,
46+
update_thumbnail: (true, true),
47+
update_audio_sample: true,
48+
}
49+
}
50+
51+
pub fn with_audio_only(track_index: usize, segment_index: usize) -> Self {
52+
Self {
53+
track_index,
54+
segment_index,
55+
update_thumbnail: (false, false),
56+
update_audio_sample: true,
57+
}
58+
}
59+
60+
pub fn should_update(&self) -> bool {
61+
!matches!(self.update_thumbnail, (false, false)) || self.update_audio_sample
62+
}
63+
}
64+
65+
// Information about segments that need preview refresh after a command
66+
#[derive(Clone, Debug, Default)]
67+
pub struct AffectedSegments {
68+
pub segments: Vec<AffectedSegment>,
69+
}
70+
71+
impl AffectedSegments {
72+
pub fn new() -> Self {
73+
Self { segments: vec![] }
74+
}
75+
76+
pub fn add(&mut self, track_index: usize, segment_index: usize) {
77+
self.segments
78+
.push(AffectedSegment::new(track_index, segment_index));
79+
}
80+
81+
pub fn add_left_thumbnail(&mut self, track_index: usize, segment_index: usize) {
82+
self.segments.push(AffectedSegment::with_left_thumbnail(
83+
track_index,
84+
segment_index,
85+
));
86+
}
87+
88+
pub fn add_right_thumbnail(&mut self, track_index: usize, segment_index: usize) {
89+
self.segments.push(AffectedSegment::with_right_thumbnail(
90+
track_index,
91+
segment_index,
92+
));
93+
}
94+
95+
pub fn add_both_thumbnails(&mut self, track_index: usize, segment_index: usize) {
96+
self.segments.push(AffectedSegment::with_both_thumbnails(
97+
track_index,
98+
segment_index,
99+
));
100+
}
101+
102+
pub fn add_audio_only(&mut self, track_index: usize, segment_index: usize) {
103+
self.segments
104+
.push(AffectedSegment::with_audio_only(track_index, segment_index));
105+
}
106+
107+
pub fn merge(&mut self, other: AffectedSegments) {
108+
self.segments.extend(other.segments);
109+
}
110+
111+
pub fn is_empty(&self) -> bool {
112+
self.segments.is_empty()
113+
}
114+
}
115+
3116
pub trait Command: Send + Sync {
4117
fn describe(&self) -> String;
5118
fn execute(&mut self, manager: &mut Manager) -> Result<()>;
@@ -8,12 +121,23 @@ pub trait Command: Send + Sync {
8121
true
9122
}
10123

11-
/// Optional: Merge with another command (for optimization)
12-
///
13-
/// If this command can be merged with another (e.g., multiple small
14-
/// adjustments of the same property), return true and modify self.
15-
/// By default, commands don't merge.
124+
// Optional: Merge with another command (for optimization)
125+
//
126+
// If this command can be merged with another (e.g., multiple small
127+
// adjustments of the same property), return true and modify self.
128+
// By default, commands don't merge.
16129
fn merge(&mut self, _other: Box<dyn Command>) -> Result<bool> {
17130
Ok(false)
18131
}
132+
133+
// Returns segments that need preview refresh after execute()
134+
// Used by UI layer to reload thumbnails/audio previews after undo/redo
135+
fn affected_segments_after_execute(&self) -> AffectedSegments {
136+
AffectedSegments::default()
137+
}
138+
139+
// Returns segments that need preview refresh after undo()
140+
fn affected_segments_after_undo(&self) -> AffectedSegments {
141+
AffectedSegments::default()
142+
}
19143
}

lib/video-editor/src/commands/history.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::{batch::BatchCommand, command::Command};
1+
use super::{
2+
batch::BatchCommand,
3+
command::{AffectedSegments, Command},
4+
};
25
use crate::{Error, Result, tracks::manager::Manager};
36

47
#[derive(derivative::Derivative)]
@@ -14,6 +17,15 @@ pub struct HistoryManager {
1417
current_batch: Option<BatchCommand>,
1518
}
1619

20+
pub struct UndoRedoResult {
21+
pub description: String,
22+
pub affected_segments: AffectedSegments,
23+
}
24+
25+
pub struct ExecuteResult {
26+
pub affected_segments: AffectedSegments,
27+
}
28+
1729
impl HistoryManager {
1830
pub fn new() -> Self {
1931
Self::default()
@@ -29,47 +41,60 @@ impl HistoryManager {
2941
// Execute a command and add it to history
3042
// If the command executes successfully, it's added to the undo stack
3143
// and the redo stack is cleared (since a new command invalidates redo history).
32-
pub fn execute(&mut self, manager: &mut Manager, mut command: Box<dyn Command>) -> Result<()> {
44+
pub fn execute(
45+
&mut self,
46+
manager: &mut Manager,
47+
mut command: Box<dyn Command>,
48+
) -> Result<ExecuteResult> {
3349
command.execute(manager)?;
50+
let affected_segments = command.affected_segments_after_execute();
3451
self.redo_stack.clear();
3552

3653
if let Some(ref mut batch) = self.current_batch {
3754
batch.add_command(command);
38-
return Ok(());
55+
return Ok(ExecuteResult { affected_segments });
3956
}
4057

4158
if self.max_history_size > 0 && self.undo_stack.len() >= self.max_history_size {
4259
self.undo_stack.remove(0);
4360
}
4461

4562
self.undo_stack.push(command);
46-
Ok(())
63+
Ok(ExecuteResult { affected_segments })
4764
}
4865

49-
pub fn undo(&mut self, manager: &mut Manager) -> Result<Option<String>> {
66+
pub fn undo(&mut self, manager: &mut Manager) -> Result<UndoRedoResult> {
5067
let mut command = self
5168
.undo_stack
5269
.pop()
5370
.ok_or_else(|| Error::CannotUndo("No commands to undo".into()))?;
5471

5572
let description = command.describe();
5673
command.undo(manager)?;
74+
let affected_segments = command.affected_segments_after_undo();
5775
self.redo_stack.push(command);
5876

59-
Ok(Some(description))
77+
Ok(UndoRedoResult {
78+
description,
79+
affected_segments,
80+
})
6081
}
6182

62-
pub fn redo(&mut self, manager: &mut Manager) -> Result<Option<String>> {
83+
pub fn redo(&mut self, manager: &mut Manager) -> Result<UndoRedoResult> {
6384
let mut command = self
6485
.redo_stack
6586
.pop()
6687
.ok_or_else(|| Error::CannotRedo("No commands to redo".into()))?;
6788

6889
let description = command.describe();
6990
command.execute(manager)?;
91+
let affected_segments = command.affected_segments_after_execute();
7092
self.undo_stack.push(command);
7193

72-
Ok(Some(description))
94+
Ok(UndoRedoResult {
95+
description,
96+
affected_segments,
97+
})
7398
}
7499

75100
/// Start a batch operation (multiple commands treated as one)

0 commit comments

Comments
 (0)