diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-28 19:41:16 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-28 19:41:16 +0200 |
| commit | f5a82152394fde4d8f740f664ab7737b82861618 (patch) | |
| tree | 5a51b181abead1ce03d2dbfc12b77918d3be0e1b /rust/crates/commands/src/timeline/track | |
| parent | e837e7504379e100ff3b56282577ec67bb88bec9 (diff) | |
| download | opencut-f5a82152394fde4d8f740f664ab7737b82861618.tar.gz | |
feat: GPUI desktop port foundation
- scene crate: full project/scene/element model, serde round-trip
proven against the web SerializedProject shape
- transfer crate: .ocp zip container, cross-compat with web fflate
archives proven in both directions
- storage crate: filesystem project store replacing IndexedDB,
.ocp import/export
- media crate: ffmpeg-next 8 decoder (frames, audio, thumbnails,
waveform summaries)
- playback crate: wall-clock anchored, frame-rounded playback clock
- gpu crate: native texture readback; compositor renders headless
offscreen on Vulkan (spike test)
- flake devshell: rust toolchain, GPUI native deps, ffmpeg dev,
bindgen clang args; GPUI hello-world builds
- port plan in notes/gpui-port.md
Diffstat (limited to 'rust/crates/commands/src/timeline/track')
5 files changed, 224 insertions, 0 deletions
diff --git a/rust/crates/commands/src/timeline/track/add_track.rs b/rust/crates/commands/src/timeline/track/add_track.rs new file mode 100644 index 00000000..d6e9bbae --- /dev/null +++ b/rust/crates/commands/src/timeline/track/add_track.rs @@ -0,0 +1,69 @@ +use scene::{Project, SceneTracks}; +use timeline::placement::{build_empty_track, get_default_insert_index_for_track}; +use timeline::TrackType; +use uuid::Uuid; + +use crate::command::{Command, CommandError, CommandResult}; +use crate::project_ext::active_scene_mut; + +pub struct AddTrackCommand { + track_type: TrackType, + index: Option<usize>, + track_id: String, + saved_state: Option<SceneTracks>, +} + +impl AddTrackCommand { + pub fn new(track_type: TrackType, index: Option<usize>) -> Self { + Self { + track_type, + index, + track_id: Uuid::new_v4().to_string(), + saved_state: None, + } + } + + pub fn track_id(&self) -> &str { + &self.track_id + } +} + +impl Command for AddTrackCommand { + fn execute( + &mut self, + project: &mut Project, + ) -> Result<Option<CommandResult>, CommandError> { + let Some(scene) = active_scene_mut(project) else { + return Ok(None); + }; + + self.saved_state = Some(scene.tracks.clone()); + + let insert_index = self.index.unwrap_or_else(|| { + get_default_insert_index_for_track(&scene.tracks, self.track_type) + }); + + let new_track = build_empty_track(&self.track_id, self.track_type, None); + if self.track_type == TrackType::Audio { + let audio_insert_index = insert_index + .saturating_sub(scene.tracks.overlay.len() + 1); + scene + .tracks + .audio + .insert(audio_insert_index.min(scene.tracks.audio.len()), new_track); + } else { + let overlay_insert_index = insert_index.min(scene.tracks.overlay.len()); + scene.tracks.overlay.insert(overlay_insert_index, new_track); + } + Ok(None) + } + + fn undo(&mut self, project: &mut Project) { + let Some(saved) = &self.saved_state else { + return; + }; + if let Some(scene) = active_scene_mut(project) { + scene.tracks = saved.clone(); + } + } +} diff --git a/rust/crates/commands/src/timeline/track/mod.rs b/rust/crates/commands/src/timeline/track/mod.rs new file mode 100644 index 00000000..c133a12d --- /dev/null +++ b/rust/crates/commands/src/timeline/track/mod.rs @@ -0,0 +1,9 @@ +pub mod add_track; +pub mod remove_track; +pub mod toggle_track_mute; +pub mod toggle_track_visibility; + +pub use add_track::AddTrackCommand; +pub use remove_track::RemoveTrackCommand; +pub use toggle_track_mute::ToggleTrackMuteCommand; +pub use toggle_track_visibility::ToggleTrackVisibilityCommand; diff --git a/rust/crates/commands/src/timeline/track/remove_track.rs b/rust/crates/commands/src/timeline/track/remove_track.rs new file mode 100644 index 00000000..5a79b4ae --- /dev/null +++ b/rust/crates/commands/src/timeline/track/remove_track.rs @@ -0,0 +1,43 @@ +use scene::{Project, SceneTracks}; + +use crate::command::{Command, CommandError, CommandResult}; +use crate::project_ext::active_scene_mut; + +pub struct RemoveTrackCommand { + track_id: String, + saved_state: Option<SceneTracks>, +} + +impl RemoveTrackCommand { + pub fn new(track_id: impl Into<String>) -> Self { + Self { + track_id: track_id.into(), + saved_state: None, + } + } +} + +impl Command for RemoveTrackCommand { + fn execute( + &mut self, + project: &mut Project, + ) -> Result<Option<CommandResult>, CommandError> { + let Some(scene) = active_scene_mut(project) else { + return Ok(None); + }; + + self.saved_state = Some(scene.tracks.clone()); + scene.tracks.overlay.retain(|track| track.id() != self.track_id); + scene.tracks.audio.retain(|track| track.id() != self.track_id); + Ok(None) + } + + fn undo(&mut self, project: &mut Project) { + let Some(saved) = &self.saved_state else { + return; + }; + if let Some(scene) = active_scene_mut(project) { + scene.tracks = saved.clone(); + } + } +} diff --git a/rust/crates/commands/src/timeline/track/toggle_track_mute.rs b/rust/crates/commands/src/timeline/track/toggle_track_mute.rs new file mode 100644 index 00000000..afaaf3f3 --- /dev/null +++ b/rust/crates/commands/src/timeline/track/toggle_track_mute.rs @@ -0,0 +1,49 @@ +use scene::{Project, SceneTracks, Track}; + +use crate::command::{Command, CommandError, CommandResult}; +use crate::project_ext::active_scene_mut; +use crate::tracks::find_track_mut; + +pub struct ToggleTrackMuteCommand { + track_id: String, + saved_state: Option<SceneTracks>, +} + +impl ToggleTrackMuteCommand { + pub fn new(track_id: impl Into<String>) -> Self { + Self { + track_id: track_id.into(), + saved_state: None, + } + } +} + +impl Command for ToggleTrackMuteCommand { + fn execute( + &mut self, + project: &mut Project, + ) -> Result<Option<CommandResult>, CommandError> { + let Some(scene) = active_scene_mut(project) else { + return Ok(None); + }; + + self.saved_state = Some(scene.tracks.clone()); + + match find_track_mut(&mut scene.tracks, &self.track_id) { + Some(Track::Video { muted, .. }) | Some(Track::Audio { muted, .. }) => { + *muted = !*muted; + } + _ => {} + } + Ok(None) + } + + fn undo(&mut self, project: &mut Project) { + let Some(saved) = &self.saved_state else { + return; + }; + if let Some(scene) = active_scene_mut(project) { + scene.tracks = saved.clone(); + } + } +} diff --git a/rust/crates/commands/src/timeline/track/toggle_track_visibility.rs b/rust/crates/commands/src/timeline/track/toggle_track_visibility.rs new file mode 100644 index 00000000..79000a73 --- /dev/null +++ b/rust/crates/commands/src/timeline/track/toggle_track_visibility.rs @@ -0,0 +1,54 @@ +use scene::{Project, SceneTracks, Track}; + +use crate::command::{Command, CommandError, CommandResult}; +use crate::project_ext::active_scene_mut; +use crate::tracks::find_track_mut; + +pub struct ToggleTrackVisibilityCommand { + track_id: String, + saved_state: Option<SceneTracks>, +} + +impl ToggleTrackVisibilityCommand { + pub fn new(track_id: impl Into<String>) -> Self { + Self { + track_id: track_id.into(), + saved_state: None, + } + } +} + +impl Command for ToggleTrackVisibilityCommand { + fn execute( + &mut self, + project: &mut Project, + ) -> Result<Option<CommandResult>, CommandError> { + let Some(scene) = active_scene_mut(project) else { + return Ok(None); + }; + + self.saved_state = Some(scene.tracks.clone()); + + match find_track_mut(&mut scene.tracks, &self.track_id) { + Some( + Track::Video { hidden, .. } + | Track::Text { hidden, .. } + | Track::Graphic { hidden, .. } + | Track::Effect { hidden, .. }, + ) => { + *hidden = !*hidden; + } + _ => {} + } + Ok(None) + } + + fn undo(&mut self, project: &mut Project) { + let Some(saved) = &self.saved_state else { + return; + }; + if let Some(scene) = active_scene_mut(project) { + scene.tracks = saved.clone(); + } + } +} |
