Add mono toggle, wider scale dropdown, dynamic UI scaling

This commit is contained in:
Armin 2026-09-16 18:38:35 +02:00
commit d15e61b96f
6 changed files with 90 additions and 30 deletions

View file

@ -11,7 +11,6 @@ TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p)
{
setSize(kBaseWidth, kBaseHeight);
setResizable(true, true);
setResizeLimits(kBaseWidth, kBaseHeight, 1200, 1200);
addAndMakeVisible(scope);
@ -47,6 +46,14 @@ TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p)
freezeAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>(
processorRef.apvts, "freeze", freezeButton);
// Mono button
addAndMakeVisible(monoButton);
monoButton.setButtonText("MONO");
monoButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899));
monoButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf));
monoAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>(
processorRef.apvts, "mono", monoButton);
// Scale dropdown
addAndMakeVisible(scaleBox);
scaleBox.addItem("100%", 1);
@ -58,6 +65,10 @@ TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p)
scaleBox.onChange = [this] { scaleChanged(); };
scaleChanged();
// After any scale change, recompute the resize limits.
const int maxDim = static_cast<int>(kBaseWidth * kMaxScale);
setResizeLimits(kBaseWidth, kBaseHeight, maxDim, maxDim);
// Knob labels (separate from knobs to ensure correct z-order)
auto setupLabel = [this](juce::Label& label, const juce::String& text)
{
@ -89,7 +100,8 @@ void TerionProcessorEditor::scaleChanged()
default: currentScale = 1.25f; break;
}
setTransform(juce::AffineTransform::scale(currentScale));
const int maxDim = static_cast<int>(kBaseWidth * kMaxScale);
setResizeLimits(kBaseWidth, kBaseHeight, maxDim, maxDim);
setSize(static_cast<int>(kBaseWidth * currentScale),
static_cast<int>(kBaseHeight * currentScale));
}
@ -98,49 +110,62 @@ void TerionProcessorEditor::paint(juce::Graphics& g)
{
g.fillAll(juce::Colour(0xff0a0a0f));
const float s = currentScale;
const int footerH = static_cast<int>(kFooterHeight * s);
// Footer
auto bounds = getLocalBounds();
auto footer = bounds.removeFromBottom(kFooterHeight);
auto footer = bounds.removeFromBottom(footerH);
g.setColour(juce::Colour(0xff15151e));
g.fillRect(footer);
g.setColour(juce::Colour(0xff555566));
g.setFont(9.0f);
g.setFont(juce::Font(juce::FontOptions(9.0f * s)));
juce::String footerText = "Terion "
+ juce::String(TERION_GIT_HASH)
+ " | " + juce::String(TERION_BUILD_TIME)
+ " | " + juce::String(TERION_GIT_LOCAL_COMMITS) + " local";
g.drawFittedText(footerText, footer.reduced(10, 0),
g.drawFittedText(footerText, footer.reduced(static_cast<int>(10.0f * s), 0),
juce::Justification::centredLeft, 1);
}
void TerionProcessorEditor::resized()
{
auto bounds = getLocalBounds();
auto footer = bounds.removeFromBottom(kFooterHeight);
const float s = currentScale;
const int footerH = static_cast<int>(kFooterHeight * s);
const int knobs = static_cast<int>(kKnobSize * s);
const int margin = static_cast<int>(kKnobMargin * s);
const int scaleW = static_cast<int>(76.0f * s);
const int monoW = static_cast<int>(72.0f * s);
// Footer: scale dropdown on the right
scaleBox.setBounds(footer.getWidth() - 60, 1, 56, kFooterHeight - 2);
auto bounds = getLocalBounds();
auto footer = bounds.removeFromBottom(footerH);
// Footer: scale dropdown + mono toggle on the right
scaleBox.setBounds(footer.getWidth() - scaleW, 1, scaleW, footerH - 2);
monoButton.setBounds(footer.getWidth() - scaleW - monoW, 1, monoW, footerH - 2);
// Scope fills the remaining area
scope.setBounds(bounds);
// Controls layout: top-right corner, horizontal row
const int startX = bounds.getWidth() - (4 * (kKnobSize + kKnobMargin) + kKnobSize + kKnobMargin);
const int startY = 10;
const int startX = bounds.getWidth() - (4 * (knobs + margin) + knobs + margin);
const int startY = static_cast<int>(10.0f * s);
fadeKnob.setBounds(startX, startY, kKnobSize, kKnobSize);
sensitivityKnob.setBounds(startX + (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize);
dotSizeKnob.setBounds(startX + 2 * (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize);
smoothingKnob.setBounds(startX + 3 * (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize);
freezeButton.setBounds(startX + 4 * (kKnobSize + kKnobMargin), startY + 10, kKnobSize, kKnobSize - 20);
fadeKnob.setBounds(startX, startY, knobs, knobs);
sensitivityKnob.setBounds(startX + (knobs + margin), startY, knobs, knobs);
dotSizeKnob.setBounds(startX + 2 * (knobs + margin), startY, knobs, knobs);
smoothingKnob.setBounds(startX + 3 * (knobs + margin), startY, knobs, knobs);
freezeButton.setBounds(startX + 4 * (knobs + margin), startY + static_cast<int>(10.0f * s),
knobs, knobs - static_cast<int>(20.0f * s));
const int labelY = startY + kKnobSize - 6;
fadeLabel.setBounds(fadeKnob.getX(), labelY, kKnobSize, 12);
sensitivityLabel.setBounds(sensitivityKnob.getX(), labelY, kKnobSize, 12);
dotSizeLabel.setBounds(dotSizeKnob.getX(), labelY, kKnobSize, 12);
smoothingLabel.setBounds(smoothingKnob.getX(), labelY, kKnobSize, 12);
const int labelY = startY + knobs - static_cast<int>(6.0f * s);
const int labelH = static_cast<int>(12.0f * s);
fadeLabel.setBounds(fadeKnob.getX(), labelY, knobs, labelH);
sensitivityLabel.setBounds(sensitivityKnob.getX(), labelY, knobs, labelH);
dotSizeLabel.setBounds(dotSizeKnob.getX(), labelY, knobs, labelH);
smoothingLabel.setBounds(smoothingKnob.getX(), labelY, knobs, labelH);
}