#ifndef AXIOMDESIGNSYSTEM_H #define AXIOMDESIGNSYSTEM_H #pragma once #include "JuceLibraryCode/BinaryData.h" namespace AXIOM { class DesignSystem : public juce::LookAndFeel_V4 { public: DesignSystem(); ~DesignSystem() override = default; struct Colours { //=====================BASE-COLORS========================// static inline const juce::Colour ACCENTCOLOUR = juce::Colour::fromRGB(86, 254, 255); // #56FEFF static inline const juce::Colour BACKGROUNDCOLOUR = juce::Colour::fromRGB(51, 51, 51); // #333333 static inline const juce::Colour FOREGROUNDCOLOUR = juce::Colour::fromRGB(252, 250, 249); // #FCFAF9 static inline const juce::Colour SURFACECOLOUR = juce::Colour::fromRGB(31, 31, 31); // #1F1F1F static inline const juce::Colour MUTEDTEXTCOLOUR = juce::Colour::fromRGB(207, 207, 207); // #CFCFCF static inline const juce::Colour ACCENTWEAKCOLOUR = juce::Colour::fromRGB(61, 183, 183); // #3DB7B7 //=====================HOVER-COLORS========================// static inline const juce::Colour ACCENTHOVER = juce::Colour::fromRGB(110, 255, 255); // #6EFFFF static inline const juce::Colour BACKGROUNDHOVER = juce::Colour::fromRGB(66, 66, 66); // #424242 static inline const juce::Colour FOREGROUNDHOVER = juce::Colour::fromRGB(255, 255, 255); // #FFFFFF static inline const juce::Colour SURFACEHOVER = juce::Colour::fromRGB(42, 42, 42); // #2A2A2A static inline const juce::Colour MUTEDTEXTHOVER = juce::Colour::fromRGB(230, 230, 230); // #E6E6E6 static inline const juce::Colour ACCENTWEAKHOVER = juce::Colour::fromRGB(82, 210, 210); // #52D2D2 }; struct Typography { inline static juce::Typeface::Ptr orbitronFont = juce::Typeface::createSystemTypefaceFor( BinaryData::OrbitronRegular_ttf, BinaryData::OrbitronRegular_ttfSize); inline static juce::Typeface::Ptr horizonFont = juce::Typeface::createSystemTypefaceFor( BinaryData::horizon_otf, BinaryData::horizon_otfSize); inline static juce::Typeface::Ptr jetBrainsMonoFont = juce::Typeface::createSystemTypefaceFor( BinaryData::JetBrainsMonoRegular_ttf, BinaryData::JetBrainsMonoRegular_ttfSize); inline static const juce::String primaryFamily{"Orbitron Bold"}; inline static const juce::String displayFamily{"Horizon"}; inline static const juce::String monoFamily{"JetBrains Mono"}; struct Scale { float basePx = 14.0f; float ratio = 1.125f; float sizeFor(int step) const noexcept { // step = 0 => basePx, step = 1 => base*ratio, step = -1 => base/ratio, ... return basePx * std::pow(ratio, static_cast(step)); } }; inline static Scale scale{}; enum class Style { Display, H1, H2, H3, Subtitle, Body, Small, Caption, Button, Mono, Overline }; struct TextToken { juce::String family; int stepFromBase; bool bold = false; bool italic = false; bool uppercase = false; float letterSpacing = 0.0f; float lineHeight = 1.25f; bool useMonospace = false; }; static TextToken getToken(Style style) noexcept { switch (style) { case Style::Display: return {displayFamily, 5, true, false, false, 0.0f, 1.15f, false}; case Style::H1: return {primaryFamily, 4, true, false, false, 0.0f, 1.15f, false}; case Style::H2: return {primaryFamily, 3, true, false, false, 0.0f, 1.20f, false}; case Style::H3: return {primaryFamily, 2, false, false, false, 0.0f, 1.20f, false}; case Style::Subtitle: return {primaryFamily, 1, false, false, false, 0.01f, 1.25f, false}; case Style::Body: return {primaryFamily, 0, false, false, false, 0.0f, 1.35f, false}; case Style::Small: return {primaryFamily, -1, false, false, false, 0.0f, 1.35f, false}; case Style::Caption: return {primaryFamily, -2, false, false, false, 0.02f, 1.40f, false}; case Style::Button: return {primaryFamily, 0, true, false, false, 0.02f, 1.10f, false}; case Style::Mono: return {monoFamily, -1, false, false, false, 0.0f, 1.30f, true}; case Style::Overline: return {primaryFamily, -3, true, false, true, 0.06f, 1.20f, false}; } return {primaryFamily, 0, false, false, false, 0.0f, 1.3f, false}; } static juce::Font getFont(Style style, float uiScale = 1.0f) { const auto t = getToken(style); const auto fam = t.family; auto height = scale.sizeFor(t.stepFromBase) * uiScale; juce::Font f{fam, height, juce::Font::plain}; if (fam == displayFamily && horizonFont != nullptr) { f = juce::Font(horizonFont).withHeight(height); } else if (fam == primaryFamily && orbitronFont != nullptr) { f = juce::Font(orbitronFont).withHeight(height); } else if (fam == monoFamily && jetBrainsMonoFont != nullptr) { f = juce::Font(jetBrainsMonoFont).withHeight(height); } else f.setTypefaceName(fam); f.setBold(t.bold); f.setItalic(t.italic); f.setExtraKerningFactor(t.letterSpacing); return f; } static void applyToLabel(juce::Label &label, Style style, float uiScale = 1.0f) { label.setFont(getFont(style, uiScale)); if (getToken(style).uppercase) label.setText(label.getText().toUpperCase(), juce::NotificationType::dontSendNotification); label.setMinimumHorizontalScale(1.0f); } static juce::TextLayout createTextLayout(juce::String text, juce::Rectangle bounds, Style style, juce::Colour colour, float uiScale = 1.0f, juce::Justification just = juce::Justification::topLeft) { const auto token = getToken(style); const auto font = getFont(style, uiScale); juce::AttributedString as; as.setJustification(just); if (token.uppercase) text = text.toUpperCase(); as.append(text, font, colour); juce::TextLayout tl; tl.createLayout(as, bounds.getWidth()); return tl; } }; struct Spacing { enum class DensityMode { Narrow, Compact, Regular, Wide, SuperWide }; static constexpr float getDensityFactor(DensityMode mode) { switch (mode) { case DensityMode::Narrow: return {0.9f}; case DensityMode::Compact: return {0.95f}; case DensityMode::Regular: return {1.0f}; case DensityMode::Wide: return {1.08f}; case DensityMode::SuperWide: return {1.15f}; } return {1.0f}; }; enum class SizeMode { XS, S, M, L, XL }; static constexpr int getSizeUnits(SizeMode size) { switch (size) { case SizeMode::XS: return 1; case SizeMode::S: return 2; case SizeMode::M: return 3; case SizeMode::L: return 4; case SizeMode::XL: return 6; } return 1; }; enum class SnapMode { Nearest, Floor, Ceiling }; struct Scale { float baseUnit = 8.0f; float densityFactor = 1.0f; float uiScale = 1.0f; float uiScaleInfluence = 0.3f; static constexpr float MINCLAMP = 4.0f; static constexpr float MAXCLAMP = 56.0f; SnapMode snapMode = SnapMode::Nearest; float stepUnits = 1.0f; float space(SizeMode size) noexcept { float px; auto units = static_cast(getSizeUnits(size)); px = units * baseUnit; px *= densityFactor; px *= 1.0f + (uiScale - 1.0f) * uiScaleInfluence; px = snapToGrid(px, stepUnits, snapMode); return std::clamp(px, MINCLAMP, MAXCLAMP); } float snapToGrid(float px, float stepUnits, SnapMode mode) noexcept{ float grid = baseUnit * stepUnits; if (grid <= 0.0f) return px; float units = px / grid; float snappedUnits; switch (mode) { case SnapMode::Nearest: snappedUnits = std::round(units); break; case SnapMode::Floor: snappedUnits = std::floor(units + 1e-6f); break; case SnapMode::Ceiling: snappedUnits = std::ceil (units - 1e-6f); break; } return snappedUnits * grid; }; }; inline static Scale scale; static void setDensity(DensityMode mode) noexcept { scale.densityFactor = getDensityFactor(mode); } }; struct Shape { }; struct Shadows { }; struct Opacity { }; struct Motion { }; struct Components { struct ButtonStyles { }; struct SliderStyles { }; struct LabelStyles { }; struct TextInputStyles { }; struct ComboBoxStyles { }; }; struct Assets { }; struct Layout { }; private : }; } #endif //AXIOMDESIGNSYSTEM_H