mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
new bg design, add navi joystick
This commit is contained in:
parent
b29e263000
commit
48ac58555f
3 changed files with 203 additions and 80 deletions
|
|
@ -12,24 +12,26 @@ RackEditor::RackEditor (RMX19AudioProcessor& p)
|
|||
backgroundImage = juce::ImageFileFormat::loadFrom (BinaryData::b3_png,
|
||||
BinaryData::b3_pngSize);
|
||||
|
||||
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);
|
||||
// circular joystick ring: angles run clockwise from 12 o'clock
|
||||
up = new DirWedge ("UP", 0.0);
|
||||
right = new DirWedge ("RIGHT", juce::MathConstants<double>::halfPi);
|
||||
down = new DirWedge ("DOWN", juce::MathConstants<double>::pi);
|
||||
left = new DirWedge ("LEFT", -juce::MathConstants<double>::halfPi);
|
||||
|
||||
okB = new CentreOkButton ("OK");
|
||||
escB = new EscButton ("ESC");
|
||||
|
||||
for (auto* b : { (juce::Button*) up, (juce::Button*) down,
|
||||
(juce::Button*) left, (juce::Button*) right,
|
||||
(juce::Button*) enterB, (juce::Button*) backB })
|
||||
(juce::Button*) okB, (juce::Button*) escB })
|
||||
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(); };
|
||||
okB->onClick = [this] { display.navEnter(); };
|
||||
escB->onClick = [this] { display.navExit(); };
|
||||
|
||||
addAndMakeVisible (display);
|
||||
|
||||
|
|
@ -158,20 +160,34 @@ void RackEditor::resized()
|
|||
display.setTransform (tf);
|
||||
display.setBounds (lcdArea);
|
||||
|
||||
// nav diamond + enter/back to the right of the LCD
|
||||
// circular joystick ring + separate ESC key, to the right of the LCD
|
||||
const juce::Rectangle<int> navArea (lcdArea.getRight() + 16, 26, baseW - lcdArea.getRight() - 30, 164);
|
||||
|
||||
const int bw = 58, bh = 20;
|
||||
const int cx = navArea.getX() + (navArea.getWidth() - bw) / 2;
|
||||
const int rowY = navArea.getY();
|
||||
constexpr float ro = DirWedge::outerR;
|
||||
const float ringCx = (float) navArea.getCentreX();
|
||||
const float ringCy = (float) lcdArea.getCentreY(); // ring sits centred on the display
|
||||
|
||||
up->setTransform (tf); up->setBounds (cx + 6, rowY + 1, bw + 3, bh);
|
||||
left->setTransform (tf); left->setBounds (navArea.getX() + 1, rowY + bh + 6, bw + 2, bh);
|
||||
down->setTransform (tf); down->setBounds (cx + 6, rowY + bh + 6 + 1, bw + 3, bh);
|
||||
right->setTransform (tf); right->setBounds (navArea.getRight() - bw + 3, rowY + bh + 6, bw - 3, bh);
|
||||
// all four wedges share the ring's bounding box; each paints only its own sector
|
||||
const juce::Rectangle<int> ringBox ((int) (ringCx - ro), (int) (ringCy - ro),
|
||||
(int) (ro * 2.0f), (int) (ro * 2.0f));
|
||||
|
||||
enterB->setTransform (tf); enterB->setBounds (navArea.getX() + 2, rowY + 2 * (bh + 6) + 4, bw + 2, bh);
|
||||
backB->setTransform (tf); backB->setBounds (navArea.getRight() - bw + 3, rowY + 2 * (bh + 6) + 4, bw - 3, bh);
|
||||
up->setTransform (tf); up->setBounds (ringBox);
|
||||
right->setTransform (tf); right->setBounds (ringBox);
|
||||
down->setTransform (tf); down->setBounds (ringBox);
|
||||
left->setTransform (tf); left->setBounds (ringBox);
|
||||
|
||||
// OK, dead centre of the ring
|
||||
okB->setTransform (tf);
|
||||
okB->setBounds ((int) (ringCx - CentreOkButton::radius), (int) (ringCy - CentreOkButton::radius),
|
||||
(int) (CentreOkButton::radius * 2.0f), (int) (CentreOkButton::radius * 2.0f));
|
||||
|
||||
// ESC, a round key centred on the point the old rectangular key occupied
|
||||
constexpr float escD = EscButton::diameter;
|
||||
const float escCx = (float) navArea.getX() + 54.0f;
|
||||
const float escCy = (float) ringBox.getBottom() - 10.0f;
|
||||
escB->setTransform (tf);
|
||||
escB->setBounds ((int) (escCx - escD * 0.5f), (int) (escCy - escD * 0.5f),
|
||||
(int) escD, (int) escD);
|
||||
}
|
||||
|
||||
void RackEditor::setScale (float s)
|
||||
|
|
@ -201,58 +217,138 @@ bool RackEditor::keyPressed (const juce::KeyPress& k)
|
|||
|
||||
// ---- 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);
|
||||
// shared palette for the black joystick keys
|
||||
static const juce::Colour keyFace (0xff080808); // black key body
|
||||
static const juce::Colour keyDown (0xff1c1c1c); // pressed
|
||||
static const juce::Colour keyRim (0xff4a4741); // keeps black keys legible on dark metal
|
||||
static const juce::Colour keyRimDn (0xff8a8a86);
|
||||
static const juce::Colour legendIdle (0xfff2f2f2); // white arrow legends
|
||||
static const juce::Colour legendHot (0xffffffff);
|
||||
|
||||
if (down)
|
||||
// solid triangular arrow centred on `centre`, pointing along `angleRad`
|
||||
// (radians clockwise from 12 o'clock, screen y pointing down)
|
||||
static void drawArrow (juce::Graphics& g, const juce::Point<float>& centre,
|
||||
double angleRad, float size)
|
||||
{
|
||||
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);
|
||||
const float dx = (float) std::sin (angleRad);
|
||||
const float dy = (float) -std::cos (angleRad);
|
||||
const float px = -dy, py = dx; // perpendicular, for the two rear corners
|
||||
|
||||
const float back = size * 0.80f;
|
||||
const float wide = size * 0.85f;
|
||||
|
||||
juce::Path t;
|
||||
t.startNewSubPath (centre.x + dx * size, centre.y + dy * size);
|
||||
t.lineTo (centre.x - dx * back + px * wide, centre.y - dy * back + py * wide);
|
||||
t.lineTo (centre.x - dx * back - px * wide, centre.y - dy * back - py * wide);
|
||||
t.closeSubPath();
|
||||
g.fillPath (t);
|
||||
}
|
||||
|
||||
// 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::DirWedge::paintButton (juce::Graphics& g, bool over, bool down)
|
||||
{
|
||||
const juce::Point<float> c (outerR, outerR);
|
||||
|
||||
// a pressed key sinks slightly into the ring
|
||||
const float ro = down ? outerR - 1.5f : outerR;
|
||||
const float ri = down ? innerR + 1.5f : innerR;
|
||||
|
||||
const double a0 = angle - halfSpan;
|
||||
const double a1 = angle + halfSpan;
|
||||
|
||||
juce::Path wedge;
|
||||
wedge.startNewSubPath (c.getPointOnCircumference (ro, a0));
|
||||
wedge.addCentredArc (c.x, c.y, ro, ro, 0.0f, a0, a1, false);
|
||||
wedge.lineTo (c.getPointOnCircumference (ri, a1));
|
||||
wedge.addCentredArc (c.x, c.y, ri, ri, 0.0f, a1, a0, false);
|
||||
wedge.closeSubPath();
|
||||
|
||||
g.setColour (down ? keyDown : keyFace);
|
||||
g.fillPath (wedge);
|
||||
g.setColour (down ? keyRimDn : keyRim);
|
||||
g.strokePath (wedge, juce::PathStrokeType (1.0f));
|
||||
|
||||
// white arrow legend, centred across the wedge band and pointing outwards
|
||||
g.setColour (over || down ? legendHot : legendIdle);
|
||||
drawArrow (g, c.getPointOnCircumference ((innerR + outerR) * 0.5f, angle), angle, 11.0f);
|
||||
}
|
||||
|
||||
void RackEditor::OkButton::paintButton (juce::Graphics& g, bool over, bool down)
|
||||
bool RackEditor::DirWedge::hitTest (int x, int y)
|
||||
{
|
||||
const auto b = getLocalBounds();
|
||||
const juce::Colour base = enter ? juce::Colour (0xff263a28) : juce::Colour (0xff332a20);
|
||||
const juce::Colour edge = juce::Colour (0xff8a9b6a);
|
||||
if (! juce::Component::hitTest (x, y))
|
||||
return false;
|
||||
|
||||
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);
|
||||
const float dx = (float) x - outerR;
|
||||
const float dy = (float) y - outerR;
|
||||
const float r = std::sqrt (dx * dx + dy * dy);
|
||||
|
||||
if (r < innerR || r > outerR)
|
||||
return false;
|
||||
|
||||
// same convention as the painted sector: radians clockwise from 12 o'clock
|
||||
double diff = std::atan2 (dx, -dy) - angle;
|
||||
|
||||
for (; diff > juce::MathConstants<double>::pi; diff -= juce::MathConstants<double>::twoPi) {}
|
||||
for (; diff < -juce::MathConstants<double>::pi; diff += juce::MathConstants<double>::twoPi) {}
|
||||
|
||||
return std::abs (diff) <= halfSpan;
|
||||
}
|
||||
|
||||
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);
|
||||
bool RackEditor::CentreOkButton::hitTest (int x, int y)
|
||||
{
|
||||
if (! juce::Component::hitTest (x, y))
|
||||
return false;
|
||||
|
||||
const float dx = (float) x - radius;
|
||||
const float dy = (float) y - radius;
|
||||
|
||||
return dx * dx + dy * dy <= radius * radius;
|
||||
}
|
||||
|
||||
void RackEditor::CentreOkButton::paintButton (juce::Graphics& g, bool over, bool down)
|
||||
{
|
||||
const auto c = getLocalBounds().getCentre().toFloat();
|
||||
const float r = radius - (down ? 1.0f : 0.0f);
|
||||
|
||||
juce::Path disc;
|
||||
disc.addEllipse (c.x - r, c.y - r, r * 2.0f, r * 2.0f);
|
||||
|
||||
g.setColour (down ? keyDown : keyFace);
|
||||
g.fillPath (disc);
|
||||
g.setColour (down ? keyRimDn : keyRim);
|
||||
g.strokePath (disc, juce::PathStrokeType (1.0f));
|
||||
|
||||
g.setColour (over || down ? legendHot : legendIdle);
|
||||
g.setFont (juce::FontOptions ("Arial", 9.5f, juce::Font::bold));
|
||||
g.drawText (getName(), getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
||||
bool RackEditor::EscButton::hitTest (int x, int y)
|
||||
{
|
||||
if (! juce::Component::hitTest (x, y))
|
||||
return false;
|
||||
|
||||
const float r = diameter * 0.5f;
|
||||
const float dx = (float) x - r;
|
||||
const float dy = (float) y - r;
|
||||
|
||||
return dx * dx + dy * dy <= r * r;
|
||||
}
|
||||
|
||||
void RackEditor::EscButton::paintButton (juce::Graphics& g, bool over, bool down)
|
||||
{
|
||||
const auto c = getLocalBounds().getCentre().toFloat();
|
||||
const float r = diameter * 0.5f - (down ? 1.0f : 0.0f);
|
||||
|
||||
juce::Path disc;
|
||||
disc.addEllipse (c.x - r, c.y - r, r * 2.0f, r * 2.0f);
|
||||
|
||||
g.setColour (down ? keyDown : keyFace);
|
||||
g.fillPath (disc);
|
||||
g.setColour (down ? keyRimDn : keyRim);
|
||||
g.strokePath (disc, juce::PathStrokeType (1.0f));
|
||||
|
||||
g.setColour (over || down ? legendHot : legendIdle);
|
||||
g.setFont (juce::FontOptions ("Arial", 7.0f, juce::Font::bold));
|
||||
g.drawText (getName(), getLocalBounds(), juce::Justification::centred, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,30 +18,57 @@ public:
|
|||
private:
|
||||
RMX19AudioProcessor& proc;
|
||||
|
||||
class NavButton : public juce::Button
|
||||
// one of the four wedge-shaped keys of the circular joystick ring. every wedge
|
||||
// component covers the whole ring bounding box and paints only its own sector,
|
||||
// so the ring centre is always at (outerR, outerR) in local coordinates.
|
||||
class DirWedge : public juce::Button
|
||||
{
|
||||
public:
|
||||
NavButton (int d, const juce::String& glyph, const juce::String& label)
|
||||
: juce::Button (label), dir (d), mark (glyph)
|
||||
DirWedge (const juce::String& label, double centreAngleRad)
|
||||
: juce::Button (label), angle (centreAngleRad)
|
||||
{ setWantsKeyboardFocus (true); setRepeatSpeed (320, 80); }
|
||||
void paintButton (juce::Graphics&, bool, bool) override;
|
||||
int dir;
|
||||
juce::String mark;
|
||||
// all four wedges share the ring's bounding box, so the box alone would let the
|
||||
// topmost one swallow every click in the ring. restrict hits to this wedge's sector.
|
||||
bool hitTest (int x, int y) override;
|
||||
|
||||
static constexpr float outerR = 66.0f;
|
||||
static constexpr float innerR = 24.0f;
|
||||
// half of a quadrant, minus a gap so neighbouring wedges read as separate keys
|
||||
static constexpr double halfSpan = 0.7156;
|
||||
|
||||
double angle; // radians clockwise from 12 o'clock
|
||||
};
|
||||
|
||||
class OkButton : public juce::Button
|
||||
// the OK key sitting in the middle of the ring
|
||||
class CentreOkButton : public juce::Button
|
||||
{
|
||||
public:
|
||||
OkButton (const juce::String& label, bool isEnter)
|
||||
: juce::Button (label), enter (isEnter)
|
||||
{ setWantsKeyboardFocus (true); setRepeatSpeed (320, 80); }
|
||||
explicit CentreOkButton (const juce::String& label) : juce::Button (label)
|
||||
{ setWantsKeyboardFocus (true); }
|
||||
void paintButton (juce::Graphics&, bool, bool) override;
|
||||
bool enter;
|
||||
// the key paints as a disc, so hit only the disc rather than its square bounds
|
||||
bool hitTest (int x, int y) override;
|
||||
|
||||
static constexpr float radius = 21.0f;
|
||||
};
|
||||
|
||||
NavButton* up = nullptr; NavButton* down = nullptr;
|
||||
NavButton* left = nullptr; NavButton* right = nullptr;
|
||||
OkButton* enterB = nullptr; OkButton* backB = nullptr;
|
||||
// the separate ESC key, outside the ring at the bottom left
|
||||
class EscButton : public juce::Button
|
||||
{
|
||||
public:
|
||||
explicit EscButton (const juce::String& label) : juce::Button (label)
|
||||
{ setWantsKeyboardFocus (true); }
|
||||
void paintButton (juce::Graphics&, bool, bool) override;
|
||||
bool hitTest (int x, int y) override;
|
||||
|
||||
static constexpr float diameter = 30.0f;
|
||||
};
|
||||
|
||||
DirWedge* up = nullptr; DirWedge* down = nullptr;
|
||||
DirWedge* left = nullptr; DirWedge* right = nullptr;
|
||||
CentreOkButton* okB = nullptr;
|
||||
EscButton* escB = nullptr;
|
||||
|
||||
juce::ComboBox scaleBox;
|
||||
LcdPanel display;
|
||||
|
|
|
|||
BIN
b3.png
BIN
b3.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 2 MiB |
Loading…
Add table
Add a link
Reference in a new issue