main_crystalizereq/CrystalizerEQ/AXIOMDesignSystem.h
Legaeli 54c64845d2 Completed Spacing, Shape and Opacity struct.
TODO: Complete further structs.
2025-10-27 19:01:17 +01:00

463 lines
18 KiB
C++

#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 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
};
enum class uiScaleMode {
XS, S, M, L, XL
};
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<float>(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);
}
static void setUiScale(uiScaleMode mode) noexcept {
switch (mode) {
case uiScaleMode::XS: scale.uiScale = 0.5f;
break;
case uiScaleMode::S: scale.uiScale = 0.75f;
break;
case uiScaleMode::M: scale.uiScale = 1.0f;
break;
case uiScaleMode::L: scale.uiScale = 1.5f;
break;
case uiScaleMode::XL: scale.uiScale = 1.75f;
break;
}
scale.uiScale = juce::jlimit(0.6f, 3.0f, scale.uiScale);
}
};
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<float>(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 = Spacing::scale.uiScale) {
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 = Spacing::scale.uiScale) {
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<float> bounds,
Style style,
juce::Colour colour,
float uiScale = Spacing::scale.uiScale,
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 Shape {
enum class RadiusMode {
XS,
S,
M,
L,
XL
};
enum class StrokeMode {
Hairline,
Thin,
Regular,
Bold
};
enum class CornerStyle {
Rounded,
Cut,
Squircle
};
struct Scale {
float baseRadius = 8.0f;
float uiScale = Spacing::scale.uiScale;
float uiScaleInfluenceRadius = 0.5f;
float uiScaleInfluenceStroke = 0.3f;
static constexpr float MINRADIUS = 2.0f;
static constexpr float MAXRADIUS = 28.0f;
static constexpr float MINSTROKE = 1.0f;
static constexpr float MAXSTROKE = 3.0f;
};
inline static Scale scale{};
static constexpr int getRadiusUnits(RadiusMode mode) {
switch (mode) {
case RadiusMode::XS: return 1;
case RadiusMode::S: return 2;
case RadiusMode::M: return 3;
case RadiusMode::L: return 4;
case RadiusMode::XL: return 6;
}
return 1;
};
static float getRadius(RadiusMode mode, juce::Rectangle<float> bounds) {
int units = getRadiusUnits(mode);
float maxRadiusAllowed = std::min(bounds.getHeight(), bounds.getWidth()) * 0.5f;
float radius = units * scale.baseRadius;
radius *= (1.0f + (Spacing::scale.uiScale - 1.0f) * scale.uiScaleInfluenceRadius);
radius = std::round(radius * 2.0f) / 2.0f;
radius = juce::jlimit(scale.MINRADIUS, std::min(scale.MAXRADIUS, maxRadiusAllowed), radius);
return radius;
};
static constexpr float getStrokeUnits(StrokeMode mode) {
switch (mode) {
case StrokeMode::Hairline: return 0.5f;
case StrokeMode::Thin: return 1.0f;
case StrokeMode::Regular: return 1.5f;
case StrokeMode::Bold: return 2.0f;
}
return 1.0f;
};
static float getStrokeWidth(StrokeMode mode) {
float width = getStrokeUnits(mode);
width *= 1.0f + (Spacing::scale.uiScale - 1) * scale.uiScaleInfluenceStroke;
width = std::round(width * 2.0f) / 2.0f;
width = juce::jlimit(scale.MINSTROKE, scale.MAXSTROKE, width);
return width;
};
};
struct Shadows {
};
struct Opacity {
static constexpr float MINALPHA = 0.04f;
static constexpr float MAXALPHA = 0.95f;
struct AlphaToken {
enum class Role {
Text, Icon, Surface, Overlay, Stroke, FocusGlow, Disabled, InteractiveFill
};
enum class State {
Rest, Hover, Active, Checked, Dragged, Disabled
};
enum class Emphasis {
High, Medium, Low, Muted
};
Role role;
State state;
Emphasis emphasis;
};
static AlphaToken getAlphaToken(AlphaToken::Role role, AlphaToken::State state, AlphaToken::Emphasis emphasis) {
return AlphaToken{ role, state, emphasis };
};
static float getAlphaFactor(AlphaToken::Role role, AlphaToken::State state, AlphaToken::Emphasis emphasis) {
float alphaFactor;
if (role == AlphaToken::Role::Text || role == AlphaToken::Role::Icon) {
alphaFactor = fromEmphasis(emphasis);
}else {
alphaFactor = fromRole(role);
}
alphaFactor += fromState(state);
if (state == AlphaToken::State::Disabled) alphaFactor *= 0.5f;
alphaFactor = std::clamp(alphaFactor, MINALPHA, MAXALPHA);
return alphaFactor;
}
static float fromEmphasis(AlphaToken::Emphasis emphasis) {
switch (emphasis) {
case AlphaToken::Emphasis::High: return 0.92f;
case AlphaToken::Emphasis::Medium: return 0.72f;
case AlphaToken::Emphasis::Low: return 0.56f;
case AlphaToken::Emphasis::Muted: return 0.42f;
}
return 0.92f;
}
static float fromRole(AlphaToken::Role role) {
switch (role) {
case AlphaToken::Role::Surface: return 0.96f;
case AlphaToken::Role::Overlay: return 0.28f;
case AlphaToken::Role::Stroke: return 0.14f;
case AlphaToken::Role::FocusGlow: return 0.18f;
case AlphaToken::Role::Disabled: return 0.45f;
case AlphaToken::Role::InteractiveFill: return 0.95f;
default: return 1.0f;
}
}
static float fromState(AlphaToken::State state) {
switch (state) {
case AlphaToken::State::Rest: return 0.0f;
case AlphaToken::State::Hover: return 0.06f;
case AlphaToken::State::Active: return 0.10f;
case AlphaToken::State::Checked: return 0.08f;
case AlphaToken::State::Dragged: return 0.10f;
case AlphaToken::State::Disabled: return 0.0f;
}
return 0.0f;
}
static juce::Colour applyAlpha(juce::Colour baseColour, AlphaToken token) {
float alphaFactor = getAlphaFactor(token.role, token.state, token.emphasis);
return baseColour.withMultipliedAlpha(alphaFactor);
}
};
struct Motion {
};
struct Components {
struct ButtonStyles {
};
struct SliderStyles {
};
struct LabelStyles {
};
struct TextInputStyles {
};
struct ComboBoxStyles {
};
};
struct Assets {
};
struct Layout {
};
private
:
};
}
#endif //AXIOMDESIGNSYSTEM_H