Location
include/mop/core/theme.h — MopTheme + API
src/core/theme.c — Default values
MopTheme
Single struct covering all chrome colors and line widths. One accent color drives outlines and indicators; per-subsystem overrides (gizmo, grid, wireframe) stay near at hand.
typedef struct MopTheme {
/* UI accent (outlines, indicators) */
MopColor accent;
/* Background gradient */
MopColor bg_top;
MopColor bg_bottom;
/* Grid */
MopColor grid_minor, grid_major, grid_axis_x, grid_axis_z;
float grid_line_width_minor, grid_line_width_major, grid_line_width_axis;
/* Gizmo */
MopColor gizmo_x, gizmo_y, gizmo_z, gizmo_center, gizmo_hover;
float gizmo_line_width; /* literal px, DPI-unscaled */
float gizmo_opacity;
float gizmo_target_opacity; /* selected mesh when gizmo on */
/* Wireframe overlay */
MopColor wireframe_color;
float wireframe_opacity;
float wireframe_line_width;
/* Selection */
MopColor selection_outline; /* post-process outline stroke */
float selection_outline_width;
MopColor vertex_select_color, edge_select_color, face_select_color;
float vertex_select_size;
float edge_select_width;
float face_select_opacity;
/* Object outline (always-on border) */
float outline_opacity_selected;
float outline_opacity_unselected;
/* Overlay extras */
MopColor normal_color; float normal_line_width;
MopColor bounds_color; float bounds_line_width;
/* HUD axis indicator */
MopColor axis_x, axis_y, axis_z, axis_neg_x, axis_neg_y, axis_neg_z;
/* Camera frustum visualization */
MopColor camera_frustum_color;
float camera_frustum_line_width;
/* Depth bias for coplanar overlays */
float depth_bias;
} MopTheme;
Functions
MopTheme mop_theme_default(void);
void mop_viewport_set_theme(MopViewport *vp, const MopTheme *theme);
const MopTheme *mop_viewport_get_theme(const MopViewport *vp);
The theme struct is copied on set_theme; callers may free their copy afterwards.
Defaults
- Background: mid-gray (
#10101blinear) - Accent: white — selection outline and indicator color
- Gizmo X / Y / Z: bright saturated red / green / blue; hover tint is the theme accent
- Grid axis lines (X / Z): neutral gray
(0.70, 0.70, 0.70)— the corner navigator and transform gizmo already carry the X/Y/Z color coding, so a saturated red/blue axis line competed with those signals at oblique angles. Override per-viewport withgrid_axis_x/grid_axis_zif your host wants the older saturated look. - Face selection fill: derived mid-shade
(0.35, 0.35, 0.35)atface_select_opacity = 0.4
Outline Color Gotcha
The selection outline painter reads theme.accent (not theme.selection_outline). selection_outline is the width-based stroke drawn in the 2D chrome pass for subelement highlights. To change the silhouette outline color around selected objects, set accent:
MopTheme t = mop_theme_default();
t.accent = (MopColor){1, 1, 1, 1}; /* white silhouette outline */
mop_viewport_set_theme(vp, &t);
Selection-chrome example: hot-pink highlight
The interactive example sets theme.accent to #FF1493 so the
silhouette outline pops against any scene, and uses the same color
as the bg_color of the selection callout pill. Note the
gamma-space difference: overlay primitives composite without gamma
(values are sRGB-fraction-direct), while the text rasterizer
applies a gamma-2.0 sqrt at composite (linear values needed):
/* Overlay path — sRGB-fraction values directly. */
const MopColor outline = {255/255.0f, 20/255.0f, 147/255.0f, 1.0f};
theme.accent = outline;
theme.selection_outline = outline;
/* Text-pill path — gamma-2.0-encoded counterpart of the same color. */
const MopColor pill_bg_linear = {1.0f, 0.0062f, 0.332f, 1.0f};
mop_text_draw_label(vp, NULL, mesh, "CUBE",
MOP_LABEL_TOP_CENTER, MOP_LABEL_ALWAYS_ON_TOP,
(MopTextStyle){
.color = (MopColor){0, 0, 0, 1},
.px_size = 13.0f,
.weight = 0.22f,
.bg_color = pill_bg_linear,
.bg_padding = 6.0f,
});
DPI Note
gizmo_line_width is in literal framebuffer pixels and is not scaled with DPI or framebuffer height. The shaft stays thin at high resolutions — matching the corner axis navigator style. Tip balls, axis letters, and diamond handles do scale with framebuffer height (reference 1080p → 1× at 1080, 1.33× at 1440p, 2× at 4K) so they remain readable.