mirror of
https://codeberg.org/armin/justasample.git
synced 2026-09-01 12:20:48 +02:00
563 lines
21 KiB
C++
563 lines
21 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
LookAndFeel.cpp
|
|
Created: 19 Sep 2023 4:48:33pm
|
|
Author: binya
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "CustomLookAndFeel.h"
|
|
|
|
#include "Components/Buttons.h"
|
|
#include "Sampler/CustomSamplerVoice.h"
|
|
|
|
CustomLookAndFeel::CustomLookAndFeel()
|
|
{
|
|
setTheme(defaultTheme);
|
|
setUsingNativeAlertWindows(true);
|
|
}
|
|
|
|
void CustomLookAndFeel::setTheme(const Colors& theme)
|
|
{
|
|
colors = theme;
|
|
|
|
setColour(juce::Label::textColourId, colors.dark);
|
|
setColour(juce::Label::textWhenEditingColourId, colors.dark);
|
|
setColour(juce::Label::outlineWhenEditingColourId, juce::Colours::transparentWhite);
|
|
setColour(juce::TextEditor::highlightColourId, colors.slate.withAlpha(0.15f));
|
|
setColour(juce::TextEditor::highlightedTextColourId, colors.dark);
|
|
setColour(juce::CaretComponent::caretColourId, colors.dark);
|
|
setColour(juce::ComboBox::textColourId, colors.dark);
|
|
setColour(juce::Slider::thumbColourId, colors.highlight);
|
|
setColour(juce::ResizableWindow::backgroundColourId, colors.foreground);
|
|
setColour(juce::ListBox::backgroundColourId, colors.foreground.withAlpha(0.f));
|
|
setColour(juce::ListBox::outlineColourId, colors.dark.withAlpha(0.f));
|
|
setColour(juce::ListBox::textColourId, colors.dark);
|
|
setColour(juce::ScrollBar::thumbColourId, colors.dark);
|
|
setColour(Colors::backgroundColorId, colors.background);
|
|
setColour(Colors::painterColorId, colors.dark);
|
|
}
|
|
|
|
juce::Slider::SliderLayout CustomLookAndFeel::getSliderLayout(juce::Slider& slider)
|
|
{
|
|
auto bounds = slider.getLocalBounds().toFloat();
|
|
bounds = bounds.reduced(bounds.getWidth() * Layout::rotaryPadding);
|
|
|
|
float textSize = bounds.getWidth() * Layout::rotaryTextSize;
|
|
|
|
juce::Slider::SliderLayout layout;
|
|
layout.sliderBounds = slider.getLocalBounds();
|
|
if (slider.getTextBoxPosition() != juce::Slider::NoTextBox)
|
|
layout.textBoxBounds = juce::Rectangle(bounds.getX(), (bounds.getHeight() - textSize / 3.5f) / 2.f, bounds.getWidth(), textSize).reduced(bounds.getWidth() * 0.1f, 0.f).getLargestIntegerWithin();
|
|
|
|
return layout;
|
|
}
|
|
|
|
juce::Label* CustomLookAndFeel::createSliderTextBox(juce::Slider& slider)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = slider.getLocalBounds().toFloat();
|
|
bounds = bounds.reduced(bounds.getWidth() * Layout::rotaryPadding);
|
|
|
|
auto label = new CustomLabel();
|
|
label->setColour(TextEditor::highlightColourId, Colours::transparentWhite);
|
|
|
|
label->setJustificationType(Justification::centredBottom);
|
|
label->setFont(getInriaSansBold().withHeight(bounds.getWidth() * Layout::rotaryTextSize));
|
|
label->setHasFocusOutline(false);
|
|
label->setKeyboardType(TextInputTarget::decimalKeyboard);
|
|
label->setEditable(false, true, false);
|
|
label->setEnabledMouseCursor(MouseCursor::IBeamCursor);
|
|
|
|
label->onEditorShow = [label]
|
|
{
|
|
auto editor = label->getCurrentTextEditor();
|
|
editor->setJustification(Justification::centredBottom);
|
|
editor->setIndents(0, 0);
|
|
editor->moveCaretToEnd();
|
|
editor->selectAll();
|
|
|
|
// Some compensation to keep the text in a constant position
|
|
auto editorBounds = editor->getBounds().toFloat();
|
|
float offsetAmount = editorBounds.getHeight() * 0.125f;
|
|
editorBounds.setY(editorBounds.getY() - offsetAmount);
|
|
editorBounds.translate(offsetAmount, 0);
|
|
editorBounds.expand(0, offsetAmount);
|
|
editor->setBounds(editorBounds.toNearestInt());
|
|
};
|
|
|
|
return label;
|
|
}
|
|
|
|
void CustomLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPosProportional, float, float, juce::Slider& slider)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = Rectangle(x, y, width, height).toFloat();
|
|
bounds = bounds.reduced(bounds.getWidth() * Layout::rotaryPadding / (1 + Layout::rotaryPadding));
|
|
bounds.setHeight(bounds.getWidth() * Layout::rotaryHeightRatio);
|
|
|
|
auto radius = bounds.getWidth() / 2.f;
|
|
auto lineWidth = radius * 0.2f;
|
|
|
|
constexpr float rotaryStartAngle = MathConstants<float>::pi * (1 + 5.f / 26.f);
|
|
constexpr float rotaryEndAngle = rotaryStartAngle + MathConstants<float>::pi * (2.f - 5.f / 13.f);
|
|
auto toAngle = rotaryStartAngle + sliderPosProportional * (rotaryEndAngle - rotaryStartAngle);
|
|
|
|
// Draw the background arc
|
|
const float lineOffset = lineWidth * 0.5f;
|
|
Path backgroundArc;
|
|
backgroundArc.addCentredArc(bounds.getCentreX(), bounds.getY() + radius + lineOffset / 2.f, radius - lineOffset, radius - lineOffset / 2.f,
|
|
0.0f, rotaryStartAngle, rotaryEndAngle, true);
|
|
|
|
g.setColour(colors.dark);
|
|
g.strokePath(backgroundArc, PathStrokeType(lineWidth));
|
|
|
|
// Draw the value arc
|
|
float baseAngle = rotaryStartAngle;
|
|
if (approximatelyEqual(slider.getRange().getStart(), -slider.getRange().getEnd()))
|
|
baseAngle = MathConstants<float>::twoPi;
|
|
|
|
Path valueArc;
|
|
valueArc.addCentredArc(bounds.getCentreX(), bounds.getY() + radius + lineOffset / 2.f, radius - lineOffset, radius - lineOffset / 2.f,
|
|
0.0f, baseAngle, toAngle, true);
|
|
|
|
g.setColour(colors.highlight);
|
|
g.strokePath(valueArc, PathStrokeType(lineWidth));
|
|
|
|
// Draw the unit label
|
|
g.setColour(colors.dark);
|
|
|
|
if (slider.getProperties().contains(ComponentProps::ROTARY_ICON))
|
|
{
|
|
auto iconPath = dynamic_cast<ReferenceCountedPath*>(slider.getProperties()[ComponentProps::ROTARY_ICON].getObject())->path;
|
|
g.fillPath(iconPath, iconPath.getTransformToScaleToFit(Rectangle{ bounds.getX() + 0.025f * bounds.getWidth(), bounds.getY() + 0.78f * bounds.getHeight(), bounds.getWidth(), 12.f * bounds.getWidth() / Layout::standardRotarySize}, true));
|
|
}
|
|
else if (slider.getTextBoxPosition() != Slider::NoTextBox)
|
|
{
|
|
var unit = slider.getProperties().getWithDefault(ComponentProps::ROTARY_UNIT, "");
|
|
if (slider.getValue() >= 1000)
|
|
unit = slider.getProperties().getWithDefault(ComponentProps::ROTARY_GREATER_UNIT, unit);
|
|
g.setFont(getInriaSansBold().withHeight(20.f * bounds.getWidth() / Layout::standardRotarySize));
|
|
g.drawText(unit, Rectangle{ bounds.getX(), bounds.getY() + 0.78f * bounds.getHeight(), bounds.getWidth(), g.getCurrentFont().getAscent() }, Justification::centredBottom);
|
|
}
|
|
|
|
// Draw % if there is no textbox
|
|
if (slider.getTextBoxPosition() == Slider::NoTextBox)
|
|
{
|
|
g.setFont(getInriaSansBold().withHeight(0.68f * bounds.getWidth()));
|
|
bounds.removeFromTop(bounds.getHeight() * 0.08f);
|
|
g.drawText("%", bounds, Justification::centred);
|
|
}
|
|
|
|
if (!slider.isEnabled())
|
|
{
|
|
g.setColour(slider.findColour(colors.backgroundColorId, true).withAlpha(0.5f));
|
|
g.fillRect(slider.getLocalBounds());
|
|
}
|
|
}
|
|
|
|
void CustomLookAndFeel::drawLabel(juce::Graphics& g, juce::Label& label)
|
|
{
|
|
using namespace juce;
|
|
|
|
if (!label.isBeingEdited())
|
|
{
|
|
auto textArea = label.getLocalBounds();
|
|
|
|
// Draw the background (if the colors are set)
|
|
float cornerSize = textArea.getHeight() * 0.2f;
|
|
float borderSize = textArea.getHeight() * 0.05f;
|
|
|
|
g.setColour(label.findColour(Label::backgroundColourId));
|
|
g.fillRoundedRectangle(textArea.toFloat().reduced(borderSize * 0.33f), cornerSize);
|
|
|
|
g.setColour(label.findColour(Label::outlineColourId));
|
|
g.drawRoundedRectangle(textArea.toFloat().reduced(borderSize * 0.48f), cornerSize, borderSize);
|
|
|
|
// Draw the text
|
|
const Font font(getLabelFont(label));
|
|
|
|
g.setColour(label.findColour(Label::textColourId).withMultipliedAlpha(label.isEnabled() ? 1.f : 0.5f));
|
|
g.setFont(font);
|
|
g.drawText(label.getText(), textArea, label.getJustificationType(), label.getProperties().contains(ComponentProps::LABEL_ELLIPSES));
|
|
}
|
|
}
|
|
|
|
juce::Button* CustomLookAndFeel::createFilenameComponentBrowseButton(const juce::String& /*text*/)
|
|
{
|
|
return new CustomShapeButton(colors.dark, getOutlineFromSVG(BinaryData::IconAdd_svg));
|
|
}
|
|
|
|
void CustomLookAndFeel::layoutFilenameComponent(juce::FilenameComponent& filenameComp, juce::ComboBox* filenameBox, juce::Button* browseButton)
|
|
{
|
|
if (browseButton == nullptr || filenameBox == nullptr)
|
|
return;
|
|
|
|
auto compBounds = filenameComp.getLocalBounds().toFloat();
|
|
|
|
auto addButtonBounds = compBounds.removeFromRight(filenameComp.getHeight() * 0.945f).reduced(0.158f * compBounds.getHeight(), 0.18f * compBounds.getHeight());
|
|
browseButton->setBounds(addButtonBounds.toNearestInt());
|
|
|
|
filenameBox->setBounds(compBounds.toNearestInt());
|
|
}
|
|
|
|
juce::Font CustomLookAndFeel::getComboBoxFont(juce::ComboBox& comboBox)
|
|
{
|
|
return getInter().withHeight(comboBox.getHeight() * 0.73f);
|
|
}
|
|
|
|
juce::Label* CustomLookAndFeel::createComboBoxTextBox(juce::ComboBox& comboBox)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = comboBox.getLocalBounds().toFloat();
|
|
auto label = new CustomLabel();
|
|
|
|
label->setFont(getInter().withHeight(bounds.getWidth() * Layout::rotaryTextSize));
|
|
label->setJustificationType(Justification::centredLeft);
|
|
label->setHasFocusOutline(false);
|
|
label->setKeyboardType(TextInputTarget::decimalKeyboard);
|
|
label->setEditable(false, true, false);
|
|
label->getProperties().set(ComponentProps::LABEL_ELLIPSES, true);
|
|
label->setEnabledMouseCursor(MouseCursor::IBeamCursor);
|
|
|
|
label->onEditorShow = [label]
|
|
{
|
|
auto editor = label->getCurrentTextEditor();
|
|
editor->setJustification(Justification::centredLeft);
|
|
editor->setIndents(0, 0);
|
|
editor->moveCaretToEnd();
|
|
|
|
// Some compensation to keep the text in a constant position
|
|
auto editorBounds = editor->getBounds().toFloat();
|
|
float offset = editorBounds.getHeight() * 0.125f;
|
|
editorBounds.translate(-1, 0);
|
|
editorBounds.expand(0, offset);
|
|
editor->setBounds(editorBounds.toNearestInt());
|
|
};
|
|
|
|
return label;
|
|
}
|
|
|
|
void CustomLookAndFeel::positionComboBoxText(juce::ComboBox& box, juce::Label& label)
|
|
{
|
|
auto bounds = box.getLocalBounds().toFloat();
|
|
bounds.removeFromRight(Layout::expandWidth * box.getHeight());
|
|
|
|
label.setBounds(bounds.toNearestInt());
|
|
label.setFont(getComboBoxFont(box));
|
|
}
|
|
|
|
void CustomLookAndFeel::drawComboBox(juce::Graphics& g, int /*width*/, int /*height*/, bool isButtonDown, int /*buttonX*/,
|
|
int /*buttonY*/, int /*buttonW*/, int /*buttonH*/, juce::ComboBox& box)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = box.getLocalBounds().toFloat();
|
|
auto expandBounds = bounds.removeFromRight(Layout::expandWidth * bounds.getHeight()).reduced(0.272f * bounds.getHeight(), 0.321f * bounds.getHeight());
|
|
|
|
if (isButtonDown)
|
|
expandBounds.reduce(expandBounds.getHeight() * 0.05f, expandBounds.getHeight() * 0.05f);
|
|
|
|
Path path;
|
|
path.startNewSubPath(expandBounds.getTopLeft());
|
|
path.lineTo(expandBounds.getCentreX(), expandBounds.getBottom());
|
|
path.lineTo(expandBounds.getTopRight());
|
|
|
|
g.setColour(colors.dark);
|
|
g.strokePath(path, PathStrokeType(0.1f * box.getHeight(), PathStrokeType::curved, PathStrokeType::rounded));
|
|
|
|
if (!box.isEnabled())
|
|
{
|
|
g.setColour(box.findColour(colors.backgroundColorId, true).withAlpha(0.5f));
|
|
g.fillRect(box.getLocalBounds());
|
|
}
|
|
}
|
|
|
|
void CustomLookAndFeel::drawComboBoxTextWhenNothingSelected(juce::Graphics& g, juce::ComboBox& box,
|
|
juce::Label& label)
|
|
{
|
|
auto textArea = getLabelBorderSize(label).subtractedFrom(label.getLocalBounds());
|
|
auto font = label.getLookAndFeel().getLabelFont(label);
|
|
|
|
g.setColour(findColour(juce::ComboBox::textColourId));
|
|
g.setFont(font);
|
|
g.drawText(box.getTextWhenNothingSelected(), textArea, label.getJustificationType());
|
|
|
|
if (!box.isEnabled())
|
|
{
|
|
g.setColour(box.findColour(colors.backgroundColorId, true).withAlpha(0.5f));
|
|
g.fillRect(box.getLocalBounds());
|
|
}
|
|
}
|
|
|
|
juce::PopupMenu::Options CustomLookAndFeel::getOptionsForComboBoxPopupMenu(juce::ComboBox& box, juce::Label& label)
|
|
{
|
|
return juce::PopupMenu::Options().withTargetComponent(&box)
|
|
.withPreferredPopupDirection(juce::PopupMenu::Options::PopupDirection::downwards)
|
|
.withItemThatMustBeVisible(box.getSelectedId())
|
|
.withInitiallySelectedItem(box.getSelectedId())
|
|
.withMinimumWidth(box.getWidth())
|
|
.withMaximumNumColumns(1)
|
|
.withStandardItemHeight(label.getHeight());
|
|
}
|
|
|
|
juce::Font CustomLookAndFeel::getPopupMenuFont()
|
|
{
|
|
return getInter().withHeight(17.f);
|
|
}
|
|
|
|
void CustomLookAndFeel::drawPopupMenuBackground(juce::Graphics& graphics, int, int)
|
|
{
|
|
graphics.fillAll(colors.slate);
|
|
}
|
|
|
|
void CustomLookAndFeel::drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area, bool /*isSeparator*/,
|
|
bool isActive, bool isHighlighted, bool isTicked, bool /*hasSubMenu*/, const juce::String& text,
|
|
const juce::String& /*shortcutKeyText*/, const juce::Drawable* /*icon*/, const Colour* /*textColour*/)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = area.toFloat();
|
|
|
|
if (isHighlighted && isActive)
|
|
{
|
|
g.setColour(colors.darkerSlate);
|
|
g.fillRect(bounds);
|
|
}
|
|
|
|
float padding = area.getWidth() * 0.02f;
|
|
bounds.reduce(padding, 0);
|
|
|
|
auto font = getPopupMenuFont();
|
|
auto maxFontHeight = bounds.getHeight() * 0.77f;
|
|
if (font.getHeight() > maxFontHeight)
|
|
font.setHeight(maxFontHeight);
|
|
|
|
g.setColour(colors.light);
|
|
g.setFont(font);
|
|
|
|
auto iconArea = bounds.removeFromLeft(bounds.getHeight()).toFloat().reduced(bounds.getHeight() * 0.29f);
|
|
if (isTicked)
|
|
{
|
|
auto tick = getTickShape(1.0f);
|
|
g.strokePath(tick, PathStrokeType(iconArea.getWidth() * 0.18f, PathStrokeType::curved, PathStrokeType::rounded), tick.getTransformToScaleToFit(iconArea, true));
|
|
}
|
|
|
|
bounds.removeFromRight(3);
|
|
g.drawText(text, bounds, Justification::centredLeft, true);
|
|
}
|
|
|
|
void CustomLookAndFeel::drawPopupMenuUpDownArrow(juce::Graphics& g, int width, int height, bool isScrollUpArrow)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto hw = float(width * 0.5f);
|
|
auto arrowW = float(height * 0.3f);
|
|
auto y1 = float(height) * (isScrollUpArrow ? 0.6f : 0.3f);
|
|
auto y2 = float(height) * (isScrollUpArrow ? 0.3f : 0.6f);
|
|
|
|
Path p;
|
|
p.addTriangle(hw - arrowW, y1,
|
|
hw + arrowW, y1,
|
|
hw, y2);
|
|
|
|
g.setColour(colors.dark);
|
|
g.fillPath(p);
|
|
}
|
|
|
|
juce::Path CustomLookAndFeel::getTickShape(float height)
|
|
{
|
|
juce::Path tick;
|
|
tick.startNewSubPath(height, 0.f);
|
|
tick.lineTo(height * 0.4f, height);
|
|
tick.lineTo(0.f, height * 0.5f);
|
|
return tick;
|
|
}
|
|
|
|
void CustomLookAndFeel::drawTickBox(juce::Graphics& g, juce::Component& component, float /*x*/, float /*y*/, float /*w*/,
|
|
float /*h*/, bool ticked, bool /*isEnabled*/, bool /*shouldDrawButtonAsHighlighted*/, bool /*shouldDrawButtonAsDown*/)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = component.getLocalBounds().toFloat();
|
|
auto size = jmin(bounds.getWidth(), bounds.getHeight());
|
|
bounds.reduce((bounds.getWidth() - size) / 2.f, (bounds.getHeight() - size) / 2.f);
|
|
|
|
float lineThickness = 0.093f * size;
|
|
|
|
if (ticked)
|
|
{
|
|
g.setColour(colors.highlight);
|
|
g.fillRect(bounds.reduced(lineThickness * 0.75f));
|
|
}
|
|
|
|
g.setColour(colors.dark);
|
|
g.drawRoundedRectangle(bounds.reduced(lineThickness * 0.66f), 0.14f * size, lineThickness);
|
|
|
|
if (!component.isEnabled())
|
|
{
|
|
g.setColour(component.findColour(colors.backgroundColorId, true).withAlpha(0.5f));
|
|
g.fillRect(component.getLocalBounds());
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
template <EnvelopeSlider Direction>
|
|
void EnvelopeSliderLookAndFeel<Direction>::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float, float, float, juce::Slider& slider)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = Rectangle(x, y, width, height).toFloat();
|
|
bounds = bounds.reduced(bounds.getWidth() * Layout::rotaryPadding);
|
|
float exp = float(slider.getValue());
|
|
|
|
Path curve;
|
|
for (int i = 0; i < width; i++)
|
|
{
|
|
float xPos = float(i) / width;
|
|
float yPos = CustomSamplerVoice::exponentialCurve(exp, xPos);
|
|
|
|
if constexpr (Direction == EnvelopeSlider::release)
|
|
yPos = 1 - yPos;
|
|
|
|
if (i == 0)
|
|
curve.startNewSubPath(float(i), bounds.getHeight() * (1 - yPos));
|
|
else
|
|
curve.lineTo(float(i), bounds.getHeight() * (1 - yPos));
|
|
}
|
|
|
|
g.setColour(colors.dark.withAlpha(slider.isEnabled() ? 1.f : 0.5f));
|
|
g.strokePath(curve, PathStrokeType(0.08f * width, PathStrokeType::curved, PathStrokeType::rounded), curve.getTransformToScaleToFit(bounds, false));
|
|
|
|
slider.setMouseCursor(slider.isEnabled() ? MouseCursor::UpDownResizeCursor : MouseCursor::NormalCursor);
|
|
}
|
|
|
|
void CustomLookAndFeel::drawCornerResizer(juce::Graphics& g, int w, int h, bool isMouseOver,
|
|
bool isMouseDragging)
|
|
{
|
|
auto lineThickness = juce::jmin(float(w), float(h)) * 0.075f;
|
|
|
|
auto d = 0.6f;
|
|
for (auto i = 0; i < 1 + int(isMouseOver || isMouseDragging); i++)
|
|
{
|
|
g.setColour(colors.slate);
|
|
g.drawLine(float(w) * d, float(h) + 1.0f, float(w) + 1.0f, float(h) * d, lineThickness);
|
|
|
|
g.setColour(colors.darkerSlate);
|
|
g.drawLine(float(w) * d + lineThickness, float(h) + 1.0f, float(w) + 1.0f, float(h) * d + lineThickness, lineThickness);
|
|
|
|
d -= 0.3f;
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
juce::Slider::SliderLayout VolumeSliderLookAndFeel::getSliderLayout(juce::Slider& slider)
|
|
{
|
|
auto borderSize = slider.getWidth() * 0.020f;
|
|
|
|
juce::Slider::SliderLayout layout;
|
|
layout.sliderBounds = slider.getLocalBounds();
|
|
layout.sliderBounds.removeFromLeft(int(std::round(slider.getWidth() * 0.12f)));
|
|
layout.sliderBounds.removeFromRight(int(std::round(borderSize + slider.getWidth() * 0.09f)));
|
|
|
|
layout.textBoxBounds = slider.getLocalBounds().removeFromTop(int(std::round(0.21f * slider.getWidth())));
|
|
layout.textBoxBounds.removeFromLeft(int(std::round(slider.getWidth() * 0.1f)));
|
|
layout.textBoxBounds.removeFromRight(int(std::round(slider.getWidth() * 0.25f)));
|
|
|
|
return layout;
|
|
}
|
|
|
|
juce::Label* VolumeSliderLookAndFeel::createSliderTextBox(juce::Slider& slider)
|
|
{
|
|
auto label = CustomLookAndFeel::createSliderTextBox(slider);
|
|
label->setFont(getInriaSansBold().withHeight(slider.getWidth() * 0.225f));
|
|
label->setJustificationType(juce::Justification::bottomRight);
|
|
auto superOnEditorShow = label->onEditorShow;
|
|
label->onEditorShow = [label, superOnEditorShow]
|
|
{
|
|
superOnEditorShow();
|
|
|
|
auto editor = label->getCurrentTextEditor();
|
|
auto editorBounds = editor->getBounds();
|
|
editorBounds.translate(3, 0);
|
|
editor->setBounds(editorBounds);
|
|
editor->setJustification(juce::Justification::bottomRight);
|
|
};
|
|
return label;
|
|
}
|
|
|
|
void VolumeSliderLookAndFeel::drawLinearSlider(juce::Graphics& g, int /*x*/, int /*y*/, int /*width*/, int /*height*/, float sliderPos, float, float, juce::Slider::SliderStyle, juce::Slider& slider)
|
|
{
|
|
using namespace juce;
|
|
|
|
auto bounds = slider.getBounds().withZeroOrigin().toFloat();
|
|
|
|
auto borderSize = bounds.getWidth() * 0.020f;
|
|
bounds = bounds.reduced(borderSize);
|
|
|
|
// Get thumb dimensions
|
|
auto thumbWidth = bounds.getWidth() * 0.203f;
|
|
auto thumbHeight = thumbWidth * 0.5f;
|
|
auto thumbRound = bounds.getWidth() * 0.032f;
|
|
auto thumb = Rectangle(sliderPos - thumbWidth / 2.f, bounds.getHeight() - thumbHeight, thumbWidth, thumbHeight);
|
|
|
|
// Draw fader shape
|
|
Path sliderShape;
|
|
float bottom = bounds.getHeight() - thumbHeight / 2.f;
|
|
float right = bounds.getRight() - bounds.getWidth() * 0.08f;
|
|
sliderShape.addTriangle(bounds.getX(), bottom, right, bottom, right, bottom - bounds.getWidth() * 0.4f);
|
|
sliderShape = sliderShape.createPathWithRoundedCorners(2 * borderSize);
|
|
|
|
g.setColour(colors.highlight);
|
|
float scale = sliderPos / (right - bounds.getX()) * 0.985f;
|
|
g.fillPath(sliderShape, AffineTransform::scale(scale, scale * 0.98f, bounds.getX(), bottom));
|
|
g.setColour(colors.dark);
|
|
g.strokePath(sliderShape, PathStrokeType(borderSize, PathStrokeType::JointStyle::curved));
|
|
|
|
if (!slider.isEnabled())
|
|
{
|
|
g.setColour(slider.findColour(colors.backgroundColorId, true).withAlpha(0.5f));
|
|
g.fillRect(slider.getLocalBounds());
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
const juce::Font& getInriaSans()
|
|
{
|
|
static juce::Font inriaSans{ juce::FontOptions(juce::Typeface::createSystemTypefaceFor(BinaryData::InriaSansRegular_ttf, BinaryData::InriaSansRegular_ttfSize)) };
|
|
|
|
return inriaSans;
|
|
}
|
|
|
|
const juce::Font& getInriaSansBold()
|
|
{
|
|
static juce::Font inriaSansBold{ juce::FontOptions(juce::Typeface::createSystemTypefaceFor(BinaryData::InriaSansBold_ttf, BinaryData::InriaSansBold_ttfSize)) };
|
|
|
|
return inriaSansBold;
|
|
}
|
|
|
|
const juce::Font& getInter()
|
|
{
|
|
static juce::Font inter{ juce::FontOptions(juce::Typeface::createSystemTypefaceFor(BinaryData::InterRegular_ttf, BinaryData::InterRegular_ttfSize)) };
|
|
|
|
return inter;
|
|
}
|
|
|
|
const juce::Font& getInterBold()
|
|
{
|
|
static juce::Font interBold{ juce::FontOptions(juce::Typeface::createSystemTypefaceFor(BinaryData::InterBold_ttf, BinaryData::InterBold_ttfSize)) };
|
|
|
|
return interBold;
|
|
}
|
|
|
|
juce::Path getOutlineFromSVG(const char* data)
|
|
{
|
|
auto xml = juce::parseXML(data);
|
|
jassert(xml != nullptr);
|
|
return juce::Drawable::createFromSVG(*xml)->getOutlineAsPath();
|
|
}
|