Add persistent UI scale parameter (75%..200%)

- Add uiScale AudioParameterChoice to APVTS with 5 options (75%, 100%, 125%, 150%, 200%)
- Connect uiScaleBox ComboBox to parameter via ComboBoxAttachment
- Restore saved scale on editor creation
- Scale persists across plugin close/reopen via APVTS state
This commit is contained in:
Armin 2026-08-31 21:39:12 +02:00
commit fbca80dfe3
3 changed files with 35 additions and 22 deletions

View file

@ -844,16 +844,11 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
setupCB(arpOctavesBox, arpOctavesAttach, "arpOctaves", {"1", "2", "3"});
setupCB(arpRateBox, arpRateAttach, "arpRate", {"1/32", "1/16", "1/8", "1/4", "1/2", "1", "2"});
setupCombo(uiScaleBox);
uiScaleBox.addItem("100%", 1);
uiScaleBox.addItem("125%", 2);
uiScaleBox.addItem("150%", 3);
uiScaleBox.addItem("200%", 4);
uiScaleBox.setSelectedId(1, juce::dontSendNotification);
setupCB(uiScaleBox, uiScaleAttach, "uiScale", {"75%", "100%", "125%", "150%", "200%"});
uiScaleBox.onChange = [this]() {
static const float scales[] = {1.0f, 1.25f, 1.5f, 2.0f};
static const float scales[] = {0.75f, 1.0f, 1.25f, 1.5f, 2.0f};
int idx = uiScaleBox.getSelectedId() - 1;
if (idx >= 0 && idx < 4 && onScaleChanged)
if (idx >= 0 && idx < 5 && onScaleChanged)
onScaleChanged(scales[idx]);
};
}
@ -1179,9 +1174,9 @@ void MainContentComponent::paint(juce::Graphics& g) {
drawSection(10, 88, 498, 200);
drawSection(518, 88, 624, 200);
drawSection(1152, 88, 498, 200);
drawSection(10, 298, 563, 160);
drawSection(583, 298, 502, 160);
drawSection(1095, 298, 555, 160);
drawSection(10, 298, 540, 160);
drawSection(560, 298, 540, 160);
drawSection(1110, 298, 540, 160);
drawSection(10, 892, 1640, 136);
// FX sub-sections
@ -1225,7 +1220,7 @@ void MainContentComponent::paint(juce::Graphics& g) {
void MainContentComponent::resized() {
auto sectionY = 92;
auto knobSize = 112;
auto knobSize = 93;
auto knobSpacing = 10;
auto labelH = 18;
auto comboH = 48;
@ -1248,16 +1243,16 @@ void MainContentComponent::resized() {
phaseOffsetKnob.setBounds(528 + (knobSize + knobSpacing) * 4, knobY2, knobSize, knobSize);
filterLabel.setBounds(1152, sectionY, 498, labelH);
filterTypeBox.setBounds(1430, 88, 220, 28);
filterTypeBox.setBounds(1430, 114, 220, 28);
// FILTER holds 5 knobs on a single row (the per-note VEL RES knob was
// removed); the type dropdown sits in the header row and the knobs span
// the section width at the original large size.
{
int fSize = 89;
int fSize = knobSize;
int fGap = 8;
int rowW = 5 * fSize + 4 * fGap;
int fSx = 1152 + (498 - rowW) / 2;
int fRowY = 124;
int fRowY = 166;
filterCutoffKnob.setBounds(fSx, fRowY, fSize, fSize);
filterResKnob.setBounds(fSx + (fSize + fGap), fRowY, fSize, fSize);
filterEnvAmtKnob.setBounds(fSx + (fSize + fGap) * 2, fRowY, fSize, fSize);
@ -1265,10 +1260,10 @@ void MainContentComponent::resized() {
velToCutKnob.setBounds(fSx + (fSize + fGap) * 4, fRowY, fSize, fSize);
}
envLabel.setBounds(10, 302, 563, labelH);
envLabel.setBounds(10, 302, 540, labelH);
{
int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 10 + (563 - rowW) / 2;
int sx = 10 + (540 - rowW) / 2;
int knobYA = 312 + labelH + 6;
envAttackKnob.setBounds(sx, knobYA, knobSize, knobSize);
envDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYA, knobSize, knobSize);
@ -1276,10 +1271,10 @@ void MainContentComponent::resized() {
envReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYA, knobSize, knobSize);
}
fEnvLabel.setBounds(583, 302, 502, labelH);
fEnvLabel.setBounds(560, 302, 540, labelH);
{
int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 583 + (502 - rowW) / 2;
int sx = 560 + (540 - rowW) / 2;
int knobYFE = 312 + labelH + 6;
fEnvAttackKnob.setBounds(sx, knobYFE, knobSize, knobSize);
fEnvDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYFE, knobSize, knobSize);
@ -1287,10 +1282,10 @@ void MainContentComponent::resized() {
fEnvReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYFE, knobSize, knobSize);
}
globalLabel.setBounds(1095, 302, 555, labelH);
globalLabel.setBounds(1110, 302, 540, labelH);
{
int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 1095 + (555 - rowW) / 2;
int sx = 1110 + (540 - rowW) / 2;
int knobYM = 312 + labelH + 6;
panKnob.setBounds(sx, knobYM, knobSize, knobSize);
driveKnob.setBounds(sx + (knobSize + knobSpacing), knobYM, knobSize, knobSize);
@ -1747,7 +1742,19 @@ ChromaFlockEditor::ChromaFlockEditor(ChromaFlockProcessor& p)
: AudioProcessorEditor(&p), content(p) {
addAndMakeVisible(content);
content.onScaleChanged = [this](float s) { setUIScale(s); };
setSize(baseWidth, baseHeight);
// Restore saved UI scale
auto* scaleParam = p.apvts.getRawParameterValue("uiScale");
float initialScale = 1.0f;
if (scaleParam) {
int idx = static_cast<int>(scaleParam->load());
static const float scales[] = {0.75f, 1.0f, 1.25f, 1.5f, 2.0f};
if (idx >= 0 && idx < 5)
initialScale = scales[idx];
}
currentScale = initialScale;
setSize(static_cast<int>(baseWidth * currentScale), static_cast<int>(baseHeight * currentScale));
setResizable(false, false);
}