Tighten layout: reduce gaps, shrink header, reposition logo, rename newlogo→logo

This commit is contained in:
Armin 2026-07-17 00:48:49 +02:00
commit 4acca46260
6 changed files with 64 additions and 45 deletions

View file

@ -23,7 +23,8 @@ juce_add_binary_data(ChromaFlockBinaryData
NAMESPACE BinaryData NAMESPACE BinaryData
SOURCES SOURCES
bg.png bg.png
newlogo.png logo.png
metalknob.png
) )
if(APPLE) if(APPLE)

View file

@ -1,6 +1,6 @@
# ChromaFlock # ChromaFlock
![ChromaFlock](newlogo.png) ![ChromaFlock](logo.png)
A subtractive synthesizer plugin built with [JUCE](https://juce.com). macOS (VST3, AU, Standalone) and Linux (VST3, Standalone). A subtractive synthesizer plugin built with [JUCE](https://juce.com). macOS (VST3, AU, Standalone) and Linux (VST3, Standalone).

View file

@ -1,6 +1,11 @@
#include "PluginEditor.h" #include "PluginEditor.h"
#include <BinaryData.h> #include <BinaryData.h>
KnobLookAndFeel::KnobLookAndFeel() {
knobOverlay = juce::ImageCache::getFromMemory(BinaryData::metalknob_png,
BinaryData::metalknob_pngSize);
}
void KnobLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, void KnobLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
float sliderPos, float rotaryStartAngle, float sliderPos, float rotaryStartAngle,
float rotaryEndAngle, juce::Slider& slider) { float rotaryEndAngle, juce::Slider& slider) {
@ -25,23 +30,33 @@ void KnobLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int widt
auto bevelPath = juce::Path(); auto bevelPath = juce::Path();
bevelPath.addEllipse(rx, ry, rw, rw); bevelPath.addEllipse(rx, ry, rw, rw);
// Metal knob overlay (clipped to circle, behind gradient)
if (knobOverlay.isValid()) {
juce::Graphics::ScopedSaveState state(g);
juce::Path circlePath;
circlePath.addEllipse(rx, ry, rw, rw);
g.reduceClipRegion(circlePath);
g.setOpacity(0.55f);
g.drawImageWithin(knobOverlay, (int)rx, (int)ry, (int)rw, (int)rw, juce::RectanglePlacement::stretchToFit);
g.setOpacity(1.0f);
}
// Knob body gradient — light top-left to dark bottom-right for 3D bulge // Knob body gradient — light top-left to dark bottom-right for 3D bulge
juce::ColourGradient bodyGrad(juce::Colour(0xb3404040), rx, ry, juce::ColourGradient bodyGrad(juce::Colour(0xa3191919), rx, ry,
juce::Colour(0xb31a1a1a), rx + rw, ry + rw, true); juce::Colour(0xd31f1f1f), rx + rw, ry + rw, true);
g.setGradientFill(bodyGrad); g.setGradientFill(bodyGrad);
g.fillEllipse(rx, ry, rw, rw); g.fillEllipse(rx, ry, rw, rw);
// Soft inner shadow ring (bottom-right dark edge, subtle) // Soft inner shadow ring (bottom-right dark edge, subtle)
g.setColour(juce::Colour(0x18000000)); // g.setColour(juce::Colour(0x18000000));
auto shadowArc = juce::Path(); // auto shadowArc = juce::Path();
float innerR = radius - 2.0f; // float innerR = radius - 2.0f;
shadowArc.addCentredArc(centreX, centreY, innerR, innerR, 0.0f, // shadowArc.addCentredArc(centreX, centreY, innerR, innerR, 0.0f, 0.5f, 2.7f, true);
0.5f, 2.7f, true); // g.strokePath(shadowArc, juce::PathStrokeType(1.5f));
g.strokePath(shadowArc, juce::PathStrokeType(1.5f));
// Outer rim // Outer rim
g.setColour(juce::Colour(0xff555555)); // g.setColour(juce::Colour(0xff555555));
g.drawEllipse(rx, ry, rw, rw, 1.5f); // g.drawEllipse(rx, ry, rw, rw, 1.5f);
// Pointer line with glow — green (left) → yellow (mid) → red (right) // Pointer line with glow — green (left) → yellow (mid) → red (right)
auto indicatorColour = juce::Colour::fromHSV(0.33f * (1.0f - sliderPos), 0.85f, 0.9f, 1.0f); auto indicatorColour = juce::Colour::fromHSV(0.33f * (1.0f - sliderPos), 0.85f, 0.9f, 1.0f);
@ -931,7 +946,7 @@ void MainContentComponent::paint(juce::Graphics& g) {
// Load logo // Load logo
if (!logoLoaded) { if (!logoLoaded) {
auto img = juce::ImageFileFormat::loadFrom( auto img = juce::ImageFileFormat::loadFrom(
BinaryData::newlogo_png, BinaryData::newlogo_pngSize); BinaryData::logo_png, BinaryData::logo_pngSize);
if (img.isValid()) { if (img.isValid()) {
juce::Image::BitmapData bd(img, juce::Image::BitmapData::readOnly); juce::Image::BitmapData bd(img, juce::Image::BitmapData::readOnly);
int minX = bd.width, maxX = 0, minY = bd.height, maxY = 0; int minX = bd.width, maxX = 0, minY = bd.height, maxY = 0;
@ -954,15 +969,15 @@ void MainContentComponent::paint(juce::Graphics& g) {
logoLoaded = true; logoLoaded = true;
} }
if (logoImage.isValid()) { if (logoImage.isValid()) {
int logoW = 440; int logoW = 352;
int logoH = logoImage.getWidth() > 0 int logoH = logoImage.getWidth() > 0
? static_cast<int>(logoW * static_cast<double>(logoImage.getHeight()) / logoImage.getWidth()) ? static_cast<int>(logoW * static_cast<double>(logoImage.getHeight()) / logoImage.getWidth())
: logoW; : logoW;
int logoX = (getWidth() - logoW) / 2; int logoX = (getWidth() - logoW) / 2;
int logoY = (124 - logoH) / 2 + 32; int logoY = (80 - logoH) / 2 - 2;
auto logoArea = juce::Rectangle<int>(logoX, logoY, logoW, logoH); auto logoArea = juce::Rectangle<int>(logoX, logoY, logoW, logoH);
juce::Graphics::ScopedSaveState saved(g); juce::Graphics::ScopedSaveState saved(g);
g.setOpacity(0.8f); g.setOpacity(1.0f);
g.drawImage(logoImage, logoArea.toFloat(), juce::RectanglePlacement::centred); g.drawImage(logoImage, logoArea.toFloat(), juce::RectanglePlacement::centred);
} }
@ -983,13 +998,13 @@ void MainContentComponent::paint(juce::Graphics& g) {
g.drawRoundedRectangle(rect, 9.0f, 1.0f); g.drawRoundedRectangle(rect, 9.0f, 1.0f);
}; };
drawSection(10, 128, 498, 200); drawSection(10, 80, 498, 200);
drawSection(518, 128, 624, 200); drawSection(518, 80, 624, 200);
drawSection(1152, 128, 498, 200); drawSection(1152, 80, 498, 200);
drawSection(10, 338, 563, 160); drawSection(10, 290, 563, 160);
drawSection(583, 338, 502, 160); drawSection(583, 290, 502, 160);
drawSection(1095, 338, 555, 160); drawSection(1095, 290, 555, 160);
drawSection(10, 956, 1640, 136); drawSection(10, 884, 1640, 136);
// FX sub-sections // FX sub-sections
auto drawSubSection = [&](int x, int y, int w, int h, const juce::String& title, int titlePadY = 2) { auto drawSubSection = [&](int x, int y, int w, int h, const juce::String& title, int titlePadY = 2) {
@ -1009,22 +1024,22 @@ void MainContentComponent::paint(juce::Graphics& g) {
g.drawText(title, x + 6, y + titlePadY, w - 12, 14, juce::Justification::centred); g.drawText(title, x + 6, y + titlePadY, w - 12, 14, juce::Justification::centred);
}; };
drawSubSection(10, 508, 510, 140, "DISTORTION", 6); drawSubSection(10, 460, 510, 115, "DISTORTION", 6);
drawSubSection(528, 508, 468, 140, "COMPRESSION", 6); drawSubSection(528, 460, 468, 115, "COMPRESSION", 6);
drawSubSection(1004, 508, 268, 140, "PANNING", 6); drawSubSection(1004, 460, 268, 115, "PANNING", 6);
drawSubSection(1280, 508, 370, 140, "LIMITER", 6); drawSubSection(1280, 460, 370, 115, "LIMITER", 6);
// LFO sub-sections // LFO sub-sections
drawSubSection(10, 658, 540, 150, "LFO 1", 4); drawSubSection(10, 586, 540, 150, "LFO 1", 4);
drawSubSection(560, 658, 530, 150, "LFO 2", 4); drawSubSection(560, 586, 530, 150, "LFO 2", 4);
// FX2 sub-sections // FX2 sub-sections
drawSubSection(1015, 658, 375, 150, "DELAY", 4); drawSubSection(1015, 586, 375, 150, "DELAY", 4);
drawSubSection(1395, 658, 255, 150, "REVERB", 4); drawSubSection(1395, 586, 255, 150, "REVERB", 4);
} }
void MainContentComponent::resized() { void MainContentComponent::resized() {
auto sectionY = 132; auto sectionY = 84;
auto knobSize = 112; auto knobSize = 112;
auto knobSpacing = 10; auto knobSpacing = 10;
auto labelH = 18; auto labelH = 18;
@ -1055,33 +1070,33 @@ void MainContentComponent::resized() {
filterEnvAmtKnob.setBounds(1162 + (knobSize + knobSpacing) * 2, knobYF, knobSize, knobSize); filterEnvAmtKnob.setBounds(1162 + (knobSize + knobSpacing) * 2, knobYF, knobSize, knobSize);
keyTrackKnob.setBounds(1162 + (knobSize + knobSpacing) * 3, knobYF, knobSize, knobSize); keyTrackKnob.setBounds(1162 + (knobSize + knobSpacing) * 3, knobYF, knobSize, knobSize);
envLabel.setBounds(10, 342, 563, labelH); envLabel.setBounds(10, 294, 563, labelH);
{ {
int rowW = 4 * knobSize + 3 * knobSpacing; int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 10 + (563 - rowW) / 2; int sx = 10 + (563 - rowW) / 2;
int knobYA = 352 + labelH + 6; int knobYA = 304 + labelH + 6;
envAttackKnob.setBounds(sx, knobYA, knobSize, knobSize); envAttackKnob.setBounds(sx, knobYA, knobSize, knobSize);
envDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYA, knobSize, knobSize); envDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYA, knobSize, knobSize);
envSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYA, knobSize, knobSize); envSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYA, knobSize, knobSize);
envReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYA, knobSize, knobSize); envReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYA, knobSize, knobSize);
} }
fEnvLabel.setBounds(583, 342, 502, labelH); fEnvLabel.setBounds(583, 294, 502, labelH);
{ {
int rowW = 4 * knobSize + 3 * knobSpacing; int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 583 + (502 - rowW) / 2; int sx = 583 + (502 - rowW) / 2;
int knobYFE = 352 + labelH + 6; int knobYFE = 304 + labelH + 6;
fEnvAttackKnob.setBounds(sx, knobYFE, knobSize, knobSize); fEnvAttackKnob.setBounds(sx, knobYFE, knobSize, knobSize);
fEnvDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYFE, knobSize, knobSize); fEnvDecayKnob.setBounds(sx + (knobSize + knobSpacing), knobYFE, knobSize, knobSize);
fEnvSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYFE, knobSize, knobSize); fEnvSustainKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYFE, knobSize, knobSize);
fEnvReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYFE, knobSize, knobSize); fEnvReleaseKnob.setBounds(sx + (knobSize + knobSpacing) * 3, knobYFE, knobSize, knobSize);
} }
globalLabel.setBounds(1095, 342, 555, labelH); globalLabel.setBounds(1095, 294, 555, labelH);
{ {
int rowW = 4 * knobSize + 3 * knobSpacing; int rowW = 4 * knobSize + 3 * knobSpacing;
int sx = 1095 + (555 - rowW) / 2; int sx = 1095 + (555 - rowW) / 2;
int knobYM = 352 + labelH + 6; int knobYM = 304 + labelH + 6;
panKnob.setBounds(sx, knobYM, knobSize, knobSize); panKnob.setBounds(sx, knobYM, knobSize, knobSize);
driveKnob.setBounds(sx + (knobSize + knobSpacing), knobYM, knobSize, knobSize); driveKnob.setBounds(sx + (knobSize + knobSpacing), knobYM, knobSize, knobSize);
masterKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYM, knobSize, knobSize); masterKnob.setBounds(sx + (knobSize + knobSpacing) * 2, knobYM, knobSize, knobSize);
@ -1089,7 +1104,7 @@ void MainContentComponent::resized() {
} }
// --- FX SECTION --- // --- FX SECTION ---
int fxY = 480; int fxY = 432;
int fxKnobSize = 80; int fxKnobSize = 80;
int fxLabelH = 16; int fxLabelH = 16;
int fxKnobY = fxY + 54; // sub-section y(476) + titlePad(6) + titleH(14) + bottomPad(6) + 4 int fxKnobY = fxY + 54; // sub-section y(476) + titlePad(6) + titleH(14) + bottomPad(6) + 4
@ -1138,9 +1153,9 @@ void MainContentComponent::resized() {
} }
// --- LFO SECTION --- // --- LFO SECTION ---
int lfoY = 638; int lfoY = 566;
int lfoKnobSize = 80; int lfoKnobSize = 80;
int lfoKnobY = 658 + 4 + 14 + 6; // subSectionY + titlePad + titleH + bottomPad int lfoKnobY = 586 + 4 + 14 + 6; // subSectionY + titlePad + titleH + bottomPad
lfoLabel.setBounds(10, lfoY, 1080, fxLabelH); lfoLabel.setBounds(10, lfoY, 1080, fxLabelH);
fx2Label.setBounds(1015, lfoY, 635, fxLabelH); fx2Label.setBounds(1015, lfoY, 635, fxLabelH);
@ -1180,7 +1195,7 @@ void MainContentComponent::resized() {
} }
// --- PRESET + SCALE (vertically centered within top section, y: 0..128) --- // --- PRESET + SCALE (vertically centered within top section, y: 0..128) ---
int headerH = 128; int headerH = 80;
int btnH = 28, btnGap = 8; int btnH = 28, btnGap = 8;
int presetGroupH = btnH * 2 + btnGap; int presetGroupH = btnH * 2 + btnGap;
int presetStartY = (headerH - presetGroupH) / 2; int presetStartY = (headerH - presetGroupH) / 2;
@ -1209,14 +1224,14 @@ void MainContentComponent::resized() {
uiScaleBox.setBounds(1560, comboY2, 80, comboH2); uiScaleBox.setBounds(1560, comboY2, 80, comboH2);
// Visualizer area // Visualizer area
int vizY = 818; int vizY = 746;
int vizH = 128; int vizH = 128;
vuMeter.setBounds(10, vizY, 40, vizH); vuMeter.setBounds(10, vizY, 40, vizH);
waveformDisplay.setBounds(60, vizY, 790, vizH); waveformDisplay.setBounds(60, vizY, 790, vizH);
spectrumAnalyzer.setBounds(860, vizY, 790, vizH); spectrumAnalyzer.setBounds(860, vizY, 790, vizH);
// Piano roll // Piano roll
pianoRoll.setBounds(10, 956, 1640, 136); pianoRoll.setBounds(10, 884, 1640, 136);
} }
// ===== PianoRollComponent ===== // ===== PianoRollComponent =====

View file

@ -7,9 +7,12 @@
class KnobLookAndFeel : public juce::LookAndFeel_V4 { class KnobLookAndFeel : public juce::LookAndFeel_V4 {
public: public:
KnobLookAndFeel();
void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
float sliderPos, float rotaryStartAngle, float sliderPos, float rotaryStartAngle,
float rotaryEndAngle, juce::Slider& slider) override; float rotaryEndAngle, juce::Slider& slider) override;
private:
juce::Image knobOverlay;
}; };
class ComboBoxLookAndFeel : public juce::LookAndFeel_V4 { class ComboBoxLookAndFeel : public juce::LookAndFeel_V4 {
@ -249,7 +252,7 @@ private:
MainContentComponent content; MainContentComponent content;
float currentScale = 1.0f; float currentScale = 1.0f;
static constexpr int baseWidth = 1660; static constexpr int baseWidth = 1660;
static constexpr int baseHeight = 1100; static constexpr int baseHeight = 1028;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockEditor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockEditor)
}; };

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 784 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB