12 : deathMetalFont(juce::FontOptions(
13 juce::Typeface::createSystemTypefaceFor(CustomFont::ArtDystopia_ttf, CustomFont::ArtDystopia_ttfSize)))
20 juce::Font getLabelFont(juce::Label &label)
override
23 return deathMetalFont;
26 juce::Font getPopupMenuFont()
override
28 return deathMetalFont;
31 void drawButtonText(juce::Graphics &g, juce::TextButton &button,
bool isMouseOverButton,
bool isButtonDown)
override
33 std::ignore = isMouseOverButton;
34 std::ignore = isButtonDown;
35 auto font = deathMetalFont;
37 g.setColour(button.findColour(juce::TextButton::textColourOnId));
38 g.drawText(button.getButtonText(), button.getLocalBounds(), juce::Justification::centred,
true);
40 button.setColour(juce::TextButton::ColourIds::buttonColourId, juce::Colours::black);
41 button.setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::black);
44 void drawLinearSlider(juce::Graphics &g,
int x,
int y,
int width,
int height,
float sliderPos,
float minSliderPos,
45 float maxSliderPos, juce::Slider::SliderStyle style, juce::Slider &slider)
override
49 g.setColour(slider.findColour(juce::Slider::trackColourId));
50 g.fillRect(slider.isHorizontal() ? juce::Rectangle<float>(
static_cast<float>(x), (
float)y + 0.5f,
51 sliderPos - (
float)x, (
float)height - 1.0f)
52 : juce::Rectangle<float>((
float)x + 0.5f, sliderPos, (
float)width - 1.0f,
53 (
float)y + ((
float)height - sliderPos)));
55 drawLinearSliderOutline(g, x, y, width, height, style, slider);
59 auto isTwoVal = (style == juce::Slider::SliderStyle::TwoValueVertical ||
60 style == juce::Slider::SliderStyle::TwoValueHorizontal);
61 auto isThreeVal = (style == juce::Slider::SliderStyle::ThreeValueVertical ||
62 style == juce::Slider::SliderStyle::ThreeValueHorizontal);
64 auto trackWidth = juce::jmin(6.0f, slider.isHorizontal() ? (
float)height * 0.25f : (
float)width * 0.25f);
66 Point<float> startPoint(slider.isHorizontal() ? (
float)x : (
float)x + (
float)width * 0.5f,
67 slider.isHorizontal() ? (
float)y + (
float)height * 0.5f : (
float)(height + y));
69 Point<float> endPoint(slider.isHorizontal() ? (
float)(width + x) : startPoint.x,
70 slider.isHorizontal() ? startPoint.y : (
float)y);
72 juce::Path backgroundTrack;
73 backgroundTrack.startNewSubPath(startPoint);
74 backgroundTrack.lineTo(endPoint);
75 g.setColour(slider.findColour(juce::Slider::backgroundColourId));
76 g.strokePath(backgroundTrack, {trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded});
78 juce::Path valueTrack;
79 juce::Point<float> minPoint, maxPoint, thumbPoint;
81 if (isTwoVal || isThreeVal)
83 minPoint = {slider.isHorizontal() ? minSliderPos : (float)width * 0.5f,
84 slider.isHorizontal() ? (float)height * 0.5f : minSliderPos};
87 thumbPoint = {slider.isHorizontal() ? sliderPos : (float)width * 0.5f,
88 slider.isHorizontal() ? (float)height * 0.5f : sliderPos};
90 maxPoint = {slider.isHorizontal() ? maxSliderPos : (float)width * 0.5f,
91 slider.isHorizontal() ? (float)height * 0.5f : maxSliderPos};
95 auto kx = slider.isHorizontal() ? sliderPos : ((float)x + (float)width * 0.5f);
96 auto ky = slider.isHorizontal() ? ((float)y + (float)height * 0.5f) : sliderPos;
98 minPoint = startPoint;
102 auto thumbWidth = getSliderThumbRadius(slider) + 4;
104 valueTrack.startNewSubPath(minPoint);
105 valueTrack.lineTo(isThreeVal ? thumbPoint : maxPoint);
106 g.setColour(slider.findColour(juce::Slider::trackColourId));
107 g.strokePath(valueTrack, {trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded});
111 g.setColour(juce::Colours::black);
112 g.fillEllipse(juce::Rectangle<float>(
static_cast<float>(thumbWidth),
static_cast<float>(thumbWidth))
113 .withCentre(isThreeVal ? thumbPoint : maxPoint));
115 g.setColour(juce::Colours::white);
117 juce::Point<float> point1(maxPoint.getX(), maxPoint.getY());
118 point1.setX(point1.getX() + (thumbWidth / 2) * ::sinf(0.f * std::numbers::pi / 180.f));
119 point1.setY(point1.getY() + (thumbWidth / 2) * ::cosf(0.f * std::numbers::pi / 180.f));
121 juce::Point<float> point2(maxPoint.getX(), maxPoint.getY());
122 point2.setX(point2.getX() +
123 (thumbWidth / 2) * ::sinf(144.f *
static_cast<float>(std::numbers::pi) / 180.f));
124 point2.setY(point2.getY() +
125 (thumbWidth / 2) * ::cosf(144.f *
static_cast<float>(std::numbers::pi) / 180.f));
127 juce::Point<float> point3(maxPoint.getX(), maxPoint.getY());
128 point3.setX(point3.getX() +
129 (thumbWidth / 2) * ::sinf(288.f *
static_cast<float>(std::numbers::pi) / 180.f));
130 point3.setY(point3.getY() +
131 (thumbWidth / 2) * ::cosf(288.f *
static_cast<float>(std::numbers::pi) / 180.f));
133 juce::Point<float> point4(maxPoint.getX(), maxPoint.getY());
134 point4.setX(point4.getX() +
135 (thumbWidth / 2) * ::sinf(72.f *
static_cast<float>(std::numbers::pi) / 180.f));
136 point4.setY(point4.getY() +
137 (thumbWidth / 2) * ::cosf(72.f *
static_cast<float>(std::numbers::pi) / 180.f));
139 juce::Point<float> point5(maxPoint.getX(), maxPoint.getY());
140 point5.setX(point5.getX() +
141 (thumbWidth / 2) * ::sinf(216.f *
static_cast<float>(std::numbers::pi) / 180.f));
142 point5.setY(point5.getY() +
143 (thumbWidth / 2) * ::cosf(216.f *
static_cast<float>(std::numbers::pi) / 180.f));
145 juce::Path pentagramPath;
146 pentagramPath.startNewSubPath(point1);
147 pentagramPath.lineTo(point2);
148 pentagramPath.lineTo(point3);
149 pentagramPath.lineTo(point4);
150 pentagramPath.lineTo(point5);
151 pentagramPath.closeSubPath();
152 g.strokePath(pentagramPath, juce::PathStrokeType(1));
154 g.drawEllipse(juce::Rectangle<float>(
static_cast<float>(thumbWidth),
static_cast<float>(thumbWidth))
155 .withCentre(isThreeVal ? thumbPoint : maxPoint)
160 if (isTwoVal || isThreeVal)
162 auto sr = juce::jmin(trackWidth, (slider.isHorizontal() ? (
float)height : (
float)width) * 0.4f);
163 auto pointerColour = slider.findColour(juce::Slider::thumbColourId);
165 if (slider.isHorizontal())
167 drawPointer(g, minSliderPos - sr,
168 juce::jmax(0.0f, (
float)y + (
float)height * 0.5f - trackWidth * 2.0f),
169 trackWidth * 2.0f, pointerColour, 2);
171 drawPointer(g, maxSliderPos - trackWidth,
172 juce::jmin((
float)(y + height) - trackWidth * 2.0f, (
float)y + (
float)height * 0.5f),
173 trackWidth * 2.0f, pointerColour, 4);
177 drawPointer(g, juce::jmax(0.0f, (
float)x + (
float)width * 0.5f - trackWidth * 2.0f),
178 minSliderPos - trackWidth, trackWidth * 2.0f, pointerColour, 1);
180 drawPointer(g, juce::jmin((
float)(x + width) - trackWidth * 2.0f, (
float)x + (
float)width * 0.5f),
181 maxSliderPos - sr, trackWidth * 2.0f, pointerColour, 3);
186 drawLinearSliderOutline(g, x, y, width, height, style, slider);
190 void drawRotarySlider(juce::Graphics &g,
int x,
int y,
int width,
int height,
float sliderPosProportional,
191 float rotaryStartAngle,
float rotaryEndAngle, juce::Slider &slider)
override
193 std::ignore = slider;
194 auto radius = (float)juce::jmin(width / 2, height / 2) - 4.0f;
195 auto centreX = (float)x + (
float)width * 0.5f;
196 auto centreY = (float)y + (
float)height * 0.5f;
197 auto rx = centreX - radius;
198 auto ry = centreY - radius;
199 auto rw = radius * 2.0f;
200 auto angle = rotaryStartAngle + sliderPosProportional * (rotaryEndAngle - rotaryStartAngle);
202 auto lineW = radius * 0.135f;
204 juce::Rectangle<float> bounds(rx, ry, rw, rw);
207 g.setColour(juce::Colours::black);
208 g.fillEllipse(bounds);
211 float adjustedRadius = radius - (lineW / 2.0f);
213 juce::Path backgroundArc;
214 backgroundArc.addCentredArc(centreX, centreY, adjustedRadius, adjustedRadius, 0.0f, rotaryStartAngle,
215 rotaryEndAngle,
true);
217 g.setColour(juce::Colours::grey.darker(1.0));
218 g.strokePath(backgroundArc,
219 juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
223 arcPath.addCentredArc(centreX, centreY, adjustedRadius, adjustedRadius, 0.0f, rotaryStartAngle, angle,
true);
226 juce::Image offscreenImage(juce::Image::ARGB, width, height,
true);
228 juce::Graphics offscreenGraphics(offscreenImage);
231 offscreenGraphics.fillAll(juce::Colours::transparentBlack);
234 offscreenGraphics.setColour(juce::Colours::white);
236 offscreenGraphics.strokePath(
237 arcPath, juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
244 juce::Image unboundImage = offscreenImage.createCopy();
247 juce::Image glowImage(juce::Image::ARGB, width, height,
true);
249 juce::Graphics glowGraphics(glowImage);
250 juce::GlowEffect glowEffect;
251 glowEffect.setGlowProperties(5.0f, juce::Colours::yellow);
252 glowEffect.applyEffect(unboundImage, glowGraphics, 1.0f, 1.0f);
256 g.drawImageAt(glowImage, x, y);
258 g.setColour(juce::Colours::white);
260 juce::Point<float> point1(centreX, centreY);
261 point1.setX(point1.getX() + (radius - lineW * 1.5f) * ::sinf(0.f * std::numbers::pi / 180.f));
262 point1.setY(point1.getY() + (radius - lineW * 1.5f) * ::cosf(0.f * std::numbers::pi / 180.f));
264 juce::Point<float> point2(centreX, centreY);
265 point2.setX(point2.getX() +
266 (radius - lineW * 1.5f) * ::sinf(144.f *
static_cast<float>(std::numbers::pi) / 180.f));
267 point2.setY(point2.getY() +
268 (radius - lineW * 1.5f) * ::cosf(144.f *
static_cast<float>(std::numbers::pi) / 180.f));
270 juce::Point<float> point3(centreX, centreY);
271 point3.setX(point3.getX() +
272 (radius - lineW * 1.5f) * ::sinf(288.f *
static_cast<float>(std::numbers::pi) / 180.f));
273 point3.setY(point3.getY() +
274 (radius - lineW * 1.5f) * ::cosf(288.f *
static_cast<float>(std::numbers::pi) / 180.f));
276 juce::Point<float> point4(centreX, centreY);
277 point4.setX(point4.getX() +
278 (radius - lineW * 1.5f) * ::sinf(72.f *
static_cast<float>(std::numbers::pi) / 180.f));
279 point4.setY(point4.getY() +
280 (radius - lineW * 1.5f) * ::cosf(72.f *
static_cast<float>(std::numbers::pi) / 180.f));
282 juce::Point<float> point5(centreX, centreY);
283 point5.setX(point5.getX() +
284 (radius - lineW * 1.5f) * ::sinf(216.f *
static_cast<float>(std::numbers::pi) / 180.f));
285 point5.setY(point5.getY() +
286 (radius - lineW * 1.5f) * ::cosf(216.f *
static_cast<float>(std::numbers::pi) / 180.f));
288 juce::Path pentagramPath;
289 pentagramPath.startNewSubPath(point1);
290 pentagramPath.lineTo(point2);
291 pentagramPath.lineTo(point3);
292 pentagramPath.lineTo(point4);
293 pentagramPath.lineTo(point5);
294 pentagramPath.closeSubPath();
297 juce::AffineTransform transform =
298 juce::AffineTransform::translation(-centreX, -centreY).rotated(angle).translated(centreX, centreY);
299 pentagramPath.applyTransform(transform);
301 g.strokePath(pentagramPath, juce::PathStrokeType(2));
304 g.drawEllipse(bounds.reduced(lineW * 2.f), 2.f);
307 juce::Path crossPath;
310 crossPath.addRectangle(centreX - lineW / 2, centreY + radius / 6, lineW, -radius);
314 crossPath.addRectangle(centreX - (radius / 3 / 2), centreY - radius / 10, radius / 3, lineW);
318 crossPath.applyTransform(transform);
320 g.setColour(juce::Colours::white);
321 g.fillPath(crossPath);
324 void drawGroupComponentOutline(juce::Graphics &g,
int width,
int height,
const juce::String &text,
325 const juce::Justification &position, juce::GroupComponent &group)
override
327 const float textH = 15.0f;
328 const float indent = 3.0f;
329 const float textEdgeGap = 4.0f;
334 auto y = deathMetalFont.getAscent() - 3.0f;
335 auto w = juce::jmax(0.0f, (
float)width - x * 2.0f);
336 auto h = juce::jmax(0.0f, (
float)height - y - indent);
337 cs = juce::jmin(cs, w * 0.5f, h * 0.5f);
338 auto cs2 = 2.0f * cs;
340 auto textW = text.isEmpty() ? 0 : juce::jlimit(0.0f, juce::jmax(0.0f, w - cs2 - textEdgeGap * 2), [&]() {
342 juce::TextLayout layout;
343 juce::AttributedString attributedText;
344 attributedText.append(text, deathMetalFont, juce::Colours::black);
346 layout.createLayout(attributedText, w - cs2 - textEdgeGap * 2);
347 return layout.getWidth() + textEdgeGap * 2.0f;
349 auto textX = cs + textEdgeGap;
351 if (position.testFlags(juce::Justification::horizontallyCentred))
352 textX = cs + (w - cs2 - textW) * 0.5f;
353 else if (position.testFlags(juce::Justification::right))
354 textX = w - cs - textW - textEdgeGap;
356 p.startNewSubPath(x + textX + textW, y);
357 p.lineTo(x + w - cs, y);
359 p.addArc(x + w - cs2, y, cs2, cs2, 0, juce::MathConstants<float>::halfPi);
360 p.lineTo(x + w, y + h - cs);
362 p.addArc(x + w - cs2, y + h - cs2, cs2, cs2, juce::MathConstants<float>::halfPi,
363 juce::MathConstants<float>::pi);
364 p.lineTo(x + cs, y + h);
366 p.addArc(x, y + h - cs2, cs2, cs2, juce::MathConstants<float>::pi, juce::MathConstants<float>::pi * 1.5f);
369 p.addArc(x, y, cs2, cs2, juce::MathConstants<float>::pi * 1.5f, juce::MathConstants<float>::twoPi);
370 p.lineTo(x + textX, y);
372 auto alpha = group.isEnabled() ? 1.0f : 0.5f;
374 g.setColour(group.findColour(juce::GroupComponent::outlineColourId).withMultipliedAlpha(alpha));
376 g.strokePath(p, juce::PathStrokeType(2.0f));
378 g.setColour(group.findColour(juce::GroupComponent::textColourId).withMultipliedAlpha(alpha));
379 g.setFont(deathMetalFont);
380 g.drawText(text, juce::roundToInt(x + textX), 0, juce::roundToInt(textW), juce::roundToInt(textH),
381 juce::Justification::centred,
true);
384 void drawPopupMenuBackground(juce::Graphics &g,
int width,
int height)
override
386 std::ignore = height;
388 g.fillAll(juce::Colours::black);
392 juce::Font deathMetalFont;
393 juce::ColourGradient mGradient;