Mix in a separate window: Edit and Mix on screen at once
Window > Mix in Separate Window (window.mix_separate, kept with the UI preferences) shows the Mix window in its own native window through an egui immediate viewport, so Edit and Mix can be seen at once, e.g. one per monitor. The main window then keeps Edit; Window > Mix and Cmd/Ctrl+= bring the Mix window to the front; closing it puts Mix back into the main window. Backends without multiple windows show it as a floating egui window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8d5b38ab3a
commit
ea531b723f
|
|
@ -88,6 +88,9 @@ pub struct UiState {
|
|||
pub show_workspace: bool,
|
||||
pub show_configurations: bool,
|
||||
pub show_playback_engine: bool,
|
||||
/// The Mix window lives in its own native window (Window › Mix in Separate Window); the main
|
||||
/// window then always shows Edit.
|
||||
pub mix_separate: bool,
|
||||
pub show_io_setup: bool,
|
||||
pub show_shortcuts: bool,
|
||||
pub show_clip_effects: bool,
|
||||
|
|
@ -149,6 +152,7 @@ impl Default for UiState {
|
|||
show_workspace: false,
|
||||
show_configurations: false,
|
||||
show_playback_engine: false,
|
||||
mix_separate: false,
|
||||
show_io_setup: false,
|
||||
show_shortcuts: false,
|
||||
show_clip_effects: false,
|
||||
|
|
@ -253,6 +257,8 @@ pub struct SoundApp {
|
|||
pub ops: ops_windows::OpsState,
|
||||
pub extra: extra_windows::ExtraState,
|
||||
pub palette: palette::PaletteState,
|
||||
/// Bring the separate Mix window to the front on the next frame.
|
||||
pub(crate) focus_mix: bool,
|
||||
pub synthetic: Vec<egui::Event>,
|
||||
synthetic_grace: u32,
|
||||
control_rx: Option<Receiver<ControlRequest>>,
|
||||
|
|
@ -322,6 +328,7 @@ impl SoundApp {
|
|||
ops: ops_windows::OpsState::default(),
|
||||
extra: extra_windows::ExtraState::default(),
|
||||
palette: palette::PaletteState::default(),
|
||||
focus_mix: false,
|
||||
synthetic: Vec::new(),
|
||||
synthetic_grace: 0,
|
||||
control_rx: None,
|
||||
|
|
@ -929,10 +936,14 @@ impl SoundApp {
|
|||
if !self.native_menu_bar {
|
||||
menus::menu_bar(self, ui);
|
||||
}
|
||||
match self.ui.window {
|
||||
// With the Mix window in its own native window, the main window keeps Edit.
|
||||
match if self.ui.mix_separate { MainWindow::Edit } else { self.ui.window } {
|
||||
MainWindow::Edit => edit_window::show(self, ui),
|
||||
MainWindow::Mix => mix_window::show(self, ui),
|
||||
}
|
||||
if self.ui.mix_separate {
|
||||
mix_window::show_separate(self, &ctx);
|
||||
}
|
||||
panels::floating(self, &ctx);
|
||||
windows::show(self, &ctx);
|
||||
video_window::show(self, &ctx);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, Option<&str>)] = &[
|
|||
("window.mix", "Mix", "Window > Mix", Some("Cmd+=")),
|
||||
("window.edit", "Edit", "Window > Edit", Some("Cmd+=")),
|
||||
("window.toggle_mix_edit", "Toggle Mix/Edit", "", Some("Cmd+=")),
|
||||
("window.mix_separate", "Mix in Separate Window", "", None),
|
||||
("window.transport", "Transport", "Window > Transport", Some("Cmd+1")),
|
||||
("window.big_counter", "Big Counter", "Window > Big Counter", Some("Cmd+3")),
|
||||
("window.memory_locations", "Memory Locations", "Window > Memory Locations", Some("Cmd+5")),
|
||||
|
|
@ -243,13 +244,25 @@ pub fn menu_bar(app: &mut SoundApp, ui: &mut egui::Ui) {
|
|||
ui.separator();
|
||||
ui.label(egui::RichText::new("Processes selected clips offline").small().color(t.text_dim));
|
||||
}
|
||||
if root.label == "Window" {
|
||||
ui.separator();
|
||||
let mut separate = app.ui.mix_separate;
|
||||
if ui
|
||||
.checkbox(&mut separate, "Mix in Separate Window")
|
||||
.on_hover_text("Show Edit and Mix at once, e.g. one per monitor")
|
||||
.changed()
|
||||
{
|
||||
let _ = run_ui_command(app, "window.mix_separate", &json!({"value": separate}));
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
let title = format!(
|
||||
"{}{} — {}",
|
||||
app.engine.session().name,
|
||||
if app.engine.is_dirty() { " *" } else { "" },
|
||||
if app.ui.window == MainWindow::Edit { "Edit" } else { "Mix" }
|
||||
if app.ui.window == MainWindow::Edit || app.ui.mix_separate { "Edit" } else { "Mix" }
|
||||
);
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.label(egui::RichText::new(title).color(t.text_dim));
|
||||
|
|
@ -473,14 +486,25 @@ pub fn run_ui_command(app: &mut SoundApp, id: &str, p: &Value) -> Option<Result<
|
|||
let r = match id {
|
||||
"window.mix" => {
|
||||
app.ui.window = MainWindow::Mix;
|
||||
app.focus_mix = app.ui.mix_separate;
|
||||
json!({"window": "mix"})
|
||||
}
|
||||
"window.mix_separate" => {
|
||||
let v = toggle(&mut app.ui.mix_separate);
|
||||
app.focus_mix = app.ui.mix_separate;
|
||||
v
|
||||
}
|
||||
"window.edit" => {
|
||||
app.ui.window = MainWindow::Edit;
|
||||
json!({"window": "edit"})
|
||||
}
|
||||
"window.toggle_mix_edit" => {
|
||||
app.ui.window = if app.ui.window == MainWindow::Edit { MainWindow::Mix } else { MainWindow::Edit };
|
||||
if app.ui.mix_separate {
|
||||
// Both are on screen: bring the Mix window forward instead.
|
||||
app.focus_mix = true;
|
||||
} else {
|
||||
app.ui.window = if app.ui.window == MainWindow::Edit { MainWindow::Mix } else { MainWindow::Edit };
|
||||
}
|
||||
json!({"window": app.ui.window})
|
||||
}
|
||||
"window.transport" => toggle(&mut app.ui.show_transport),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,25 @@ pub fn show(app: &mut SoundApp, ui: &mut Ui) {
|
|||
crate::docking::show(app, ui, crate::MainWindow::Mix);
|
||||
}
|
||||
|
||||
/// The Mix window in its own native window (Window › Mix in Separate Window), so Edit and Mix can
|
||||
/// be seen at once, e.g. one per monitor. Closing that window brings Mix back into the main one.
|
||||
/// Backends without multiple windows show it as a floating egui window instead.
|
||||
pub fn show_separate(app: &mut SoundApp, ctx: &egui::Context) {
|
||||
let title = format!("{} — Mix", app.engine.session().name);
|
||||
let builder = egui::ViewportBuilder::default().with_title(title).with_inner_size([1100.0, 720.0]).with_min_inner_size([480.0, 320.0]);
|
||||
ctx.show_viewport_immediate(egui::ViewportId::from_hash_of("soundcraft.mix"), builder, |ui, _class| {
|
||||
ui.set_style(ui.ctx().global_style());
|
||||
if std::mem::take(&mut app.focus_mix) {
|
||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Focus);
|
||||
}
|
||||
show(app, ui);
|
||||
if ui.ctx().input(|i| i.viewport().close_requested()) {
|
||||
app.ui.mix_separate = false;
|
||||
app.ui.window = crate::MainWindow::Mix;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn content(app: &mut SoundApp, ui: &mut Ui) {
|
||||
let ids: Vec<TrackId> = app.engine.session().tracks.iter().filter(|x| !x.hidden && x.kind != TrackKind::Video).map(|x| x.id).collect();
|
||||
// Per-channel peaks for multichannel meters (read once per frame).
|
||||
|
|
|
|||
Loading…
Reference in New Issue