ui::Theme¶
Header: winui/ui/themes/Theme.hpp.
Everything a theme defines: the palette and accent shades, corner radii, padding and border width, the surfaces, text styles and type ramp, the focus visual, motion, popup behavior, window frame settings, and the three button sets custom controls draw with (draw_control_chrome).
A default-constructed Theme is not usable: button_normal, accent_button_* and the other button visuals have no meaningful default. Always get a Theme from Theme::light() or Theme::fluent(palette), also for a class member's default (Theme theme_ = Theme::light();, never Theme theme_{};).
A Window owns the active theme (Window::pin_theme()) and hands it to every element's measure and render. An element with a requested theme draws its subtree with with_dark_mode. The fields are plain data, so an app may take a resolved theme and change single values (metrics, type, motion) before applying it. The role colors are the exception: colors() is read-only, and a theme with other colors comes from fluent(const ThemeColors&) or classic(const ThemeColors&, ...). How each built-in control draws itself in an era (its toggle, slider, field and row-selection looks) is internal to the theme and not public. A copy of a theme carries it along unchanged.
Example:
// A dark Fluent theme with a wider focus ring, applied to a window:
ui::Theme theme = ui::Theme::fluent(ui::Palette::fluent_dark());
theme.focus_ring_outer_width = 3.0f;
window.pin_theme(std::move(theme));
variant¶
The design language the theme draws in, the one its factory built it for. Under high contrast it is the variant whose contrast look this is, so check high_contrast too. A custom control reads it to ask "is this Fluent?" before reading palette.
Example:
const bool fluent = (theme.variant == ui::ThemeVariant::Fluent2 || theme.variant == ui::ThemeVariant::Fluent) &&
!theme.high_contrast;
const ui::Color fill = fluent ? theme.palette.card_background_default : theme.colors().field;
high_contrast¶
Whether the theme is a high-contrast one, from a system contrast theme or ThemeMode::HighContrast. Its colors are then the contrast scheme's, and palette is left default even under the Fluent variants.
dark¶
Whether the theme draws light content on a dark background: the dark mode of a variant that has both (Fluent 2, Fluent, Mdl2, Metro), or a dark contrast scheme. Every theme sets it, and the fixed-palette eras are light. A custom control reads it to pick a per-mode value, such as a shadow's alpha or a band color, under any variant. palette.dark is set only by Theme::fluent().
palette¶
The Fluent palette the theme was derived from. Only Theme::fluent() sets it, so it is filled for the Fluent variants outside high contrast. Every other theme leaves it default, so check variant before reading it.
accent_ramp¶
The accent shades the theme was resolved from, the Windows defaults when the theme was not resolved from the system's settings. Controls and apps that need a lighter or darker shade of the accent read it here.
with_dark_mode¶
The same theme resolved light (false) or dark (true), which an element with a requested theme (Element::set_requested_theme()) draws its subtree with. Set by resolve_theme() for the variants that have both. Empty for the fixed-palette eras and under high contrast, where a requested theme is ignored. It resolves the request again, so later edits to this theme are not in its result. An element with a requested theme carries the edits that are not colors (metrics, type, motion) over on top of it.
corner_radius¶
Control corner radius in DIPs: buttons, fields, toggles.
overlay_corner_radius¶
Popup corner radius in DIPs: flyouts, menus, dialogs.
padding¶
Button content padding.
border_width¶
Default control border width; see ButtonVisual::border_width.
sizing¶
Control density overrides; all unset (standard density) by default.
focus_ring_outer_width¶
outer ring stroke, in DIPs
The two-ring focus visual (render/FocusRing.hpp's draw_focus_ring()): a 2px outer ring in colors.focus with a 1px colors.focus_inner ring right inside it. The pair fills the focus_ring_margin pixels outside the control's bounds. focus_ring_margin is how far out the outer ring's outer edge lands, half a stroke beyond its center line.
focus_ring_inner_width¶
inner ring stroke, in DIPs
focus_ring_margin¶
how far the two rings reach outside the control
spelling_error¶
TextBox's misspelling squiggle. Defaults to the Fluent light theme's critical red, the red every Windows spell checker uses, which the era themes keep. Theme::fluent() takes the palette's semantic_fill_critical, a lighter red in dark themes. High contrast uses the window text color, the only one sure to show on the field background.
disabled_text_effect¶
How a disabled control's text is drawn, on top of colors.text_disabled: the text beside a CheckBox, RadioButton or ToggleSwitch, and a disabled TextControl. Windows 95 through 2000 draw disabled text the way they draw a disabled push button's label, engraved: a highlight copy offset by a pixel under a shadow copy. So Theme::classic() sets the same Emboss it gives button_disabled.
Text only. A disabled check mark or radio dot stays a flat gray glyph, so the indicators read their own text and never this. Windows 3.x draws disabled text in plain gray, as every non-classic theme does.
severity¶
InfoBar, InfoBadge and ProgressBar severity colors.
anim_faster¶
The hover and press highlight fade of every control that can be toggled.
anim_fast¶
For offset and scale moves: ToggleSwitch's knob slide, RadioButton's dot resize, and Slider's inner-circle grow/shrink into Normal/Disabled.
anim_normal¶
Slider's inner thumb circle growing into Hover/Pressed. The duration is keyed by the state being entered, so leaving Hover runs at anim_fast.
anim_fast_out_slow_in¶
The easing curve for every offset/scale motion above, at either duration.
animations_enabled¶
Whether controls animate at all, resolved from ThemeRequest::animations and the system setting. When false, resolve_theme() also zeroes the durations above and popup_motion, and Window ticks its tree with animations_enabled() false, so every animation the framework plays lands on its end state at once.
text_style¶
The theme's base UI font: button labels, most control text, and menus unless chrome_text_style is set. TextControl reads type instead.
chrome_text_style¶
The font menus and menu bars draw in, for an era whose chrome font differs from its content font. nullopt uses text_style. Windows 3.x sets it: its chrome is System 10pt bold, and its dialogs MS Sans Serif 8pt.
type¶
The type ramp TextControl resolves its role() against.
page¶
Page silhouette metrics.
rasterization¶
How the era's shapes and text are rasterized. Window::render() applies it first, every frame. The default is antialiased shapes, grayscale text and no pixel snapping. Only the classic, Luna and Aero themes set something else.
focus_visual¶
How keyboard focus is shown: the Fluent two rings (the default), the classic eras' black dotted rectangle inside the control (Windows 95 through 7, drawn in colors.focus), or Metro's dashed pair.
divider_visual¶
Which divider geometry separators draw; EtchedGroove unless Theme::fluent().
disclosure_visual¶
Which expand, collapse and drop-down marks controls draw; ClassicArrow in Theme::classic().
popup_shadow¶
The shadow an open popup casts.
popup_motion¶
The era's popup open/close animations.
overlay_material¶
The material that flyouts, drop-down lists, menus and the other popups that close on an outside click fill with: in-app acrylic, which blurs what is drawn behind it. Only Fluent 2 sets it (Palette::acrylic_in_app_default()), and only while the system allows transparency (OsThemeSnapshot::transparency_enabled) and no contrast theme is on. Unset, those surfaces fill with their solid color, surfaces.menu. fill_overlay_surface() makes that choice.
icon_font_family¶
The symbol font a control's glyphs are drawn from, passed as the font_family of render/IconGlyph.hpp's draw_icon. Windows 10 Fluent uses Segoe MDL2 Assets, and Windows 11 Segoe Fluent Icons, a redesign with the same code points. Each theme names its font.
button_corner_radius¶
Button's own corner radius, for an era whose push button is rounded differently from its other controls. Unset, the default, follows corner_radius. Windows 3.x sets it, since its push button is the era's one rounded control.
surfaces¶
The era-specific surface colors every theme fills (see Surfaces): field borders, menus, toolbars, tabs, tooltips, link states and scroll bars.
chrome¶
Settings for the system-drawn window frame (see ChromeHints). The defaults leave the system's corner rounding, title bar mode and frame colors alone.
button_normal¶
Neutral Button (the default ButtonVariant), the look of every Button unless it opts into ButtonVariant::Accent/Subtle.
button_hover¶
button_pressed¶
button_disabled¶
accent_button_normal¶
ButtonVariant::Accent: a filled accent surface, at most one per view.
accent_button_hover¶
accent_button_pressed¶
accent_button_disabled¶
subtle_button_normal¶
ButtonVariant::Subtle: borderless, with only a wash on hover and press. Toolbar, command bar and menu buttons use it.
subtle_button_hover¶
subtle_button_pressed¶
subtle_button_disabled¶
colors¶
The colors by role, filled for every era and mode (see ThemeColors), and what a custom control reads first. Read-only. A different set of colors is a different theme, made by fluent(const ThemeColors&) or classic(const ThemeColors&, ...).
with_content_colors¶
A copy of this theme for drawing a container's content. Content in the default text color draws in text, and content that reads the background, such as a control that blends into its surface, sees background when one is given. A container whose surface differs from the page, such as a card, a popup or an accent-filled box, draws its children with it.
Example:
// An accent-filled box whose text follows the accent:
class AccentBoxControl : public ui::Element
{
protected:
void render_override(ui::Renderer& renderer, const ui::RenderContext& context) const override
{
const ui::Theme& theme = context.theme;
renderer.fill_rounded_rect(bounds(), 0.0f, theme.colors().accent);
child_->render(renderer, context.with_theme(theme.with_content_colors(theme.colors().text_on_accent)));
}
private:
std::unique_ptr<ui::Element> child_;
};
generation¶
A number unique to this Theme value, never 0. Each theme built, copied or moved into gets a new one, so a cache of measurements or derived themes can tell a new or edited theme from the one it came from. Editing a field in place keeps the number. Hand the edited theme over as a new value (Window::pin_theme()) for caches to see the change.
menu_text_style¶
chrome_text_style if this era has one, text_style otherwise.
resolved_button_corner_radius¶
button_corner_radius if set, corner_radius otherwise.
for_state¶
The neutral Button's visual (button_*) for an interaction state.
subtle_for_state¶
The borderless subtle_button_* visual for an interaction state. Menu bar, command bar and toolbar buttons use it, the menu bar's items included. A Button the app places itself uses for_state() unless it opts into ButtonVariant::Subtle.
focus_ring_style¶
The focus visual settings that render/FocusRing.hpp's draw_focus_ring() and draw_focus_ring_rings() take, built from this theme's fields.
fluent¶
A Fluent theme derived from palette: every value above and the built-in controls' own looks.
fluent¶
A Fluent theme derived from a full set of role colors. The roles that differ from the Fluent light colors, or the dark ones when roles.background is dark, replace the palette colors they come from, and everything else follows from them as in fluent(const Palette&). Change accent alone and the selection, the accent button's hover and pressed fills and the informational severity follow it. colors() of the result reads back what was passed for every role that was changed.
Example:
ui::ThemeColors colors = ui::Theme::fluent(ui::Palette::fluent_dark()).colors();
colors.accent = ui::Color::from_argb(0xFF8764B8);
window.pin_theme(ui::Theme::fluent(colors));
classic¶
static Theme classic(const ClassicPalette& palette, bool hot_track, const char* font_family, bool win3x_edge = false)
A classic (Windows 95, 98, 2000) theme derived from palette, the classic counterpart of fluent(). Every classic theme, a custom scheme included, is one ClassicPalette plus this function, with no hand-assembled ButtonVisual per state. hot_track lights a button's face under the pointer, as Windows 98 does and 95 does not. font_family is the UI font, at 11 px. Name a TrueType font: bitmap fonts can't be loaded, so MS Sans Serif or System quietly gives Segoe UI. The Windows 9x and 3.x themes pass Microsoft Sans Serif and then set it at 8 pt on a 13 px line. win3x_edge switches every edge to the Windows 3.x construction in place of the 9x four-ring bevel. Windows 3.x has only face, shadow and highlight colors for buttons, but draws a four-color, uneven edge: a 1px black frame, a 1px white highlight on the top and left and a 2px gray shadow on the right and bottom (render/Bevel.hpp's draw_win3x_border). It also rounds the push button, flattens the check box to a black and white outline, marks it with an X instead of a tick, and drops the engraved disabled label.
classic¶
static Theme classic(const ThemeColors& roles, bool hot_track, const char* font_family, bool win3x_edge = false)
A classic theme derived from a full set of role colors, the classic counterpart of fluent(const ThemeColors&). The roles that differ from the Windows Standard scheme's colors replace the scheme color they come from: background and control_fill replace the face, and with it the bevel, text the face text, and field and field_text the window colors. selection, selection_text, text_disabled, link and overlay (the menu) replace their own. The other roles follow from those, as in classic(const ClassicPalette&, ...).
Example:
ui::ThemeColors colors = ui::Theme::classic(ui::ClassicPalette::windows_standard(), true, "Tahoma").colors();
colors.selection = ui::Color::from_rgb(0x008080);
window.pin_theme(ui::Theme::classic(colors, true, "Tahoma"));
metro¶
A Metro (Windows 8.1) theme derived from palette, like fluent() and classic(). Metro has a dark and a light mode and a user accent, and dark says which mode the theme is. Pass MetroPalette::metro_dark() or metro_light() with the matching dark.
mdl2¶
An MDL2 (Windows 10, 1507 to 1703) theme derived from palette, like fluent(), classic() and metro(). Like Metro, MDL2 has a dark and a light mode, and dark says which, together with the matching Mdl2Palette::mdl2_dark() or mdl2_light(). accent is the system's current accent, which this era uses on the controls.
early¶
An early-Windows (1.x and 2.x) theme derived from palette, like fluent(), classic(), metro() and mdl2(), and the oldest of the five.
This era has no sense of depth and no button face, selection or disabled-text color, so its colors are an EarlyPalette. Its whole drawing model is six rules: a 1px frame, a flat fill, reverse video, black-and-white glyphs, fixed-pitch text without antialiasing, and a double 1px frame for a dialog.
light¶
Windows 11 Fluent, light: fluent(Palette::fluent_light()). The default theme a Window starts with, and the one to reach for in member defaults.
default_light¶
One light() theme that lives as long as the program, the default argument where a control is measured or drawn on its own. Some containers keep a pointer to the theme they were last measured with, so a default made of a temporary would dangle.
struct ControlSizing¶
Control density: the sizes compact density overrides. Each is nullopt in a standard theme, which leaves the control at its own standard size. ControlSizing::compact() holds the compact values, and ui/containers/DensityScope.hpp's DensityScope applies them to a subtree.
text_control_min_height¶
Text field minimum height (standard: 32).
text_control_padding¶
Text field padding (standard: the field's padding).
combo_box_min_height¶
ComboBox minimum height.
combo_box_padding¶
ComboBox padding (standard: its padding).
compact¶
Compact density: text field minimum height 24 and padding 2,2,6,1, ComboBox minimum height 24 and padding 12,1,0,3.
struct SeverityVisual¶
Severity colors: InfoBar's icon disc and body, and ProgressBar's paused and error indicator. Read these instead of palette, which only the Fluent themes fill.
The defaults are the Fluent light colors, so a theme that sets none still draws readable bars. Theme::fluent() fills them from the palette, resolve_high_contrast() from the system's contrast colors, and an era theme sets its own where the era has them.
| Field | Type | Default | |
|---|---|---|---|
fill |
Color |
the strong mark: InfoBar's icon disc, ProgressBar's indicator | |
background |
Color |
the surface behind the whole bar |
struct Severities¶
The four InfoBar severities plus the shared neutral fill, glyph and stroke colors.
| Field | Type | Default | |
|---|---|---|---|
informational |
SeverityVisual |
{Color::from_argb(0xFF0078D4), Color::from_argb(0x80F6F6F6)} |
Drawn in the accent itself. Theme::fluent() fills it from accent_button_normal. |
success |
SeverityVisual |
{Color::from_argb(0xFF0F7B0F), Color::from_argb(0xFFDFF6DD)} |
The success, caution and critical severities. |
caution |
SeverityVisual |
{Color::from_argb(0xFF9D5D00), Color::from_argb(0xFFFFF4CE)} |
|
critical |
SeverityVisual |
{Color::from_argb(0xFFC42B1C), Color::from_argb(0xFFFDE7E9)} |
|
neutral |
Color |
Color::from_argb(0xFF8A8A8A) |
The gray of InfoBadge's Informational styles. A fill only, since no InfoBar severity is neutral. |
text_on_fill |
Color |
colors::White |
The glyph drawn on one of the fill discs, and the bar's own 1px outline. |
stroke |
Color |
Color::from_argb(0x0F000000) |
struct TypeRamp¶
The type ramp TextControl::set_role() resolves against. Every theme fills body from text_style. Metro, MDL2 and the Fluent themes give the other roles their own size and weight.
header¶
One style per TextRole. A theme that sets only body leaves the rest at defaults.
subheader¶
title¶
subtitle¶
body¶
caption¶
body_strong¶
BodyStrong's own style, where an era has one, as Fluent 2 does. Unset, the role is body in SemiBold (TextControl::resolved_style()).
color¶
Per-role foregrounds; all unset outside Aero.
for_role¶
The style for a TextRole (BodyStrong falls back to body; see body_strong).
color_for_role¶
The color for a TextRole (BodyStrong shares body's); unset means colors.text.
struct RoleColors¶
Per-role foreground, for an era whose type ramp carries color as well as size and weight. Aero sets it: its main instruction is #003399 Segoe UI 12pt and links are #0066CC. Every entry is unset for every other theme, so a role paints in ThemeColors::text.
| Field | Type | Default | |
|---|---|---|---|
header |
std::optional<Color> |
One optional color per role; unset paints in ThemeColors::text. |
|
subheader |
std::optional<Color> |
||
title |
std::optional<Color> |
||
subtitle |
std::optional<Color> |
||
body |
std::optional<Color> |
||
caption |
std::optional<Color> |
struct PageMetrics¶
Page layout metrics: the grid step, the page inset and header, and the item spacing. Metro sets its 20px grid and 120/140/50 page layout here; the defaults suit every other theme.
| Field | Type | Default | |
|---|---|---|---|
grid_unit |
float |
4.0f |
the layout grid step, in DIPs |
content_margin |
Thickness |
{12.0f} |
page content inset, in DIPs |
header_height |
float |
0.0f |
0 == no header band |
item_spacing |
float |
4.0f |
gap between stacked items, in DIPs |
command_bar_height |
float |
48.0f |
a command bar's height, in DIPs |
struct PopupMotion¶
How menus and drop-down lists open and close.
| Field | Type | Default | |
|---|---|---|---|
menu |
ThemePopupAnimation |
ThemePopupAnimation::Slide |
how a menu opens |
list |
ThemePopupAnimation |
ThemePopupAnimation::None |
how a drop-down list opens |
open |
Duration |
{250.0} |
the opening animation's length |
close |
Duration |
{83.0} |
a menu's fade-out; 0 closes at once |