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

+65
View File
@@ -312,6 +312,16 @@ pub fn readout(ui: &mut egui::Ui, label: &str, value: String, color: Color32) {
}
/// Product header: accent mark, product name, subtitle, then right-aligned controls.
pub fn header(ui: &mut egui::Ui, name: &str, subtitle: &str, right: impl FnOnce(&mut egui::Ui)) {
header_paged(ui, name, subtitle, None, right);
}
/// Product header with the MAIN / ADVANCED page switch after the subtitle.
pub fn header_paged(
ui: &mut egui::Ui,
name: &str,
subtitle: &str,
advanced: Option<&mut bool>,
right: impl FnOnce(&mut egui::Ui),
) {
ui.horizontal(|ui| {
let (mark, _) = ui.allocate_exact_size(Vec2::new(4., 26.), Sense::hover());
ui.painter().rect_filled(mark, 1., ACCENT);
@@ -321,6 +331,10 @@ pub fn header(ui: &mut egui::Ui, name: &str, subtitle: &str, right: impl FnOnce(
.color(TEXT),
);
ui.label(RichText::new(subtitle).size(12.).color(MUTED));
if let Some(advanced) = advanced {
ui.add_space(14.);
page_switch(ui, advanced);
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), right);
});
}
@@ -526,3 +540,54 @@ fn pitch_note(ui: &mut egui::Ui, pitch: &mut Pitch, octave: bool) {
});
}
}
/// MAIN / ADVANCED page switch. The main page holds the few controls a first result needs;
/// the advanced page holds everything else. Returns true while the advanced page shows.
pub fn page_switch(ui: &mut egui::Ui, advanced: &mut bool) -> bool {
ui.horizontal(|ui| {
for (page, label, help) in [
(
false,
"MAIN",
"The controls that matter for a first result.",
),
(true, "ADVANCED", "Every control, for fine tuning."),
] {
let r = ui
.selectable_label(*advanced == page, RichText::new(label).size(12.))
.on_hover_text(help);
if r.clicked() {
*advanced = page;
}
}
});
*advanced
}
/// Choice drawn as large chips, centred, for a plugin's mode on its main page.
pub fn choice<T: PartialEq + Copy>(
ui: &mut egui::Ui,
value: &mut T,
options: &[(T, &str, &str)],
) -> Option<egui::Response> {
let mut hovered = None;
let n = options.len() as f32;
let width = n * 96. + (n - 1.) * ui.spacing().item_spacing.x;
ui.horizontal(|ui| {
ui.add_space(((ui.available_width() - width) / 2.).max(0.));
for (v, label, help) in options {
let r = ui
.add_sized(
[96., 30.],
egui::SelectableLabel::new(*value == *v, RichText::new(*label).size(14.)),
)
.on_hover_text(*help);
if r.clicked() {
*value = *v;
}
if r.hovered() {
hovered = Some(r);
}
}
});
hovered
}