Reorder filter dropdown: list LP 48dB below LP 24dB

Keep the parameter choice indices stable (so saved projects, .cfl
files and presets stay correct) and only reorder the displayed menu
via explicit item IDs plus a new IndexedComboBoxAttachment, since the
stock JUCE ComboBoxAttachment hard-maps choice index to menu position.
This commit is contained in:
Armin 2026-08-13 14:55:33 +02:00
commit 45d6082657
2 changed files with 59 additions and 3 deletions

View file

@ -622,8 +622,20 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
setupParam(phaseOffsetKnob, phaseOffsetAttach, "phaseOffset", "PHASE"); setupParam(phaseOffsetKnob, phaseOffsetAttach, "phaseOffset", "PHASE");
setupLabel(filterLabel, "FILTER"); setupLabel(filterLabel, "FILTER");
setupCB(filterTypeBox, filterTypeAttach, "filterType", {"LP 12dB", "LP 24dB", "Band Pass", "High Pass", "Notch", // Display order differs from the parameter's index order: LP 48dB is shown
"Band Pass 24", "High Pass 24", "Notch 24", "LP 48dB"}); // directly under LP 24dB. itemIds are (paramIndex + 1) so the underlying
// choice index (and therefore saved state / presets) stays meaningful.
setupCombo(filterTypeBox);
filterTypeBox.addItem("LP 12dB", 1);
filterTypeBox.addItem("LP 24dB", 2);
filterTypeBox.addItem("LP 48dB", 9);
filterTypeBox.addItem("Band Pass", 3);
filterTypeBox.addItem("High Pass", 4);
filterTypeBox.addItem("Notch", 5);
filterTypeBox.addItem("Band Pass 24", 6);
filterTypeBox.addItem("High Pass 24", 7);
filterTypeBox.addItem("Notch 24", 8);
filterTypeAttach = std::make_unique<IndexedComboBoxAttachment>(processorRef.apvts, "filterType", filterTypeBox);
setupParam(filterCutoffKnob, filterCutoffAttach, "filterCutoff", "CUTOFF"); setupParam(filterCutoffKnob, filterCutoffAttach, "filterCutoff", "CUTOFF");
setupParam(filterResKnob, filterResAttach, "filterRes", "RES"); setupParam(filterResKnob, filterResAttach, "filterRes", "RES");
setupParam(filterEnvAmtKnob, filterEnvAmtAttach, "filterEnvAmt", "ENV AMT"); setupParam(filterEnvAmtKnob, filterEnvAmtAttach, "filterEnvAmt", "ENV AMT");

View file

@ -214,9 +214,53 @@ private:
using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment; using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
// Maps a 0-based parameter choice index to a ComboBox itemId (index + 1)
// so the dropdown can be displayed in an order that differs from the
// parameter's index order (e.g. LP 48dB listed under LP 24dB). This keeps
// stored parameter indices meaningful while only reordering the menu.
class IndexedComboBoxAttachment : public juce::AudioProcessorValueTreeState::Listener,
private juce::ComboBox::Listener {
public:
IndexedComboBoxAttachment (juce::AudioProcessorValueTreeState& stateToUse,
const juce::String& paramID,
juce::ComboBox& comboToUse)
: state (stateToUse), paramId (paramID), combo (comboToUse) {
combo.addListener (this);
state.addParameterListener (paramId, this);
auto* p = state.getParameter (paramId);
combo.setSelectedId (1 + juce::roundToInt (p->getValue() * (combo.getNumItems() - 1)),
juce::dontSendNotification);
}
~IndexedComboBoxAttachment() override {
combo.removeListener (this);
state.removeParameterListener (paramId, this);
}
void parameterChanged (const juce::String& parameterID, float newValue) override {
if (parameterID == paramId)
combo.setSelectedId (1 + juce::roundToInt (newValue * (combo.getNumItems() - 1)),
juce::dontSendNotification);
}
void comboBoxChanged (juce::ComboBox* cb) override {
if (cb != &combo)
return;
auto* p = state.getParameter (paramId);
int index = combo.getSelectedId() - 1;
p->setValueNotifyingHost (static_cast<float> (index) / (combo.getNumItems() - 1));
}
private:
juce::AudioProcessorValueTreeState& state;
juce::String paramId;
juce::ComboBox& combo;
};
std::unique_ptr<SliderAttachment> osc1OctAttach, osc1SemiAttach, osc1FineAttach, osc1LevelAttach; std::unique_ptr<SliderAttachment> osc1OctAttach, osc1SemiAttach, osc1FineAttach, osc1LevelAttach;
std::unique_ptr<SliderAttachment> osc2OctAttach, osc2SemiAttach, osc2FineAttach, osc2LevelAttach, phaseOffsetAttach; std::unique_ptr<SliderAttachment> osc2OctAttach, osc2SemiAttach, osc2FineAttach, osc2LevelAttach, phaseOffsetAttach;
std::unique_ptr<ComboBoxAttachment> osc1WaveAttach, osc2WaveAttach, filterTypeAttach; std::unique_ptr<ComboBoxAttachment> osc1WaveAttach, osc2WaveAttach;
std::unique_ptr<IndexedComboBoxAttachment> filterTypeAttach;
std::unique_ptr<ComboBoxAttachment> octaveTransposeAttach, semitoneTransposeAttach; std::unique_ptr<ComboBoxAttachment> octaveTransposeAttach, semitoneTransposeAttach;
std::unique_ptr<SliderAttachment> filterCutoffAttach, filterResAttach, filterEnvAmtAttach, keyTrackAttach; std::unique_ptr<SliderAttachment> filterCutoffAttach, filterResAttach, filterEnvAmtAttach, keyTrackAttach;
std::unique_ptr<SliderAttachment> envAttackAttach, envDecayAttach, envSustainAttach, envReleaseAttach; std::unique_ptr<SliderAttachment> envAttackAttach, envDecayAttach, envSustainAttach, envReleaseAttach;