From 04dab602c7cd7f1ee1645919a238568e12ea7d02 Mon Sep 17 00:00:00 2001 From: ljkx <177671075+ljkx@users.noreply.github.com> Date: Mon, 2 Nov 2026 19:38:53 +0100 Subject: [PATCH] A new send opens its Send window, so its fader is on screen right away (#1269) Picking a destination for an empty send slot, or making one with new bus..., now opens that send's Send window, as in Pro Tools. Moving an existing send to another bus leaves the window as it was. --- crates/ui-egui/src/mix_window/sends.rs | 86 +++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/mix_window/sends.rs b/crates/ui-egui/src/mix_window/sends.rs index 87cac59d..dc338527 100644 --- a/crates/ui-egui/src/mix_window/sends.rs +++ b/crates/ui-egui/src/mix_window/sends.rs @@ -388,8 +388,15 @@ fn send_route_menu(app: &mut SoundApp, ui: &Ui, field: &egui::Response, id: Trac }, ); let Some((picked, footer)) = shown else { return }; + // As in Pro Tools, a new send opens its Send window, so its fader is on screen right away. + let created = |app: &mut SoundApp, sent: bool| { + if sent && !has_send { + app.ui.send_window = Some((id, slot)); + } + }; if let Some(bus) = picked { - let _ = app.run("mix.send", json!({"track": id.0, "slot": slot, "bus": bus})); + let sent = app.run("mix.send", json!({"track": id.0, "slot": slot, "bus": bus})).is_ok(); + created(app, sent); } match footer { Some(SendFooter::NewBus) => { @@ -400,7 +407,8 @@ fn send_route_menu(app: &mut SoundApp, ui: &Ui, field: &egui::Response, id: Trac n += 1; } if app.run("mix.new_bus", json!({"name": format!("Bus {n}")})).is_ok() { - let _ = app.run("mix.send", json!({"track": id.0, "slot": slot, "bus": format!("Bus {n}")})); + let sent = app.run("mix.send", json!({"track": id.0, "slot": slot, "bus": format!("Bus {n}")})).is_ok(); + created(app, sent); } } Some(SendFooter::RemoveSend) => { @@ -464,4 +472,78 @@ mod tests { assert_eq!(field(&h), search, "the search field stays put"); assert!((footer(&h).top() - full.top()).abs() < 1.0, "cleared, the footer is back under the full list: {:?} vs {full:?}", footer(&h)); } + + /// A send slot drawn in a test harness, for the track named "Kick", with an "Aux" bus to pick. + fn kick_send_harness(send_first: bool) -> (Harness<'static, (SoundApp, TrackId)>, TrackId) { + let mut app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default()); + app.run("mix.new_bus", json!({"name": "Aux"})).unwrap(); + app.run("mix.new_bus", json!({"name": "Other"})).unwrap(); + let kick = app.engine.session().track_by_name("Kick").unwrap().id; + if send_first { + app.run("mix.send", json!({"track": kick.0, "slot": 0, "bus": "Other"})).unwrap(); + } + let mut ready = false; + let h = Harness::builder().with_size(vec2(600.0, 800.0)).build_ui_state( + move |ui, (app, kick): &mut (SoundApp, TrackId)| { + if !ready { + crate::fonts::install(ui.ctx(), true); + crate::theme::apply(ui.ctx(), false); + ready = true; + return; + } + let track = app.engine.session().track(*kick).cloned().unwrap(); + let r = Rect::from_min_size(ui.max_rect().min + vec2(20.0, 20.0), vec2(120.0, 24.0)); + send_slot(app, ui, &track, 0, r); + }, + (app, kick), + ); + (h, kick) + } + + /// Creating a send opens its Send window, so the new send's fader is on screen right away (#1269). + #[test] + fn creating_a_send_opens_its_send_window() { + let _palette = crate::theme::lock_test_palette(); + let (mut h, kick) = kick_send_harness(false); + h.run(); + h.get_by_label("Send A for Kick").click(); + h.run(); + h.get_by_label("Aux").click(); + h.run(); + let (app, _) = h.state(); + let send = app.engine.session().track(kick).and_then(|t| t.mixer.sends.first().cloned().flatten()); + assert!(send.is_some(), "the send was created"); + assert_eq!(app.ui.send_window, Some((kick, 0)), "the new send's window is open"); + } + + /// So does a send made with "new bus...". + #[test] + fn a_send_to_a_new_bus_opens_its_send_window() { + let _palette = crate::theme::lock_test_palette(); + let (mut h, kick) = kick_send_harness(false); + h.run(); + h.get_by_label("Send A for Kick").click(); + h.run(); + h.get_by_label("new bus...").click(); + h.run(); + let (app, _) = h.state(); + assert!(app.engine.session().track(kick).and_then(|t| t.mixer.sends.first().cloned().flatten()).is_some(), "the send was created"); + assert_eq!(app.ui.send_window, Some((kick, 0)), "the new send's window is open"); + } + + /// Moving an existing send to another bus keeps the Send window as it was. + #[test] + fn changing_a_sends_destination_leaves_the_send_window_alone() { + let _palette = crate::theme::lock_test_palette(); + let (mut h, kick) = kick_send_harness(true); + h.run(); + h.get_by_label("Send A for Kick").click(); + h.run(); + h.get_by_label("Aux").click(); + h.run(); + let (app, _) = h.state(); + let target = app.engine.session().track(kick).and_then(|t| t.mixer.sends.first().cloned().flatten()).map(|s| route_name(app, &s.target)); + assert_eq!(target.as_deref(), Some("Aux"), "the send moved"); + assert_eq!(app.ui.send_window, None, "no window opened"); + } }