feat(ui): MAIN and ADVANCED pages, Anvil main page around Mode, Split and Sub level

Producers found the panels crowded: every control had the same weight. The
header now carries a MAIN / ADVANCED switch (common-ui header_paged). The
main page keeps the controls a first result needs; the advanced page shows
everything, as before.

- Anvil: Mode (Replace or Enhance), Split and Sub level, plus Octave, Ghost,
  Keep and Output. Kick lock, Dynamics, Drive, Glide, Phase, Gate and the
  pitch switches move to Advanced. The note readout says when the original
  sub plays because tracking is unsure.
- Lathe, Temper, Knurl, Planish, Braze: tracking and fine-tuning controls
  (Gate, Smooth, Snap, Harmonics, Push at, Floor, Bright, Normalize, band
  gains, Phase) move to Advanced. Lathe labels the octaves of the note in Hz
  on the harmonic axis.
- Forge passes the page to its module pages.
- Weld: the main page shows the level won back at full height, Mode,
  Response and Output; the phase plot, Sections, Low, High and Gate move to
  Advanced.
- editor-check renders the advanced pages with --advanced.
This commit is contained in:
nvrl committed 2026-11-06 21:16:11 +01:00
1 parent d6939a753a
commit 01fd3241eb
10 files changed
+594 -227

No files matched your search

+44 -26
View File
@@ -21,6 +21,8 @@ pub struct State {
/// True while a pointer button is held, so knob drags become one host gesture.
pub gesture_active: bool,
pub help: String,
/// The advanced page shows its extra controls. Not saved.
pub advanced: bool,
}
impl Default for State {
fn default() -> Self {
@@ -28,6 +30,7 @@ impl Default for State {
analyzer: AnalyzerFrame::default(),
report: Report::default(),
gesture_active: false,
advanced: false,
help: DEFAULT_HELP.into(),
}
}
@@ -86,7 +89,8 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
state.help = DEFAULT_HELP.into();
state.gesture_active = ui.input(|i| i.pointer.any_down());
ui.add_space(2.);
header(ui, "WELD", "LAYER ALIGN", |ui| {
let mut advanced = state.advanced;
header_paged(ui, "WELD", "LAYER ALIGN", Some(&mut advanced), |ui| {
let r = ui
.selectable_label(settings.bypass, RichText::new("BYPASS").size(13.))
.on_hover_text("Pass the main layer through unchanged.");
@@ -107,6 +111,7 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
}
caption(ui, "LISTEN");
});
state.advanced = advanced;
ui.add_space(6.);
ui.horizontal_top(|ui| {
panel(ui, |ui| {
@@ -119,9 +124,14 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
ui.set_width(ui.available_width());
ui.vertical(|ui| {
let report = state.report;
band_plot(ui, &report, 190.);
ui.add_space(4.);
phase_plot(ui, &report, TOP - 194.);
// The phase plot is for fine tuning: the main page shows the level won back.
if state.advanced {
band_plot(ui, &report, 190.);
ui.add_space(4.);
phase_plot(ui, &report, TOP - 194.);
} else {
band_plot(ui, &report, TOP);
}
});
});
});
@@ -514,35 +524,43 @@ fn controls(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
}
hint(&r, state, help);
}
ui.add_space(30.);
caption(ui, "SECTIONS");
let minus = ui.button(RichText::new("−").size(14.));
if minus.clicked() && settings.sections > 1 {
settings.sections -= 1;
if state.advanced {
ui.add_space(30.);
caption(ui, "SECTIONS");
let minus = ui.button(RichText::new("−").size(14.));
if minus.clicked() && settings.sections > 1 {
settings.sections -= 1;
}
ui.label(RichText::new(format!("{}", settings.sections)).monospace().size(14.).color(TEXT));
let plus = ui.button(RichText::new("+").size(14.));
if plus.clicked() && settings.sections < MAX_SECTIONS {
settings.sections += 1;
}
let help = "Most allpass sections the fit may use. Fewer is gentler; Weld uses fewer when they are enough.";
hint(&minus, state, help);
hint(&plus, state, help);
}
ui.label(RichText::new(format!("{}", settings.sections)).monospace().size(14.).color(TEXT));
let plus = ui.button(RichText::new("+").size(14.));
if plus.clicked() && settings.sections < MAX_SECTIONS {
settings.sections += 1;
}
let help = "Most allpass sections the fit may use. Fewer is gentler; Weld uses fewer when they are enough.";
hint(&minus, state, help);
hint(&plus, state, help);
});
ui.add_space(4.);
ui.columns(6, |c| {
let advanced = state.advanced;
ui.columns(if advanced { 6 } else { 3 }, |c| {
knob(&mut c[0], state, "Response", &mut settings.response, 0.3..=5., d.response, " s",
"Averaging time in FOLLOW. Longer is steadier; shorter follows switch-ups faster.");
knob(&mut c[1], state, "Low", &mut settings.low, 30. ..=400., d.low, " Hz",
"Lowest frequency the fit listens to. Leave the sub to Anvil.");
knob(&mut c[2], state, "High", &mut settings.high, 1000. ..=12000., d.high, " Hz",
"Highest frequency the fit listens to.");
knob(&mut c[3], state, "Gate", &mut settings.gate, -70. ..=-30., d.gate, " dB",
"Moments where either layer is quieter than this are not measured.");
knob(&mut c[4], state, "Output", &mut settings.output, -12. ..=12., d.output, " dB",
let at = if advanced {
knob(&mut c[1], state, "Low", &mut settings.low, 30. ..=400., d.low, " Hz",
"Lowest frequency the fit listens to. Leave the sub to Anvil.");
knob(&mut c[2], state, "High", &mut settings.high, 1000. ..=12000., d.high, " Hz",
"Highest frequency the fit listens to.");
knob(&mut c[3], state, "Gate", &mut settings.gate, -70. ..=-30., d.gate, " dB",
"Moments where either layer is quieter than this are not measured.");
4
} else {
1
};
knob(&mut c[at], state, "Output", &mut settings.output, -12. ..=12., d.output, " dB",
"Output trim.");
let report = state.report;
c[5].vertical(|ui| {
c[at + 1].vertical(|ui| {
caption(ui, "TIME OFFSET");
let clear = report.lag_strength > 0.3;
ui.label(big(