pub struct Theme {
pub name: Cow<'static, str>,
pub light: Option<ThemeMode>,
pub dark: Option<ThemeMode>,
pub layout: LayoutTheme,
pub icon_set: Option<IconSet>,
pub icon_theme: Option<Cow<'static, str>>,
}Expand description
A complete native theme with a name and optional light/dark variants.
This is the top-level type that theme files deserialize into and that platform readers produce.
§Examples
use native_theme::theme::Theme;
// Load a bundled preset
let theme = Theme::preset("dracula").unwrap();
assert_eq!(theme.name, "Dracula");
// Parse from a TOML string
let toml = r##"
name = "Custom"
[light.defaults]
accent_color = "#ff6600"
"##;
let custom = Theme::from_toml(toml).unwrap();
assert_eq!(custom.name, "Custom");
// Merge themes (overlay wins for populated fields)
let mut base = Theme::preset("catppuccin-mocha").unwrap();
base.merge(&custom);
assert_eq!(base.name, "Catppuccin Mocha"); // base name is preservedFields§
§name: Cow<'static, str>Theme name (e.g., “Breeze”, “Adwaita”, “Windows 11”).
Uses Cow<'static, str> so bundled presets can store borrowed
&'static str values without per-load String allocations.
User-provided names (from TOML files or runtime detection)
are Cow::Owned.
light: Option<ThemeMode>Light variant of the theme.
dark: Option<ThemeMode>Dark variant of the theme.
layout: LayoutThemeLayout spacing constants (shared between light and dark variants).
icon_set: Option<IconSet>Which icon loading mechanism to use (Freedesktop, Material, Lucide,
SfSymbols, SegoeIcons). Shared across light and dark variants.
When None, filled during resolution from
system_icon_set().
§Examples
use native_theme::theme::{Theme, IconSet};
let theme = Theme::preset("material")?;
assert_eq!(theme.icon_set, Some(IconSet::Material));icon_theme: Option<Cow<'static, str>>Visual icon theme name (shared across light and dark variants).
Acts as the default when a variant’s
ThemeDefaults::icon_theme
is None. Variants that need a different icon theme per color mode
(e.g. KDE Plasma: "breeze" light / "breeze-dark" dark) set the
override on ThemeDefaults::icon_theme.
Precedence at resolve time:
ThemeMode::defaults.icon_theme— per-variant override (if set)Theme::icon_theme— this field (if set)system_icon_theme()— runtime fallback
See doc 1 §20 and docs/todo_v0.5.7_gaps.md §G4 for the design rationale.
Implementations§
Source§impl Theme
impl Theme
Sourcepub fn merge(&mut self, overlay: &Self)
pub fn merge(&mut self, overlay: &Self)
Merge an overlay theme into this theme.
The base name is kept. For each variant (light/dark):
- If both base and overlay have a variant, they are merged recursively.
- If only the overlay has a variant, it is cloned into the base.
- If only the base has a variant (or neither), no change.
Sourcepub fn pick_variant(&self, mode: ColorMode) -> Result<&ThemeMode>
pub fn pick_variant(&self, mode: ColorMode) -> Result<&ThemeMode>
Pick the appropriate variant for the given mode, with cross-fallback.
When mode is ColorMode::Dark, prefers dark and falls back to light.
When mode is ColorMode::Light, prefers light and falls back to dark.
§Errors
Returns Error::NoVariant if the theme has
no variants at all.
Sourcepub fn into_variant(self, mode: ColorMode) -> Result<ThemeMode>
pub fn into_variant(self, mode: ColorMode) -> Result<ThemeMode>
Extract a variant by consuming the theme, avoiding a clone.
When mode is ColorMode::Dark, returns the dark variant (falling back to
light). When ColorMode::Light, returns light (falling back to dark).
Use this when you own the Theme and don’t need it afterward.
For read-only inspection, use pick_variant().
§Errors
Returns Error::NoVariant if the theme has
no variants at all.
§Examples
use native_theme::theme::ColorMode;
let theme = native_theme::theme::Theme::preset("dracula")?;
let variant = theme.into_variant(ColorMode::Dark)?;
let resolved = variant.resolve_system()?;Sourcepub fn resolve(&self, mode: ColorMode) -> Result<Resolved>
pub fn resolve(&self, mode: ColorMode) -> Result<Resolved>
Resolve this theme for a color mode into a ready-to-render bundle.
Picks the variant (with cross-fallback), applies inheritance and
validation to produce a ResolvedTheme, and resolves the shared
icon_set / icon_theme fields. This is the one-call path from a
parsed Theme to the values a UI framework needs.
icon_set falls back to system_icon_set() when the TOML omits
it. icon_theme uses three-tier precedence:
- The picked variant’s
defaults.icon_theme(per-mode override). Theme::icon_theme(shared across variants).system_icon_theme()(runtime detect).
Resolved::icon_theme_explicit reports whether tiers 1 or 2
fired (value came from TOML) versus tier 3 (runtime fallback).
Uses ResolutionContext::from_system
for DPI and button-order. For custom contexts, construct the
variant manually and call
ThemeMode::into_resolved.
§Errors
Returns crate::Error::NoVariant when the theme has no variants,
or crate::Error::ResolutionIncomplete /
crate::Error::ResolutionInvalid when the picked variant cannot
be fully resolved.
§Examples
use native_theme::theme::{ColorMode, Theme};
// Theme-level `icon_theme` (tier 2) — applies to both modes.
let material = Theme::preset("material")?;
let r = material.resolve(ColorMode::Dark)?;
assert_eq!(r.icon_theme.as_ref(), "material");
assert!(r.icon_theme_explicit);
// Per-variant `icon_theme` (tier 1) — differs by mode.
let breeze = Theme::preset("kde-breeze")?;
assert_eq!(breeze.resolve(ColorMode::Light)?.icon_theme.as_ref(), "breeze");
assert_eq!(breeze.resolve(ColorMode::Dark)?.icon_theme.as_ref(), "breeze-dark");Sourcepub fn preset(name: &str) -> Result<Self>
pub fn preset(name: &str) -> Result<Self>
Load a bundled theme preset by name.
Returns the preset as a fully populated Theme with both
light and dark variants.
§Errors
Returns crate::Error::UnknownPreset if the preset name is not recognized.
§Examples
let theme = native_theme::theme::Theme::preset("catppuccin-mocha")?;
assert!(theme.light.is_some());Bundled preset names are Cow::Borrowed (no allocation):
use native_theme::theme::Theme;
let theme = Theme::preset("dracula")?;
assert!(matches!(theme.name, std::borrow::Cow::Borrowed(_)));Sourcepub fn from_toml(toml_str: &str) -> Result<Self>
pub fn from_toml(toml_str: &str) -> Result<Self>
Parse a TOML string into a Theme.
§TOML Format
Theme files use the following structure. All fields are Option<T> –
omit any field you don’t need. Unknown fields are silently ignored.
Hex colors accept #RRGGBB or #RRGGBBAA format.
name = "My Theme"
[light.defaults]
accent_color = "#4a90d9"
background_color = "#fafafa"
text_color = "#2e3436"
surface_color = "#ffffff"
muted_color = "#929292"
shadow_color = "#00000018"
danger_color = "#dc3545"
warning_color = "#f0ad4e"
success_color = "#28a745"
info_color = "#4a90d9"
selection_background = "#4a90d9"
selection_text_color = "#ffffff"
link_color = "#2a6cb6"
focus_ring_color = "#4a90d9"
disabled_text_color = "#c0c0c0"
disabled_opacity = 0.5
[light.defaults.font]
family = "sans-serif"
size = 10.0
[light.defaults.mono_font]
family = "monospace"
size = 10.0
[light.defaults.border]
color = "#c0c0c0"
corner_radius = 6.0
corner_radius_lg = 12.0
line_width = 1.0
opacity = 0.15
shadow_enabled = true
[light.button]
background_color = "#e8e8e8"
min_height = 32.0
[light.button.font]
color = "#2e3436"
[light.button.border]
padding_horizontal = 12.0
padding_vertical = 6.0
[light.tooltip]
background_color = "#2e3436"
max_width = 300.0
[light.tooltip.font]
color = "#f0f0f0"
# [dark.*] mirrors the same structure as [light.*]§Errors
Returns crate::Error::Toml if the TOML is invalid.
§Examples
let toml = r##"
name = "My Theme"
[light.defaults]
accent_color = "#ff0000"
"##;
let theme = native_theme::theme::Theme::from_toml(toml).unwrap();
assert_eq!(theme.name, "My Theme");Sourcepub fn from_file(path: impl AsRef<Path>) -> Result<Self>
pub fn from_file(path: impl AsRef<Path>) -> Result<Self>
Load a Theme from a TOML file.
§Errors
Returns crate::Error::Io if the file cannot be read, or
crate::Error::Toml if the TOML content is invalid.
§Examples
let theme = native_theme::theme::Theme::from_file("my-theme.toml").unwrap();Sourcepub fn list_presets() -> &'static [PresetInfo]
pub fn list_presets() -> &'static [PresetInfo]
List all available bundled presets with structured metadata.
Returns a static slice of PresetInfo entries,
one per bundled preset. Each entry carries the machine-readable key,
human-readable display name, target platform tags, and a light_only flag.
§Examples
let presets = native_theme::theme::Theme::list_presets();
assert_eq!(presets.len(), 16);
assert_eq!(presets[0].key, "kde-breeze");
assert_eq!(presets[0].display_name, "KDE Breeze");Sourcepub fn list_presets_for_platform() -> Vec<PresetInfo>
pub fn list_presets_for_platform() -> Vec<PresetInfo>
List presets appropriate for the current platform, with structured metadata.
Platform-specific presets (kde-breeze, adwaita, windows-11, macos-sonoma, ios) are only included on their native platform. Community themes are always included.
Note: Unlike list_presets() which returns a static slice,
this method returns Vec because it filters the preset list at runtime based
on the detected platform.
§Examples
let presets = native_theme::theme::Theme::list_presets_for_platform();
// On Linux KDE: includes kde-breeze, adwaita, plus all community themes
// On Windows: includes windows-11 plus all community themes
assert!(!presets.is_empty());Sourcepub fn to_toml(&self) -> Result<String>
pub fn to_toml(&self) -> Result<String>
Serialize this theme to a TOML string.
§Errors
Returns crate::Error::ReaderFailed if serialization fails.
§Examples
let theme = native_theme::theme::Theme::preset("catppuccin-mocha").unwrap();
let toml_str = theme.to_toml().unwrap();
assert!(toml_str.contains("name = \"Catppuccin Mocha\""));Sourcepub fn lint_toml(toml_str: &str) -> Result<Vec<String>>
pub fn lint_toml(toml_str: &str) -> Result<Vec<String>>
Check a TOML string for unrecognized field names.
Parses the TOML as a generic table and walks all keys, comparing
against the known fields for each section. Returns a Vec<String>
of warnings for any keys that don’t match a known field. An empty
vec means all keys are recognized.
This is an opt-in linting tool for theme authors. It does NOT affect
from_toml() behavior (which silently ignores unknown fields via serde).
§Errors
Returns Err if the TOML string cannot be parsed at all.
§Examples
let warnings = native_theme::theme::Theme::lint_toml(r##"
name = "Test"
[light.defaults]
backround = "#ffffff"
"##).unwrap();
assert_eq!(warnings.len(), 1);
assert!(warnings[0].contains("backround"));