feat: UI overhaul — input filters, tabs, minimap, dialog standardization#207
feat: UI overhaul — input filters, tabs, minimap, dialog standardization#207
Conversation
- Add shared DIALOG_TITLE_SIZE, DIALOG_CONTENT_SPACING, and DIALOG_CONTENT_PADDING constants in dialogs/mod.rs - Move close button to title row (top-right) in all four dialogs - Fix tuner dialog: match title size, spacing, padding with others; use full-screen layout with centered tuner content - Settings: rename cancel button label to "Close" for consistency - Replace hardcoded values with shared constants across all dialogs
- Remove dead dark overlay from settings dialog - Rename SettingsMessage::Cancel to Close to match button label - Extract DIALOG_TITLE_ROW_SPACING constant, use in all four dialogs - Remove vestigial row! wrapper around single button in midi dialog - Restore tuner inner spacing to 10 (intra-component, not inter-section)
- Extract Filter stages from preset chain into dedicated input_filters config with independent HP/LP controls - Add preset migration: auto-converts old format on load - Enforce stage ordering by category (pre → core → post) - Add tab system and stage category grouping to the GUI - Add minimap component for stage chain overview - Add new presets: Clean_Eb, Djent_Eb - Remove old Control, StageList, and Filter stage GUI components - Update engine to support dedicated input filter processing - Add i18n keys for new UI strings
c5f77cc to
659a017
Compare
There was a problem hiding this comment.
Pull request overview
This PR overhauls the GUI and preset format to support categorized (Amp/Effects) stage management with tabs + minimap, replaces in-chain Filter stages with dedicated input HP/LP controls (including preset migration), and adds a new Freeverb-based Reverb stage.
Changes:
- Add
input_filtersto presets, migrate legacy"Filter"stages into the new field, and enforce stage ordering by category on load. - Introduce GUI tabs (Amp/Effects/Cabinet/Input), a signal-chain minimap, and category-aware stage insertion/reordering.
- Add a new Reverb stage (GUI + DSP) and update the audio engine to apply input filters before the amp chain.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/preset/mod.rs | Adds input_filters to Preset and updates constructors/defaults. |
| src/preset/manager.rs | Adds preset migration from legacy Filter stages + enforces stage ordering; adds migration tests. |
| src/i18n/mod.rs | Adds translation keys for reverb params, tabs, and input filter UI labels. |
| src/gui/tabs.rs | Introduces Tab enum for GUI navigation. |
| src/gui/stages/reverb.rs | Adds GUI config/message/view for the Reverb stage. |
| src/gui/stages/mod.rs | Adds StageCategory, categorizes stages, removes Filter stage, registers Reverb stage. |
| src/gui/stages/filter.rs | Removes legacy Filter stage UI/config/message. |
| src/gui/mod.rs | Exposes new tabs module. |
| src/gui/messages/settings.rs | Renames SettingsMessage::Cancel to Close. |
| src/gui/messages/mod.rs | Adds tab + input filter messages; removes Filter message exports. |
| src/gui/handlers/settings.rs | Updates handler to use the renamed SettingsMessage::Close. |
| src/gui/handlers/preset.rs | Persists and restores input_filters during preset save/load. |
| src/gui/components/stage_list.rs | Removes old StageList component (replaced by tabbed stage views). |
| src/gui/components/peak_meter.rs | Splits peak meter into main meter + compact status display; adjusts styling. |
| src/gui/components/mod.rs | Registers new minimap + input filter modules; removes old control/stage_list modules. |
| src/gui/components/minimap.rs | Adds minimap component for chain overview and tab switching. |
| src/gui/components/input_filter_control.rs | Adds InputFilterConfig (serde) used by presets and UI. |
| src/gui/components/dialogs/tuner.rs | Standardizes dialog title row (with close button) and shared spacing/padding constants. |
| src/gui/components/dialogs/settings.rs | Standardizes dialog title row (with close button) and shared spacing/padding constants; removes cancel button. |
| src/gui/components/dialogs/mod.rs | Adds shared dialog layout constants. |
| src/gui/components/dialogs/midi.rs | Standardizes dialog title row and shared spacing/padding constants. |
| src/gui/components/dialogs/hotkey.rs | Standardizes dialog title row and shared spacing/padding constants. |
| src/gui/components/control.rs | Removes old control bar component (replaced by tabbed add-stage bar + header actions). |
| src/gui/app.rs | Implements tabs, input filter UI, minimap footer, category-aware stage add/move/collapse behavior. |
| src/audio/engine.rs | Adds engine-level input HP/LP stages and applies them before the amp chain. |
| src/amp/stages/reverb.rs | Adds new Freeverb-based Reverb DSP stage with tests. |
| src/amp/stages/mod.rs | Exposes new reverb module. |
| presets/Sabbath.json | Migrates legacy Filter stages into input_filters. |
| presets/Petrucci.json | Migrates legacy Filter stages into input_filters. |
| presets/High_Gain.json | Migrates legacy Filter stages into input_filters. |
| presets/Funky_Bass.json | Migrates legacy Filter stages into input_filters. |
| presets/Djent_Eb.json | Adds new preset using new format (with input_filters). |
| presets/Djent.json | Migrates legacy Filter stages into input_filters and updates fields. |
| presets/Crunch.json | Migrates legacy Filter stage into input_filters. |
| presets/Clean_Overdrive.json | Migrates legacy Filter stages into input_filters. |
| presets/Clean_Eb.json | Adds new preset using new format (with input_filters). |
| presets/Clean.json | Migrates legacy Filter stages into input_filters and updates stages/fields. |
| presets/Bitey.json | Migrates legacy Filter stages into input_filters. |
Comments suppressed due to low confidence (1)
src/gui/components/peak_meter.rs:127
view_statushard-codes English labels ("XR" and "CPU") and no longer uses the existing translations (tr!(xruns)/tr!(cpu)) that were previously displayed in this component. If i18n is still expected here, please revert to using translation keys (or add new keys for the abbreviated forms).
row![
text(format!("XR {xrun_count}"))
.size(11)
.style(move |_: &iced::Theme| iced::widget::text::Style {
color: Some(xrun_color),
}),
text(format!("CPU {cpu_load:.0}%"))
.size(11)
.style(move |_: &iced::Theme| iced::widget::text::Style {
color: Some(cpu_color),
}),
]
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Change stage_header/stage_card API to accept explicit can_move_up and can_move_down flags instead of idx/total_stages pair; compute flags per-category in app.rs to fix move buttons for categorized stage tabs - Remove heap allocation in RT audio path: apply input filters in-place via the output buffer instead of allocating a Vec every callback - Move buffer size mismatch check to public process() entry point - Remove unwrap() calls in preset migration; build stages array directly and handle input_filters serialization gracefully
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 43 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Apply input filters in-place via output buffer to avoid allocation | ||
| output[..input.len()].copy_from_slice(input); | ||
| self.apply_input_filters(&mut output[..input.len()]); |
There was a problem hiding this comment.
copy_from_slice requires that input and output do not overlap. Engine::process doesn't document or enforce this, so callers that ever pass the same buffer for in-place processing could trigger undefined behavior. Consider guarding against aliasing (e.g., skip the copy when the slices have the same base pointer) or documenting the non-overlap requirement on the public API.
| let input_filters = InputFilterConfig { | ||
| hp_enabled: hp_cutoff.is_some(), | ||
| hp_cutoff: hp_cutoff.unwrap_or(100.0), | ||
| lp_enabled: lp_cutoff.is_some(), | ||
| lp_cutoff: lp_cutoff.unwrap_or(8000.0), | ||
| }; |
There was a problem hiding this comment.
In migrate_preset, hp_enabled/lp_enabled are derived from whether a cutoff value was parsed (*_cutoff.is_some()). If an old preset contains a Filter stage but is missing cutoff_hz (or it’s non-numeric), the stage will be dropped and the corresponding filter will end up disabled instead of enabled with a default cutoff. Consider tracking presence of Highpass/Lowpass stages separately from cutoff parsing, and defaulting the cutoff when missing.
| }, | ||
| { | ||
| "Delay": { | ||
| "delay_ms": 300.0, | ||
| "feedback": 0.3, | ||
| "mix": 0.26 | ||
| } |
There was a problem hiding this comment.
This preset update adds a new Delay stage to Clean.json, which goes beyond the stated preset-format migration (removing Filter stages + adding input_filters). If this is intentional sound/design change, it should be called out in the PR description; otherwise consider keeping the stage chain unchanged and only applying the mechanical migration.
| "ir_name": "Science Amplification/4x12/G12H-150/SM57 Brighter.wav", | ||
| "ir_gain": 0.17999999 | ||
| } No newline at end of file | ||
| "ir_gain": 0.14999999, | ||
| "pitch_shift_semitones": 0, |
There was a problem hiding this comment.
Djent.json changes ir_gain (0.17999999 -> 0.14999999) as part of the migration. If this gain change is intentional (tone-level adjustment) it should be mentioned in the PR description; otherwise consider preserving the original ir_gain to avoid unexpected loudness changes for existing users.
| text(format!("XR {xrun_count}")) | ||
| .size(11) | ||
| .style(move |_: &iced::Theme| iced::widget::text::Style { | ||
| color: Some(xrun_color), | ||
| }), | ||
| text(format!("{}: {cpu_load:.0}%", tr!(cpu))) | ||
| .size(14) | ||
| .width(Length::Fixed(80.0)) | ||
| text(format!("CPU {cpu_load:.0}%")) | ||
| .size(11) |
There was a problem hiding this comment.
view_status hardcodes the labels "XR" and "CPU", which bypasses the existing translation keys (tr!(xruns), tr!(cpu)) used elsewhere in this component. This makes the status text non-localizable (e.g., ZH_CN). Consider formatting these labels using translated strings (or introduce dedicated short translation keys if you want abbreviations).
…abels - Guard against input/output buffer aliasing in Engine::process by skipping copy_from_slice when pointers match - Track filter stage presence separately from cutoff parsing in migrate_preset so a Highpass/Lowpass stage without cutoff_hz is still enabled (with default cutoff) - Use tr!(xruns) and tr!(cpu) in peak meter status instead of hardcoded English strings
Summary
SettingsMessage::CanceltoClose, remove dead overlay from settingsTest plan
make lintpassesmake testpasses (76 tests)