Skip to content

Instantly share code, notes, and snippets.

@memononen
Created December 18, 2024 08:23
Show Gist options
  • Save memononen/460c86488a29bebe4b3a2540f6ef8b68 to your computer and use it in GitHub Desktop.
Save memononen/460c86488a29bebe4b3a2540f6ef8b68 to your computer and use it in GitHub Desktop.
// Named color names (theme specific)
enum MIcolorNames {
MI_COLOR_NEUTRAL,
MI_COLOR_ACCENT_A,
};
// Tones per name (theme specific)
enum MIcolorTone {
MI_COLORTONE_50,
MI_COLORTONE_75,
MI_COLORTONE_100,
MI_COLORTONE_200,
MI_COLORTONE_300,
MI_COLORTONE_400,
MI_COLORTONE_500,
MI_COLORTONE_600,
MI_COLORTONE_700,
MI_COLORTONE_800,
MI_COLORTONE_900,
MI_COUNT_COLORTONES,
};
static float scaled(float v, float scale)
{
return froundf(v * scale);
}
void miInitTestTheme(float scale)
{
// Setup color palette
MIcolor neutralColors[MI_COUNT_COLORTONES] = {
miRGBA(29,29,29, 255), // 50
miRGBA(38,38,38, 255), // 75
miRGBA(50,50,50, 255), // 100
miRGBA(63,63,63, 255), // 200
miRGBA(84,84,84, 255), // 300
miRGBA(112,112,112, 255), // 400
miRGBA(144,144,144, 255), // 500
miRGBA(178,178,178, 255), // 600
miRGBA(209,209,209, 255), // 700
miRGBA(235,235,235, 255), // 800
miRGBA(255,255,255, 255), // 900
};
MIcolor accentAColors[MI_COUNT_COLORTONES] = {
miRGBA(76,161,169, 29), // 50
miRGBA(76,161,169, 38), // 75
miRGBA(76,161,169, 50), // 100 - window background
miRGBA(76,161,169, 63), // 200
miRGBA(76,161,169, 84), // 300
miRGBA(76,161,169, 112), // 400
miRGBA(76,161,169, 144), // 500
miRGBA(76,161,169, 178), // 600
miRGBA(76,161,169, 209), // 700 - text
miRGBA(76,161,169, 235), // 800
miRGBA(76,161,169, 255), // 900
};
miSetColorTokenScale(MI_COLOR_NEUTRAL, neutralColors, MI_COUNT_COLORTONES);
miSetColorTokenScale(MI_COLOR_ACCENT_A, accentAColors, MI_COUNT_COLORTONES);
// Common design tokens
float defaultWidgetHeight = scaled(22, scale);
float defaultWidgetWidth = scaled(100, scale);
float defaultFontSize = scaled(13, scale);
float smallFontSize = scaled(11, scale);
float defaultIconSize = scaled(18, scale);
float smallRounding = scaled(1, scale);
float normalRounding = scaled(2, scale);
float mediumRounding = scaled(4, scale);
float largeRounding = scaled(8, scale);
MIsize defaultWidgetSize = { defaultWidgetWidth, defaultWidgetHeight };
float scrollbarSize = scaled(10, scale);
float sliderTrackSize = scaled(4, scale);
MIpadding contentPadding = { scaled(8, scale), scaled(0, scale) };
MIpadding windowPadding = { scaled(4, scale), scaled(4, scale) };
MIpadding windowPaddingSmall = { scaled(2, scale), scaled(2, scale) };
float windowSpacing = scaled(6, scale);
float smallSpacing = scaled(2, scale);
float defaultSpacing = scaled(4, scale);
float strokeWidth = scaled(1, scale);
float caretWidth = scaled(2, scale);
// Common fonts
MIfontStyle normalFont = {
.face = MI_FONT_NORMAL,
.size = defaultFontSize,
.lineHeight = 1.4f,
};
MIfontStyle normalSmallFont = {
.face = MI_FONT_NORMAL,
.size = smallFontSize,
.lineHeight = 1.4f,
};
MIfontStyle boldFont = {
.face = MI_FONT_BOLD,
.size = defaultFontSize,
.lineHeight = 1.4f,
};
MIfontStyle iconFont = {
.face = MI_FONT_ICON,
.size = defaultIconSize,
};
// The styles are defined using a macro, which adds (hopefully) a but more typesafety.
// The type name gets hashed to the lookup key.
//
// The macro is kinda annoying for syntax highlighting, though. I wish I could od without it.
//
// #define MI_THEME_DEFINE_STYLE(name, type, ...) miThemeDefineStyle(name, #type, &(type) { __VA_ARGS__ }, sizeof(type))
// #define MI_THEME_GET_STYLE(name, type) (type*)miThemeGetStyle(name, #type, sizeof(type))
// Styles
MI_THEME_DEFINE_STYLE("label", MIthemeTextStyle,
.font = normalFont,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_500),
.align = MI_ALIGN_LEFT | MI_ALIGN_MIDDLE,
.minSize = { 0.f, defaultWidgetHeight } // width based on content
);
MI_THEME_DEFINE_STYLE("text", MIthemeTextStyle,
.font = normalFont,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_700),
.align = MI_ALIGN_LEFT | MI_ALIGN_MIDDLE,
.minSize = { 0.f, defaultWidgetHeight } // width based on content
);
MI_THEME_DEFINE_STYLE("paragraph", MIthemeTextStyle,
.font = normalFont,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_700),
.align = MI_ALIGN_LEFT | MI_ALIGN_MIDDLE,
.minSize = { 0.f, defaultWidgetHeight } // width based on content
);
MI_THEME_DEFINE_STYLE("icon", MIthemeTextStyle,
.font = iconFont,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_500),
.align = MI_ALIGN_LEFT | MI_ALIGN_MIDDLE,
.minSize = { defaultIconSize, defaultWidgetHeight }
);
MI_THEME_DEFINE_STYLE("input", MIinputStyle,
.text = {
.font = normalFont,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_700),
.states = {
.active = miColorModDeltaTone(1),
.disabled = miColorModDeltaTone(-2)
},
.align = MI_ALIGN_LEFT | MI_ALIGN_MIDDLE,
},
.box = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_200),
.strokeStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.strokeWidth = strokeWidth,
.rounding = smallRounding,
.roundingMask = MI_ROUND_ALL,
},
.defaultSize = defaultWidgetSize,
.padding = contentPadding,
.caretWidth = caretWidth,
.caretColor = miNamedColor(MI_COLOR_ACCENT_A, MI_COLORTONE_900),
.selectionColor = miNamedColor(MI_COLOR_ACCENT_A, MI_COLORTONE_500),
);
MI_THEME_DEFINE_STYLE("slider", MIsliderStyle,
.track = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.fillStates = { 0 },
.rounding = sliderTrackSize/2,
.roundingMask = MI_ROUND_ALL,
},
.range = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_300),
.fillStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.rounding = sliderTrackSize/2,
.roundingMask = MI_ROUND_ALL,
},
.thumb = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_600),
.fillStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_75),
.strokeWidth = strokeWidth,
.rounding = defaultWidgetHeight/4,
.roundingMask = MI_ROUND_ALL,
},
.defaultSize = defaultWidgetSize,
.thumbSize = (MIsize) { defaultWidgetHeight/2, defaultWidgetHeight },
.trackSize = sliderTrackSize,
);
MI_THEME_DEFINE_STYLE("scrollbar", MIsliderStyle,
.track = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_75),
.fillStates = {0},
.rounding = scrollbarSize/2,
.roundingMask = MI_ROUND_ALL,
},
.thumb = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_200),
.fillStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.strokeStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.strokeWidth = strokeWidth,
.rounding = scrollbarSize/2,
.roundingMask = MI_ROUND_ALL,
},
.defaultSize = (MIsize){ scrollbarSize, scrollbarSize },
.thumbSize = (MIsize) { scrollbarSize, scrollbarSize },
.trackSize = scrollbarSize,
);
MI_THEME_DEFINE_STYLE("button", MIbuttonStyle,
.text = {
.font = normalFont,
.align = MI_ALIGN_CENTER | MI_ALIGN_MIDDLE,
.color = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_700),
.states = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
}
},
.box = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_200),
.fillStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
},
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.strokeStates = {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(1),
},
.strokeWidth = strokeWidth,
.rounding = mediumRounding,
.roundingMask = MI_ROUND_ALL,
},
.defaultSize = { 0.f, defaultWidgetHeight },
.padding = contentPadding
);
MI_THEME_DEFINE_STYLE("window", MIwindowStyle,
.box = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_100),
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_75),
.strokeWidth = strokeWidth,
.rounding = normalRounding,
.roundingMask = MI_ROUND_ALL,
},
.padding = windowPadding,
.spacing = windowSpacing,
);
MI_THEME_DEFINE_STYLE("popup", MIwindowStyle,
.box = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_75),
.strokeColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.strokeWidth = strokeWidth,
.rounding = normalRounding,
.roundingMask = MI_ROUND_ALL,
},
.padding = windowPadding,
.spacing = windowSpacing,
.offset = smallSpacing,
);
MI_THEME_DEFINE_STYLE("tooltip", MIwindowStyle,
.box = {
.fillColor = miNamedColor(MI_COLOR_NEUTRAL, MI_COLORTONE_50),
.rounding = smallRounding,
.roundingMask = MI_ROUND_ALL,
},
.padding = windowPaddingSmall,
.spacing = defaultSpacing,
.offset = smallSpacing,
);
{
// Menu item is compount widget.
//
// [icon] Text detail [>]
//
MIthemeTextStyle smallLabel = *MI_THEME_GET_STYLE("label", MIthemeTextStyle);
smallLabel.font = normalSmallFont;
smallLabel.states = (MIcolorModStates) {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
};
MIthemeTextStyle text = *MI_THEME_GET_STYLE("text", MIthemeTextStyle);
text.states = (MIcolorModStates) {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
};
MIthemeTextStyle icon = *MI_THEME_GET_STYLE("icon", MIthemeTextStyle);
icon.states = (MIcolorModStates) {
.hover = miColorModDeltaTone(1),
.active = miColorModDeltaTone(2),
};
MI_THEME_DEFINE_STYLE("menuitem", MImenuItemStyle,
.icon = icon,
.text = text,
.detail = smallLabel,
.subMenu = icon,
.background = {
.fillColor = miNamedColor(MI_COLOR_ACCENT_A, MI_COLORTONE_500),
.fillStates = {
.normal = miColorModClear(),
.active = miColorModDeltaTone(4),
},
.rounding = smallRounding,
.roundingMask = MI_ROUND_ALL,
},
.defaultSize = { 0.f, defaultWidgetHeight },
.padding = { defaultSpacing, 0 },
.spacing = defaultSpacing,
.checkIcon = /*ICON_CHECK*/ "\xee\x97\x8a",
.subMenuIcon = /*ICON_CHEVRON_RIGHT*/ "\xee\x97\x8c",
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment