Merge pull request #576 from colliderman/fix/edit-track-header-alignments

fix(ui): center track names in edit window and remove unnecessary spacing for Solo and Mute buttons in Master tracks
This commit is contained in:
FredApps authored and GitHub committed 2026-11-03 21:01:19 +01:00
commit b26ab2b25c
2 files changed
+72 -6

No files matched your search

+42 -6
View File
@@ -115,8 +115,18 @@ fn name_plate(app: &mut SoundApp, ui: &mut Ui, track: &Track, head: Rect, select
track.name.clone()
};
let name_color = if selected { t.text_dark } else { t.text.lerp_to_gamma(t.text_dim, 0.4) };
ui.painter().with_clip_rect(name_r).text(pos2(name_r.min.x + 1.5, name_r.center().y), Align2::LEFT_CENTER, &shown_name, bold(12.5), name_color);
let nresp = ui.interact(name_r, ui.scope_id().with(("name", id.0)), Sense::click_and_drag());
let font = bold(12.5);
let galley = ui.painter().layout_no_wrap(shown_name.clone(), font.clone(), name_color);
let overflows = galley.size().x > name_r.width() - 3.0;
let (text_pos, align) =
if overflows { (pos2(name_r.min.x + 1.5, name_r.center().y), Align2::LEFT_CENTER) } else { (name_r.center(), Align2::CENTER_CENTER) };
ui.painter().with_clip_rect(name_r).text(text_pos, align, &shown_name, font, name_color);
let mut nresp = ui.interact(name_r, ui.scope_id().with(("name", id.0)), Sense::click_and_drag());
if overflows {
nresp = nresp.on_hover_ui(|ui| {
ui.label(&shown_name);
});
}
if nresp.double_clicked() {
app.dialogs.open_rename_track(id, &track.name);
} else if nresp.clicked() {
@@ -190,8 +200,9 @@ fn toggle_keys(app: &mut SoundApp, ui: &mut Ui, track: &Track, head: Rect) {
let h = head.height();
let at = |x: f32, y: f32, w: f32, h: f32| Rect::from_min_size(pos2(head.min.x + x, head.min.y + y), vec2(w, h));
let m = &track.mixer;
if h >= 37.0 {
let (records, mixes) = (track.kind.has_playlist(), track.kind != TrackKind::Master);
let is_master = track.kind == TrackKind::Master;
if h >= 37.0 && !is_master {
let (records, mixes) = (track.kind.has_playlist(), true);
let implied = !m.solo && app.engine.session().tracks.iter().any(|t| t.mixer.solo);
let keys = [
(HeaderKey::Rec, m.record_arm, false, "mix.record_arm", records),
@@ -213,10 +224,13 @@ fn view_selector(app: &mut SoundApp, ui: &mut Ui, track: &Track, head: Rect) {
let id = track.id;
let h = head.height();
let at = |x: f32, y: f32, w: f32, h: f32| Rect::from_min_size(pos2(head.min.x + x, head.min.y + y), vec2(w, h));
if h >= 58.0 {
let is_master = track.kind == TrackKind::Master;
let view_y = if is_master { 20.0 } else { 39.0 };
let view_min_h = if is_master { 37.0 } else { 58.0 };
if h >= view_min_h {
let vresp = header_selector(
ui,
at(17.0, 39.0, 105.0, 17.0),
at(17.0, view_y, 105.0, 17.0),
ui.scope_id().with(("view", id.0)),
&view_label(&track.view),
t.text,
@@ -225,10 +239,32 @@ fn view_selector(app: &mut SoundApp, ui: &mut Ui, track: &Track, head: Rect) {
);
egui::Popup::menu(&vresp).show(|ui| view_menu(app, ui, track));
}
if is_master && h >= 58.0 {
let mode = track.mixer.automation_mode;
let col = match mode {
soundcraft_model::AutomationMode::Read => t.auto_read,
soundcraft_model::AutomationMode::Write => t.auto_write,
soundcraft_model::AutomationMode::Touch | soundcraft_model::AutomationMode::TouchLatch => t.auto_touch,
soundcraft_model::AutomationMode::Latch => t.auto_latch,
_ => t.text_dim,
};
let mode_name = format!("Automation mode for {}", track.name);
let aresp = header_selector(ui, at(17.0, 39.0, 105.0, 17.0), ui.scope_id().with(("auto_mode", id.0)), mode.label(), col, true, &mode_name);
egui::Popup::menu(&aresp).show(|ui| {
for m in soundcraft_model::AutomationMode::ALL {
if ui.selectable_label(m == mode, m.label()).clicked() {
let _ = app.run("mix.automation_mode", json!({"track": id.0, "mode": m.label()}));
}
}
});
}
}
/// Freeze, voice and automation mode.
fn voice_row(app: &mut SoundApp, ui: &mut Ui, track: &Track, head: Rect) {
if track.kind == TrackKind::Master {
return;
}
let t = Tokens::current();
let id = track.id;
let h = head.height();
+30
View File
@@ -1525,3 +1525,33 @@ fn alt_click_on_a_marker_deletes_it() {
assert_eq!(h.state().engine.session().markers.len(), before - 1);
assert!(!h.state().engine.session().markers.iter().any(|m| m.name == "Alt-delete me"));
}
#[test]
fn short_track_name_is_centered_and_long_name_is_left_aligned() {
let mut h = harness(2);
let s = h.state().engine.session_arc();
let first = s.tracks[0].id.0;
let second = s.tracks[1].id.0;
h.state_mut().run("track.rename", json!({"track": first, "name": "Vocal"})).unwrap();
h.state_mut().run("track.rename", json!({"track": second, "name": "A Very Long Track Name That Exceeds Header Width"})).unwrap();
h.step();
let r0 = row(&h, 0);
let r1 = row(&h, 1);
let first_text = h.output().shapes.iter().find_map(|s| match &s.shape {
Shape::Text(t) if t.galley.text() == "Vocal" && s.clip_rect.intersects(r0) => Some((t.pos, t.galley.size())),
_ => None,
});
let long_text = h.output().shapes.iter().find_map(|s| match &s.shape {
Shape::Text(t) if t.galley.text() == "A Very Long Track Name That Exceeds Header Width" && s.clip_rect.intersects(r1) => Some(t.pos),
_ => None,
});
let (first_pos, first_size) = first_text.expect("short track name should be rendered in edit header");
let long_pos = long_text.expect("long track name should be rendered in edit header");
assert_eq!(long_pos.x, r1.min.x + 17.5);
let first_center_x = first_pos.x + first_size.x * 0.5;
assert!((first_center_x - (r0.min.x + 64.5)).abs() <= 0.1, "short name should be centered (center was at {first_center_x})");
}