Transport window: counters, expanded pre/post-roll, MIDI controls, output meters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-05 23:50:24 -04:00
parent a30af91006
commit 718dccdad7
1 changed files with 60 additions and 3 deletions

View File

@ -135,7 +135,10 @@ fn transport_window(app: &mut SoundApp, ctx: &egui::Context) {
if !open {
return;
}
let t = Tokens::DARK;
egui::Window::new("Transport").open(&mut open).resizable(false).default_pos(pos2(400.0, 500.0)).show(ctx, |ui| {
let flags = app.engine.session().edit.flags.clone();
let expanded = flags.contains("view.transport.expanded");
ui.horizontal(|ui| {
for (icon, cmd) in [
("rtz", "transport.rtz"),
@ -146,14 +149,68 @@ fn transport_window(app: &mut SoundApp, ctx: &egui::Context) {
("end", "transport.go_to_end"),
("record", "transport.record"),
] {
if crate::widgets::icon_button(ui, vec2(34.0, 26.0), icon, false, cmd).clicked() {
if crate::widgets::icon_button(
ui,
vec2(34.0, 26.0),
icon,
(cmd == "transport.play" && app.is_playing()) || (cmd == "transport.record" && app.engine.transport.recording),
cmd,
)
.clicked()
{
let _ = app.run(cmd, json!({}));
}
}
if flags.contains("view.transport.output_meters") {
let (r, _) = ui.allocate_exact_size(vec2(22.0, 26.0), Sense::hover());
let m = app.main_meter;
crate::widgets::meter(ui, Rect::from_min_size(r.min, vec2(10.0, 26.0)), m.level[0], m.hold[0], m.clip);
crate::widgets::meter(ui, Rect::from_min_size(pos2(r.min.x + 12.0, r.min.y), vec2(10.0, 26.0)), m.level[1], m.hold[1], m.clip);
}
});
let s = app.engine.session();
let txt = format_position(app.position(), s.edit.main_counter, s.sample_rate, &s.tempo, s.frame_rate, s.timecode_start);
ui.label(egui::RichText::new(txt).font(mono(20.0)).color(Tokens::DARK.counter_text));
let fmt = |x: i64| format_position(x, s.edit.main_counter, s.sample_rate, &s.tempo, s.frame_rate, s.timecode_start);
if flags.contains("view.transport.counters") || expanded || flags.is_empty() {
ui.label(egui::RichText::new(fmt(app.position())).font(mono(20.0)).color(t.counter_text));
}
if expanded {
let e = s.edit.clone();
egui::Grid::new("tx_exp").num_columns(4).show(ui, |ui| {
ui.label("Start");
ui.label(egui::RichText::new(fmt(e.selection.start)).font(mono(11.0)));
ui.label("Pre-roll");
ui.label(egui::RichText::new(format!("{:.2} s", s.sample_rate.seconds(e.pre_roll))).font(mono(11.0)));
ui.end_row();
ui.label("End");
ui.label(egui::RichText::new(fmt(e.selection.end)).font(mono(11.0)));
ui.label("Post-roll");
ui.label(egui::RichText::new(format!("{:.2} s", s.sample_rate.seconds(e.post_roll))).font(mono(11.0)));
ui.end_row();
});
}
if flags.contains("view.transport.midi_controls") || expanded {
let (click, countoff, merge, loop_rec, prepost) = {
let e = &app.engine.session().edit;
(e.click, e.countoff, e.midi_merge, e.loop_record, e.pre_post_roll)
};
ui.horizontal(|ui| {
for (label, on, cmd) in [
("Click", click, "options.click"),
("Count Off", countoff, "options.countoff"),
("MIDI Merge", merge, "options.midi_merge"),
("Loop Rec", loop_rec, "options.loop_record"),
("Pre/Post", prepost, "options.pre_post_roll"),
] {
if ui.selectable_label(on, label).clicked() {
let _ = app.run(cmd, json!({}));
}
}
});
let tick = app.engine.session().tempo.samples_to_ticks(app.position(), app.engine.session().sample_rate);
let bpm = app.engine.session().tempo.tempo_at_tick(tick);
let m = app.engine.session().tempo.meter_at_tick(tick);
ui.label(egui::RichText::new(format!("♩ = {bpm:.2} {}/{}", m.numerator, m.denominator)).font(mono(12.0)).color(t.counter_text));
}
});
app.ui.show_transport = open;
}