2026-07-23 23:40:48 +02:00
|
|
|
/*
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
|
|
LookAndFeel.h
|
|
|
|
|
Created: 19 Sep 2023 4:48:33pm
|
|
|
|
|
Author: binya
|
|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
#include <JuceHeader.h>
|
|
|
|
|
|
|
|
|
|
/** This struct contains the plugin's colors. */
|
|
|
|
|
struct Colors
|
|
|
|
|
{
|
|
|
|
|
/** Background for the editor */
|
|
|
|
|
juce::Colour background;
|
|
|
|
|
|
|
|
|
|
/** Foreground for the controls */
|
|
|
|
|
juce::Colour foreground;
|
|
|
|
|
|
|
|
|
|
/** Dark color */
|
|
|
|
|
juce::Colour dark;
|
|
|
|
|
|
|
|
|
|
/** Highlight color for controls */
|
|
|
|
|
juce::Colour highlight;
|
|
|
|
|
|
|
|
|
|
/** Loop color */
|
|
|
|
|
juce::Colour loop;
|
|
|
|
|
|
|
|
|
|
/** Lighter slate for some buttons */
|
|
|
|
|
juce::Colour slate;
|
|
|
|
|
|
|
|
|
|
/** Darker slate for waveform and some buttons */
|
|
|
|
|
juce::Colour darkerSlate;
|
|
|
|
|
|
|
|
|
|
/** Background neutral color (outside editor) */
|
|
|
|
|
juce::Colour light;
|
|
|
|
|
|
|
|
|
|
/** Prompt background color */
|
|
|
|
|
juce::Colour prompt;
|
|
|
|
|
|
|
|
|
|
static constexpr int backgroundColorId{ -1 };
|
|
|
|
|
static constexpr int painterColorId{ -2 };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const Colors defaultTheme
|
|
|
|
|
{
|
|
|
|
|
.background = juce::Colour{0xFFFDF6C3},
|
|
|
|
|
.foreground = juce::Colour{0xFFEAFFF7},
|
|
|
|
|
.dark = juce::Colour{0xFF171614},
|
|
|
|
|
.highlight = juce::Colour{0xFFFF595E},
|
|
|
|
|
.loop = juce::Colour{0xFFFFDA22},
|
|
|
|
|
.slate = juce::Colour{0xFF6E7894},
|
|
|
|
|
.darkerSlate = juce::Colour{0xFF403D37},
|
|
|
|
|
.light = juce::Colour{0xFFFFFFFF},
|
|
|
|
|
.prompt = juce::Colour{0xFF171614}.withAlpha(0.3f)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const Colors darkTheme
|
|
|
|
|
{
|
|
|
|
|
.background = juce::Colour{0xFF0F0F1A},
|
|
|
|
|
.foreground = juce::Colour{0xFF1A1A30},
|
|
|
|
|
.dark = juce::Colour{0xFF8888AA},
|
|
|
|
|
.highlight = juce::Colour{0xFF3A73E5},
|
|
|
|
|
.loop = juce::Colour{0xFFEE8833},
|
|
|
|
|
.slate = juce::Colour{0xFF2A2A4A},
|
|
|
|
|
.darkerSlate = juce::Colour{0xFF1A192D},
|
|
|
|
|
.light = juce::Colour{0xFFFFFFFF},
|
|
|
|
|
.prompt = juce::Colour{0xFF0F0F1A}.withAlpha(0.8f)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** This struct contains layout constants. */
|
|
|
|
|
struct Layout
|
|
|
|
|
{
|
|
|
|
|
// Toolbar values should all be scaled according to the window width
|
|
|
|
|
static constexpr int figmaWidth{ 1999 };
|
|
|
|
|
|
|
|
|
|
static constexpr int controlsHeight{ 159 };
|
|
|
|
|
static constexpr int controlsPaddingX{ 16 };
|
|
|
|
|
static constexpr int moduleGap{ 36 };
|
|
|
|
|
|
|
|
|
|
static constexpr int moduleLabelHeight{ 42 };
|
|
|
|
|
static constexpr int moduleLabelPadding{ 3 };
|
|
|
|
|
static constexpr int moduleLabelGap{ 3 };
|
|
|
|
|
static constexpr int moduleControlsGap{ 22 };
|
|
|
|
|
|
|
|
|
|
static constexpr int tuningWidth{ 320 }; // As ratios of the window width
|
|
|
|
|
static constexpr int attackWidth{ 204 };
|
|
|
|
|
static constexpr int releaseWidth{ 204 };
|
|
|
|
|
static constexpr int playbackWidth{ 534 };
|
2026-07-24 13:09:52 +02:00
|
|
|
static constexpr int loopWidth{ 310 };
|
2026-07-23 23:40:48 +02:00
|
|
|
static constexpr int masterWidth{ 295 };
|
|
|
|
|
|
|
|
|
|
static constexpr int standardRotarySize{ 87 }; // Not including padding
|
|
|
|
|
static constexpr float rotaryHeightRatio{ 0.965f }; // The height of the rotary slider as a ratio of its width
|
|
|
|
|
static constexpr float rotaryPadding{ 0.09f };
|
|
|
|
|
static constexpr float rotaryTextSize{ 0.40f };
|
|
|
|
|
|
|
|
|
|
static constexpr float boundsWidth{ 0.0045f };
|
|
|
|
|
static constexpr float handleWidth{ boundsWidth / 2.f };
|
|
|
|
|
static constexpr float playheadWidth{ handleWidth * 0.66f };
|
|
|
|
|
|
|
|
|
|
static constexpr juce::Point<int> sampleControlsMargin{ 46, 25 };
|
|
|
|
|
static constexpr int sampleControlsHeight{ 45 };
|
|
|
|
|
static constexpr int fileControlsWidth{ 1179 };
|
|
|
|
|
static constexpr int waveformModeWidth{ 238 };
|
|
|
|
|
static constexpr int playbackControlsWidth{ 137 };
|
|
|
|
|
static constexpr float expandWidth{ 1.185f }; // Ratio of combobox height
|
|
|
|
|
|
|
|
|
|
static constexpr int sampleNavigatorHeight{ 93 };
|
|
|
|
|
static constexpr float navigatorBoundsWidth{ 0.002f };
|
|
|
|
|
static constexpr juce::Point<int> navigatorControlsSize{ 124, 52 };
|
|
|
|
|
|
|
|
|
|
static constexpr int fxChainHeight{ 384 };
|
|
|
|
|
static constexpr float fxChainDivider{ 0.001f };
|
|
|
|
|
static constexpr float fxModuleHeader{ 73.f };
|
|
|
|
|
static constexpr float fxDisplayStrokeWidth{ 0.004f };
|
|
|
|
|
|
|
|
|
|
static constexpr int footerHeight{ 64 };
|
2026-07-24 18:03:15 +02:00
|
|
|
|
|
|
|
|
static constexpr int modulationSectionHeight{ 140 };
|
2026-07-23 23:40:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** This struct contains certain UX constants */
|
|
|
|
|
struct Feel
|
|
|
|
|
{
|
|
|
|
|
static constexpr int MOUSE_SENSITIVITY{ 60 };
|
|
|
|
|
static constexpr int DRAGGABLE_SNAP{ 10 };
|
|
|
|
|
|
|
|
|
|
static constexpr int MINIMUM_BOUNDS_DISTANCE{ 10 }; // This needs to be at some minimum value
|
|
|
|
|
static constexpr int MINIMUM_VIEW{ 6 * MINIMUM_BOUNDS_DISTANCE }; // Minimum view size in samples
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|
/** Some utilities */
|
|
|
|
|
struct ComponentProps
|
|
|
|
|
{
|
|
|
|
|
inline static const juce::String& ROTARY_UNIT{ "props_unit" }; // The rotary unit
|
|
|
|
|
inline static const juce::String& ROTARY_GREATER_UNIT{ "greater_unit" }; // A larger unit (e.g. s instead of ms)
|
|
|
|
|
inline static const juce::String& ROTARY_ICON{ "label_icon" }; // An icon to display in place of the unit
|
|
|
|
|
|
|
|
|
|
inline static const juce::String& ROTARY_PARAMETER_NAME{ "parameter_name" }; // For use in a custom rotaries help text
|
|
|
|
|
|
|
|
|
|
inline static const juce::String& LABEL_ELLIPSES{ "label_ellipses" }; // Use ellipses for the label
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ReferenceCountedPath final : public juce::ReferenceCountedObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit ReferenceCountedPath(juce::Path path) : path(std::move(path)) {}
|
|
|
|
|
|
|
|
|
|
juce::Path path;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const juce::Font& getInriaSans();
|
|
|
|
|
const juce::Font& getInriaSansBold();
|
|
|
|
|
const juce::Font& getInter();
|
|
|
|
|
const juce::Font& getInterBold();
|
|
|
|
|
juce::Path getOutlineFromSVG(const char* data);
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|
class CustomLookAndFeel : public juce::LookAndFeel_V4
|
|
|
|
|
{
|
|
|
|
|
using Colour = juce::Colour;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CustomLookAndFeel();
|
|
|
|
|
void setTheme(const Colors& theme);
|
|
|
|
|
|
|
|
|
|
juce::Slider::SliderLayout getSliderLayout(juce::Slider& slider) override;
|
|
|
|
|
juce::Label* createSliderTextBox(juce::Slider& slider) override;
|
|
|
|
|
void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, juce::Slider& slider) override;
|
|
|
|
|
|
|
|
|
|
void drawLabel(juce::Graphics&, juce::Label&) override;
|
|
|
|
|
|
|
|
|
|
juce::Button* createFilenameComponentBrowseButton(const juce::String& text) override;
|
|
|
|
|
void layoutFilenameComponent(juce::FilenameComponent&, juce::ComboBox* filenameBox, juce::Button* browseButton) override;
|
|
|
|
|
|
|
|
|
|
juce::Font getComboBoxFont(juce::ComboBox&) override;
|
|
|
|
|
juce::Label* createComboBoxTextBox(juce::ComboBox&) override;
|
|
|
|
|
void positionComboBoxText(juce::ComboBox&, juce::Label& label) override;
|
|
|
|
|
void drawComboBox(juce::Graphics&, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox&) override;
|
|
|
|
|
void drawComboBoxTextWhenNothingSelected(juce::Graphics&, juce::ComboBox&, juce::Label&) override;
|
|
|
|
|
juce::PopupMenu::Options getOptionsForComboBoxPopupMenu(juce::ComboBox&, juce::Label&) override;
|
|
|
|
|
|
|
|
|
|
juce::Font getPopupMenuFont() override;
|
|
|
|
|
void drawPopupMenuBackground(juce::Graphics&, int width, int height) override;
|
|
|
|
|
void drawPopupMenuItem(juce::Graphics&, 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) override;
|
|
|
|
|
void drawPopupMenuUpDownArrow(juce::Graphics&, int width, int height, bool isScrollUpArrow) override;
|
|
|
|
|
juce::Path getTickShape(float height) override;
|
|
|
|
|
|
|
|
|
|
void drawTickBox(juce::Graphics&, juce::Component&, float x, float y, float w, float h, bool ticked, bool isEnabled, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override;
|
|
|
|
|
|
|
|
|
|
juce::Font getAlertWindowTitleFont() override { return getInterBold().withHeight(18.f); }
|
|
|
|
|
juce::Font getAlertWindowMessageFont() override { return getInter().withHeight(16.f); }
|
|
|
|
|
juce::Font getAlertWindowFont() override { return getInter().withHeight(14.f); }
|
|
|
|
|
|
|
|
|
|
void drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override;
|
|
|
|
|
|
|
|
|
|
Colors colors;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|
/** Here's a custom look and feel for the envelope sliders. A template was not the only way to do this, but I thought I'd try it. */
|
|
|
|
|
enum class EnvelopeSlider : std::uint8_t
|
|
|
|
|
{
|
|
|
|
|
attack,
|
|
|
|
|
release
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <EnvelopeSlider Direction>
|
|
|
|
|
class EnvelopeSliderLookAndFeel final : public CustomLookAndFeel
|
|
|
|
|
{
|
|
|
|
|
void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float rotaryStartAngle, float rotaryEndAngle, juce::Slider& slider) override;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template class EnvelopeSliderLookAndFeel<EnvelopeSlider::attack>;
|
|
|
|
|
template class EnvelopeSliderLookAndFeel<EnvelopeSlider::release>;
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|
/** Here's a custom look and feel for the volume slider. */
|
|
|
|
|
class VolumeSliderLookAndFeel final : public CustomLookAndFeel
|
|
|
|
|
{
|
|
|
|
|
juce::Slider::SliderLayout getSliderLayout(juce::Slider& slider) override;
|
|
|
|
|
juce::Label* createSliderTextBox(juce::Slider& slider) override;
|
|
|
|
|
void drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, juce::Slider::SliderStyle, juce::Slider&) override;
|
|
|
|
|
};
|