mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
196 lines
6.4 KiB
C++
196 lines
6.4 KiB
C++
|
|
#include "PluginEditor.h"
|
||
|
|
#include "BuildInfo.h"
|
||
|
|
#include <juce_gui_basics/juce_gui_basics.h>
|
||
|
|
|
||
|
|
RackEditor::RackEditor (RMX19AudioProcessor& p)
|
||
|
|
: AudioProcessorEditor (p)
|
||
|
|
, proc (p)
|
||
|
|
, display (p)
|
||
|
|
{
|
||
|
|
face = juce::Colour (0xff16130f);
|
||
|
|
|
||
|
|
up = new NavButton (-1, "^", "UP");
|
||
|
|
down = new NavButton (1, "v", "DOWN");
|
||
|
|
left = new NavButton (-1, "<", "LEFT");
|
||
|
|
right = new NavButton (1, ">", "RIGHT");
|
||
|
|
enterB = new OkButton ("ENTER", true);
|
||
|
|
backB = new OkButton ("BACK", false);
|
||
|
|
|
||
|
|
for (auto* b : { (juce::Button*) up, (juce::Button*) down,
|
||
|
|
(juce::Button*) left, (juce::Button*) right,
|
||
|
|
(juce::Button*) enterB, (juce::Button*) backB })
|
||
|
|
addAndMakeVisible (b);
|
||
|
|
|
||
|
|
up->onClick = [this] { display.navUp(); };
|
||
|
|
down->onClick = [this] { display.navDown(); };
|
||
|
|
left->onClick = [this] { display.navLeft(); };
|
||
|
|
right->onClick = [this] { display.navRight(); };
|
||
|
|
enterB->onClick = [this] { display.navEnter(); };
|
||
|
|
backB->onClick = [this] { display.navExit(); };
|
||
|
|
|
||
|
|
addAndMakeVisible (display);
|
||
|
|
|
||
|
|
setWantsKeyboardFocus (true);
|
||
|
|
setSize (656, 248);
|
||
|
|
startTimerHz (18);
|
||
|
|
}
|
||
|
|
|
||
|
|
RackEditor::~RackEditor()
|
||
|
|
{
|
||
|
|
stopTimer();
|
||
|
|
}
|
||
|
|
|
||
|
|
void RackEditor::paint (juce::Graphics& g)
|
||
|
|
{
|
||
|
|
const auto b = getLocalBounds();
|
||
|
|
|
||
|
|
// --- metal rack face ---
|
||
|
|
g.setColour (face);
|
||
|
|
g.fillRect (b);
|
||
|
|
|
||
|
|
// brushed texture
|
||
|
|
g.setColour (juce::Colour::fromRGBA (255, 255, 255, 7));
|
||
|
|
for (int x = 0; x < b.getWidth(); x += 3)
|
||
|
|
g.fillRect (x, 0, 1, b.getHeight());
|
||
|
|
|
||
|
|
// rack rails (top/bottom)
|
||
|
|
g.setColour (juce::Colour (0xff221e1a));
|
||
|
|
g.fillRect (0, 0, b.getWidth(), 4);
|
||
|
|
g.fillRect (0, b.getHeight() - 4, b.getWidth(), 4);
|
||
|
|
g.setColour (juce::Colour (0xff5a5347));
|
||
|
|
g.fillRect (0, 4, b.getWidth(), 1);
|
||
|
|
g.fillRect (0, b.getHeight() - 5, b.getWidth(), 1);
|
||
|
|
|
||
|
|
// screw holes
|
||
|
|
const auto screw = [&] (int x, int y)
|
||
|
|
{
|
||
|
|
g.setColour (juce::Colour (0xff2a2620));
|
||
|
|
g.fillEllipse (x - 4, y - 4, 8, 8);
|
||
|
|
g.setColour (juce::Colour (0xff8a7f6e));
|
||
|
|
g.drawEllipse (x - 4.5f, y - 4.5f, 9, 9, 1.0f);
|
||
|
|
g.drawLine (x - 3, y, x + 3, y, 1.0f);
|
||
|
|
};
|
||
|
|
screw (8, 8); screw (b.getWidth() - 9, 8);
|
||
|
|
screw (8, b.getHeight() - 9); screw (b.getWidth() - 9, b.getHeight() - 9);
|
||
|
|
|
||
|
|
// brand plate
|
||
|
|
g.setColour (juce::Colour (0xff614f3a));
|
||
|
|
g.fillRect (12, 6, 150, 12);
|
||
|
|
g.setColour (juce::Colour (0xffc9a86f));
|
||
|
|
g.setFont (juce::FontOptions ("Arial", 10.0f, juce::Font::bold));
|
||
|
|
g.drawFittedText ("RMX-19 RACK SYNTH", 12, 6, 150, 12, juce::Justification::centred, 1);
|
||
|
|
|
||
|
|
// power LED (right of LCD status)
|
||
|
|
const int ledX = getWidth() - 20, ledY = 18;
|
||
|
|
g.setColour (juce::Colour (0xff3a0d0d));
|
||
|
|
g.fillEllipse (ledX - 3, ledY - 3, 8, 8);
|
||
|
|
g.setColour (juce::Colour::fromFloatRGBA (0.95f, 0.25f + 0.1f * powerPulse, 0.1f, 1.0f));
|
||
|
|
g.fillEllipse (ledX - 2, ledY - 2, 6, 6);
|
||
|
|
g.setColour (juce::Colour (0xff8a7f6e));
|
||
|
|
g.setFont (juce::FontOptions ("Arial", 8.0f, juce::Font::plain));
|
||
|
|
g.drawFittedText ("PWR", ledX - 26, ledY - 5, 20, 10, juce::Justification::centredRight, 1);
|
||
|
|
|
||
|
|
// rear engraving text
|
||
|
|
g.setColour (juce::Colour (0xff6e6657));
|
||
|
|
g.setFont (juce::FontOptions ("Courier New", 8.0f, juce::Font::plain));
|
||
|
|
const juce::String ser = "S/N 0001-1943 MIDI OUT (R) MIDI IN (R) AUDIO 24BIT/96K";
|
||
|
|
g.drawFittedText (ser, 12, getHeight() - 16, getWidth() - 24, 11, juce::Justification::centred, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
void RackEditor::resized()
|
||
|
|
{
|
||
|
|
const auto b = getLocalBounds();
|
||
|
|
|
||
|
|
// LCD recessed module
|
||
|
|
const juce::Rectangle<int> lcdArea (14, 24, 356, 168);
|
||
|
|
display.setBounds (lcdArea);
|
||
|
|
|
||
|
|
// nav diamond + enter/back to the right of the LCD
|
||
|
|
const juce::Rectangle<int> navArea (lcdArea.getRight() + 16, 26, b.getWidth() - lcdArea.getRight() - 30, 164);
|
||
|
|
|
||
|
|
const int bw = 58, bh = 20;
|
||
|
|
const int cx = navArea.getX() + (navArea.getWidth() - bw) / 2;
|
||
|
|
const int rowY = navArea.getY();
|
||
|
|
|
||
|
|
up->setBounds (cx, rowY, bw, bh);
|
||
|
|
left->setBounds (navArea.getX() + 2, rowY + bh + 6, bw, bh);
|
||
|
|
down->setBounds (cx, rowY + bh + 6, bw, bh);
|
||
|
|
right->setBounds (navArea.getRight() - bw - 2, rowY + bh + 6, bw, bh);
|
||
|
|
|
||
|
|
enterB->setBounds (navArea.getX() + 2, rowY + 2 * (bh + 6) + 4, bw, bh);
|
||
|
|
backB->setBounds (navArea.getRight() - bw - 2, rowY + 2 * (bh + 6) + 4, bw, bh);
|
||
|
|
}
|
||
|
|
|
||
|
|
void RackEditor::timerCallback()
|
||
|
|
{
|
||
|
|
powerPulse = 0.5f + 0.5f * std::sin (juce::Time::getMillisecondCounter() * 0.0022f);
|
||
|
|
repaint (getLocalBounds());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool RackEditor::keyPressed (const juce::KeyPress& k)
|
||
|
|
{
|
||
|
|
if (display.keyTrap (k))
|
||
|
|
{
|
||
|
|
repaint();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---- buttons -------------------------------------------------------------
|
||
|
|
|
||
|
|
void RackEditor::NavButton::paintButton (juce::Graphics& g, bool, bool down)
|
||
|
|
{
|
||
|
|
const auto b = getLocalBounds();
|
||
|
|
const juce::Colour base = juce::Colour (0xff221d18);
|
||
|
|
const juce::Colour edge = juce::Colour (0xff6b5f4e);
|
||
|
|
const juce::Colour text = juce::Colour (0xfffff3d8);
|
||
|
|
|
||
|
|
if (down)
|
||
|
|
{
|
||
|
|
g.setColour (edge.darker (0.4f));
|
||
|
|
g.fillRect (b);
|
||
|
|
g.setColour (base.brighter (0.1f));
|
||
|
|
g.fillRect (b.reduced (2, 2));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
g.setColour (base);
|
||
|
|
g.fillRect (b);
|
||
|
|
g.setColour (edge);
|
||
|
|
g.drawRect (b, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
// glyph
|
||
|
|
g.setColour (text);
|
||
|
|
g.setFont (juce::FontOptions ("Arial", 12.0f, juce::Font::bold));
|
||
|
|
g.drawFittedText (mark, b.withTrimmedBottom (9), juce::Justification::centred, 1);
|
||
|
|
g.setFont (juce::FontOptions ("Arial", 7.0f, juce::Font::plain));
|
||
|
|
g.drawFittedText (getName(), b.withTrimmedTop (b.getHeight() - 8), juce::Justification::centred, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
void RackEditor::OkButton::paintButton (juce::Graphics& g, bool over, bool down)
|
||
|
|
{
|
||
|
|
const auto b = getLocalBounds();
|
||
|
|
const juce::Colour base = enter ? juce::Colour (0xff263a28) : juce::Colour (0xff332a20);
|
||
|
|
const juce::Colour edge = juce::Colour (0xff8a9b6a);
|
||
|
|
|
||
|
|
if (down)
|
||
|
|
{
|
||
|
|
g.setColour (edge.darker (0.35f));
|
||
|
|
g.fillRect (b);
|
||
|
|
g.setColour (base.brighter (0.12f));
|
||
|
|
g.fillRect (b.reduced (2, 2));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
g.setColour (base);
|
||
|
|
g.fillRect (b);
|
||
|
|
g.setColour (edge);
|
||
|
|
g.drawRect (b, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
g.setColour (over ? juce::Colour (0xffffd977) : juce::Colour (0xffd8d2c6));
|
||
|
|
g.setFont (juce::FontOptions ("Arial", 9.0f, juce::Font::bold));
|
||
|
|
g.drawFittedText (getName(), b, juce::Justification::centred, 1);
|
||
|
|
}
|