summaryrefslogtreecommitdiff
path: root/rust/crates/timeline/src/placement/apply.rs
blob: 4e044a68d9a58bf55da0ff6e64e961882e264da9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use scene::{Element, SceneTracks, Track};

use super::track_factory::build_empty_track;
use super::{PlacementResult, ordered_tracks};
use crate::track_element_update::update_track_in_scene_tracks;
use crate::types::TrackType;

pub struct PlacementApplied {
    pub updated_tracks: SceneTracks,
    pub target_track_id: String,
}

pub struct ApplyPlacementParams {
    pub tracks: SceneTracks,
    pub placement_result: PlacementResult,
    pub elements: Vec<Element>,
    pub new_track_id: String,
    pub new_track_insert_index_override: Option<usize>,
}

pub fn apply_placement(params: &ApplyPlacementParams) -> Option<PlacementApplied> {
    let tracks = &params.tracks;
    match &params.placement_result {
        PlacementResult::ExistingTrack { track_index, .. } => {
            let ordered = ordered_tracks(tracks);
            let target_track = ordered.get(*track_index)?;

            let updated_tracks =
                update_track_in_scene_tracks(tracks, target_track.id(), |track| {
                    let mut next = track.clone();
                    next.elements_mut().extend(params.elements.iter().cloned());
                    next
                });

            Some(PlacementApplied {
                updated_tracks,
                target_track_id: target_track.id().to_string(),
            })
        }
        PlacementResult::NewTrack {
            track_type,
            insert_index,
            ..
        } => {
            let new_track_id = params.new_track_id.clone();
            let insert_index = params
                .new_track_insert_index_override
                .unwrap_or(*insert_index);
            let new_track = build_empty_track(new_track_id.clone(), *track_type, None)
                .with_elements(params.elements.clone());

            let updated_tracks = if *track_type == TrackType::Audio {
                SceneTracks {
                    audio: insert_into_audio_tracks(tracks, insert_index, new_track),
                    ..tracks.clone()
                }
            } else {
                SceneTracks {
                    overlay: insert_into_overlay_tracks(tracks, insert_index, new_track),
                    ..tracks.clone()
                }
            };

            Some(PlacementApplied {
                updated_tracks,
                target_track_id: new_track_id,
            })
        }
    }
}

fn insert_into_overlay_tracks(
    tracks: &SceneTracks,
    insert_index: usize,
    track: Track,
) -> Vec<Track> {
    let normalized = insert_index.min(tracks.overlay.len());
    let mut next = tracks.overlay.clone();
    next.insert(normalized, track);
    next
}

fn insert_into_audio_tracks(tracks: &SceneTracks, insert_index: usize, track: Track) -> Vec<Track> {
    let audio_index = insert_index
        .saturating_sub(tracks.overlay.len() + 1)
        .min(tracks.audio.len());
    let mut next = tracks.audio.clone();
    next.insert(audio_index, track);
    next
}

trait WithElements {
    fn with_elements(self, elements: Vec<Element>) -> Self;
}

impl WithElements for Track {
    fn with_elements(mut self, elements: Vec<Element>) -> Self {
        *self.elements_mut() = elements;
        self
    }
}