/* ============================================================================== LookAndFeel.h Created: 19 Sep 2023 4:48:33pm Author: binya ============================================================================== */ #pragma once #include /** 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 }; static constexpr int loopWidth{ 217 }; 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 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 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 }; }; /** 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& 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 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; template class EnvelopeSliderLookAndFeel; //============================================================================== /** 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; };