Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 60 additions & 9 deletions apps/desktop-gpui/src/editor_edits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,42 @@ pub fn reorder_overlay_track(
true
}

/// Where `lane` ends up when lane `from` is dragged to `to`: the moved lane
/// takes `to`, and the lanes it passed shift by one toward `from`.
fn reordered_lane(lane: u32, from: u32, to: u32) -> u32 {
if lane == from {
to
} else if from < to && lane > from && lane <= to {
lane - 1
} else if from > to && lane >= to && lane < from {
lane + 1
} else {
lane
}
}

/// Moves `kind`'s lane names along with a [`reorder_track_lane`], so a name
/// stays on the lane it was given to.
pub fn reorder_track_names(
names: &mut std::collections::BTreeMap<String, String>,
kind: TrackKind,
from: u32,
to: u32,
) {
let prefix = format!("{}:", kind.name_key());
let moved: Vec<(u32, String)> = names
.iter()
.filter_map(|(key, name)| {
let lane = key.strip_prefix(&prefix)?.parse::<u32>().ok()?;
Some((lane, name.clone()))
})
.collect();
names.retain(|key, _| !key.starts_with(&prefix));
for (lane, name) in moved {
names.insert(format!("{prefix}{}", reordered_lane(lane, from, to)), name);
}
}

pub fn reorder_track_lane(
timeline: &mut TimelineConfiguration,
kind: TrackKind,
Expand All @@ -1172,15 +1208,7 @@ pub fn reorder_track_lane(
let mut changed = false;
for segment in segments {
let lane = segment.lane();
let next = if lane == from {
to
} else if from < to && lane > from && lane <= to {
lane - 1
} else if from > to && lane >= to && lane < from {
lane + 1
} else {
lane
};
let next = reordered_lane(lane, from, to);
if next != lane {
set(segment, next);
changed = true;
Expand Down Expand Up @@ -2895,6 +2923,29 @@ mod tests {

// -- Selection ----------------------------------------------------------

#[test]
fn lane_names_follow_their_lane_when_lanes_are_reordered() {
let mut names = std::collections::BTreeMap::from([
("audio:0".to_string(), "Voice".to_string()),
("audio:1".to_string(), "Music".to_string()),
("style:0".to_string(), "Intro".to_string()),
]);
reorder_track_names(&mut names, TrackKind::Audio, 1, 0);
assert_eq!(names.get("audio:0").map(String::as_str), Some("Music"));
assert_eq!(names.get("audio:1").map(String::as_str), Some("Voice"));
assert_eq!(names.get("style:0").map(String::as_str), Some("Intro"));

// Dragging lane 0 to the bottom of three shifts the others up.
let mut names = std::collections::BTreeMap::from([
("audio:0".to_string(), "A".to_string()),
("audio:2".to_string(), "C".to_string()),
]);
reorder_track_names(&mut names, TrackKind::Audio, 0, 2);
assert_eq!(names.get("audio:2").map(String::as_str), Some("A"));
assert_eq!(names.get("audio:1").map(String::as_str), Some("C"));
assert!(!names.contains_key("audio:0"));
}

#[test]
fn a_plain_click_selects_one_segment() {
let selection = click_selection(None, TrackKind::Zoom, 3, false, false);
Expand Down
72 changes: 69 additions & 3 deletions apps/desktop-gpui/src/editor_timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,23 @@ pub enum TrackKind {
}

impl TrackKind {
/// The track's part of a `ProjectConfiguration::track_names` key.
pub fn name_key(self) -> &'static str {
match self {
Self::Clip => "clip",
Self::Style => "style",
Self::Image => "image",
Self::Caption => "caption",
Self::Keyboard => "keyboard",
Self::Text => "text",
Self::Mask => "mask",
Self::Audio => "audio",
Self::Zoom => "zoom",
Self::ThreeD => "3d",
Self::Scene => "scene",
}
}

/// `trackDefinitions` (`TL/index.tsx:89-144`) and `trackIcons` (`:70-80`).
pub fn label(self) -> &'static str {
match self {
Expand Down Expand Up @@ -1291,6 +1308,8 @@ impl TrackRow {
/// Everything the timeline draws, derived once per project-config change.
#[derive(Debug, Clone, Default)]
pub struct TimelineModel {
/// Lane names the user gave, by `"<track>:<lane>"`.
pub track_names: std::collections::BTreeMap<String, SharedString>,
pub style: Vec<Segment>,
pub image: Vec<Segment>,
pub rows: Vec<TrackRow>,
Expand Down Expand Up @@ -1530,6 +1549,7 @@ impl TimelineModel {
})
.collect();
let mut model = Self {
track_names: Default::default(),
style,
image,
rows: Vec::new(),
Expand Down Expand Up @@ -1568,6 +1588,12 @@ impl TimelineModel {
) -> Self {
let mut model = Self::build(config, has_camera, multiple_clips);
model.rows = build_rows(config, &model, has_camera, lanes);
model.track_names = config
.track_names
.iter()
.filter(|(_, name)| !name.trim().is_empty())
.map(|(key, name)| (key.clone(), SharedString::from(name.clone())))
.collect();
model
}
}
Expand Down Expand Up @@ -1887,10 +1913,14 @@ impl SegmentUi<'_> {
// Rendering
// ---------------------------------------------------------------------------

pub fn track_name_key(row: TrackRow) -> String {
format!("{}:{}", row.kind.name_key(), row.lane)
}

/// The gutter cell: a tinted 22px tile carrying the track's glyph, then the
/// track's name. The saturated pill the source draws
/// (`TL/TrackManager.tsx:264-281`) is gone.
pub fn track_chip(theme: &Theme, kind: TrackKind) -> impl IntoElement {
pub fn track_chip(theme: &Theme, kind: TrackKind, name: Option<SharedString>) -> impl IntoElement {
let color = kind.color();
div()
.w_full()
Expand Down Expand Up @@ -1925,7 +1955,7 @@ pub fn track_chip(theme: &Theme, kind: TrackKind) -> impl IntoElement {
.text_size(px(11.))
.line_height(px(14.))
.font_weight(FontWeight::MEDIUM)
.child(kind.label()),
.child(name.unwrap_or_else(|| SharedString::new_static(kind.label()))),
)
}

Expand Down Expand Up @@ -2222,7 +2252,11 @@ pub fn render_row(
.w(px(TRACK_GUTTER))
.flex_none()
.relative()
.child(track_chip(theme, row.kind)),
.child(track_chip(
theme,
row.kind,
model.track_names.get(&track_name_key(row)).cloned(),
)),
Comment on lines +2255 to +2259

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Deletion gives lanes wrong names

Lane deletion shifts segment indexes but leaves the new track_names keys unchanged. Name audio:0 “Voice” and audio:1 “Music”, then delete lane 0: the music segments move to lane 0 and display “Voice”. Remove the deleted lane’s name and shift higher name keys down in the same edit, including when deleting an empty lane.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_timeline.rs
Line: 2217-2221

Comment:
**Deletion gives lanes wrong names**

Lane deletion shifts segment indexes but leaves the new `track_names` keys unchanged. Name `audio:0` “Voice” and `audio:1` “Music”, then delete lane 0: the music segments move to lane 0 and display “Voice”. Remove the deleted lane’s name and shift higher name keys down in the same edit, including when deleting an empty lane.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

)
.child(
div()
Expand Down Expand Up @@ -3865,6 +3899,38 @@ mod tests {

const AUDIO_SAMPLE_RATE: usize = 48_000;

#[test]
fn lane_names_come_from_the_project_by_track_and_lane() {
let mut config = ProjectConfiguration::default();
config.track_names.insert("audio:1".into(), "Music".into());
config.track_names.insert("text:0".into(), " ".into());
let lanes = TrackLanes {
style: 0,
image: 0,
caption: false,
keyboard: false,
scene: false,
three_d: false,
text: 1,
mask: 0,
audio: 2,
};
let model = TimelineModel::build_with_lanes(&config, false, false, &lanes);
let audio = TrackRow {
kind: TrackKind::Audio,
lane: 1,
};
assert_eq!(track_name_key(audio), "audio:1");
assert_eq!(
model.track_names.get("audio:1").map(|n| n.as_ref()),
Some("Music")
);
assert!(
!model.track_names.contains_key("text:0"),
"blank names are ignored"
);
}

// -- The ruler ----------------------------------------------------------

#[test]
Expand Down
Loading
Loading