1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Renaming, comment & better default system

This commit is contained in:
adrien
2026-06-24 19:37:15 +02:00
parent 8762c7dba7
commit 63eacb1800
8 changed files with 229 additions and 268 deletions

View File

@@ -38,7 +38,7 @@ use crate::{
resize, response, scroll_area, theme_plugin, resize, response, scroll_area, theme_plugin,
util::IdTypeMap, util::IdTypeMap,
viewport::ViewportClass, viewport::ViewportClass,
widget_style::{Classes, StyleStruct, WidgetState}, widget_style::{Classes, WidgetState, WidgetStyle},
}; };
use crate::IdMap; use crate::IdMap;
@@ -2031,37 +2031,36 @@ impl Context {
} }
impl Context { impl Context {
/// Register a [`ThemeStyle`](crate::theme_plugin::ThemeStyle) for the specified widget [`StyleStruct`](StyleStruct) `S` /// Register a [`ThemeStyle`](crate::theme_plugin::ThemeStyle) for the specified widget type.
/// ///
/// A theme can only be added once for a specified widget. /// A theme can only be added once for a specified widget.
/// This way it's convenient to add themes in `eframe::run_simple_native`. /// If a theme is already registered for this widget, this is a no-op (useful for `eframe::run_simple_native`).
/// If you want to add the theme anyway, use [`Self::replace_theme`] instead. ///
pub fn add_theme<S: StyleStruct + 'static>( /// If you want to add the theme anyway, use [`Self::replace_widget_theme`] instead.
pub fn add_widget_theme<S: WidgetStyle + 'static>(
&self, &self,
theme: impl theme_plugin::ThemeStyle<S> + Send + Sync + 'static, theme: impl theme_plugin::ThemeStyle<S> + Send + Sync + 'static,
) { ) {
self.write(|ctx| ctx.themes.register::<S>(theme, false)); self.write(|ctx| ctx.themes.register::<S>(theme, false));
} }
/// Register a [`ThemeStyle`](crate::theme_plugin::ThemeStyle) for the specified widget [`StyleStruct`](StyleStruct) `S` /// Register a [`ThemeStyle`](crate::theme_plugin::ThemeStyle) for the specified widget.
/// ///
/// Overwrite any theme already registered for the specified widget [`StyleStruct`](StyleStruct). /// Overwrite any theme already registered for the specified widget [`WidgetStyle`](WidgetStyle).
/// If you want to avoid overwriting existing theme, use [`Self::add_theme`] instead. /// This allow to live edit a theme.
pub fn replace_theme<S: StyleStruct + 'static>( pub fn replace_widget_theme<S: WidgetStyle + 'static>(
&self, &self,
theme: impl theme_plugin::ThemeStyle<S> + Send + Sync + 'static, theme: impl theme_plugin::ThemeStyle<S> + Send + Sync + 'static,
) { ) {
self.write(|ctx| ctx.themes.register::<S>(theme, true)); self.write(|ctx| ctx.themes.register::<S>(theme, true));
} }
/// Compute the [`StyleStruct`] using the registered theme if available. /// Compute the [`WidgetStyle`] using the registered theme.
/// pub(crate) fn get_widget_style<S: WidgetStyle + Clone + 'static>(
/// Return `None` if no theme was registered for the [`StyleStruct`]
pub fn get_style<S: StyleStruct + Clone + 'static>(
&self, &self,
classes: &Classes, classes: &Classes,
state: WidgetState, state: WidgetState,
) -> Option<S> { ) -> S {
self.write(move |ctx| ctx.themes.get::<S>(classes, state)) self.write(move |ctx| ctx.themes.get::<S>(classes, state))
} }
} }

View File

@@ -1,11 +1,16 @@
use std::sync::Arc; use std::{any::TypeId, sync::Arc};
use epaint::mutex::Mutex; use emath::Vec2;
use epaint::{Shadow, Stroke, mutex::Mutex, text::TextWrapMode};
use crate::{ use crate::{
Id, Ui, Frame, Id, Spacing, Style, TextStyle, Ui, Visuals,
style::Widgets,
util::IdTypeMap, util::IdTypeMap,
widget_style::{Classes, StyleStruct, WidgetState}, widget_style::{
BaseStyle, ButtonStyle, CheckboxStyle, Classes, HasClasses as _, LabelStyle,
SELECTED_CLASS, SeparatorStyle, TextVisuals, WidgetState, WidgetStyle,
},
}; };
/// A cache that can be implemented to reduce computation time of a `ThemeStyle` /// A cache that can be implemented to reduce computation time of a `ThemeStyle`
@@ -15,12 +20,12 @@ pub struct ThemeCache {
} }
impl ThemeCache { impl ThemeCache {
/// Access the cache for the requested [`StyleStruct`] based on the [`Classes`] and /// Access the cache for the requested [`WidgetStyle`] based on the [`Classes`] and
/// the [`WidgetState`] /// the [`WidgetState`]
/// ///
/// If no entry match the parameter then compute the fallback style and /// If no entry match the parameter then compute the fallback style and
/// save the output for later. /// save the output for later.
pub fn get<S: StyleStruct + 'static>( pub fn get<S: WidgetStyle + 'static>(
&mut self, &mut self,
classes: &Classes, classes: &Classes,
state: WidgetState, state: WidgetState,
@@ -37,45 +42,201 @@ impl ThemeCache {
} }
} }
/// A Theme plugin that implement a style computation for a defined `StyleStruct` /// A Theme plugin that implement a style computation for a defined `WidgetStyle`
pub trait ThemeStyle<S> { pub trait ThemeStyle<S> {
/// The style according to the classes and state of the widget /// The style according to the classes and state of the widget
fn style(&mut self, classes: &Classes, state: WidgetState) -> S; fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> S;
/// Help to differ the different themes
fn theme_type_id(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
}
#[derive(Debug, Clone)]
struct DefaultStyle;
impl ThemeStyle<BaseStyle> for DefaultStyle {
fn style(&mut self, _themes: &Themes, _classes: &Classes, state: WidgetState) -> BaseStyle {
let visuals = Widgets::dark();
let spacing = Spacing::default();
let visuals = match state {
WidgetState::Noninteractive => visuals.noninteractive,
WidgetState::Inactive => visuals.inactive,
WidgetState::Hovered => visuals.hovered,
WidgetState::Active => visuals.active,
};
BaseStyle {
frame: Frame {
fill: visuals.bg_fill,
stroke: visuals.bg_stroke,
corner_radius: visuals.corner_radius,
inner_margin: spacing.button_padding.into(),
..Default::default()
},
stroke: visuals.fg_stroke,
text: TextVisuals {
color: visuals.text_color(),
font_id: TextStyle::Body.resolve(&Style::default()),
strikethrough: Stroke::NONE,
underline: Stroke::NONE,
},
}
}
}
impl ThemeStyle<ButtonStyle> for DefaultStyle {
fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> ButtonStyle {
let widget_visuals = Widgets::dark();
let spacing = Spacing::default();
let mut widget_visuals = match state {
WidgetState::Noninteractive => widget_visuals.noninteractive,
WidgetState::Inactive => widget_visuals.inactive,
WidgetState::Hovered => widget_visuals.hovered,
WidgetState::Active => widget_visuals.active,
};
let mut ws: BaseStyle = themes.get(classes, state);
if classes.has(SELECTED_CLASS) {
let visuals = Visuals::default();
widget_visuals.weak_bg_fill = visuals.selection.bg_fill;
widget_visuals.bg_fill = visuals.selection.bg_fill;
widget_visuals.fg_stroke = visuals.selection.stroke;
ws.text.color = visuals.selection.stroke.color;
}
ButtonStyle {
frame: Frame {
fill: widget_visuals.weak_bg_fill,
stroke: widget_visuals.bg_stroke,
corner_radius: widget_visuals.corner_radius,
outer_margin: (-Vec2::splat(widget_visuals.expansion)).into(),
inner_margin: (spacing.button_padding + Vec2::splat(widget_visuals.expansion)
- Vec2::splat(widget_visuals.bg_stroke.width))
.into(),
..Default::default()
},
text_style: ws.text,
}
}
}
impl ThemeStyle<CheckboxStyle> for DefaultStyle {
fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> CheckboxStyle {
let widget_visuals = Widgets::dark();
let spacing = Spacing::default();
let widget_visuals = match state {
WidgetState::Noninteractive => widget_visuals.noninteractive,
WidgetState::Inactive => widget_visuals.inactive,
WidgetState::Hovered => widget_visuals.hovered,
WidgetState::Active => widget_visuals.active,
};
let ws: BaseStyle = themes.get(classes, state);
CheckboxStyle {
frame: Frame::new(),
checkbox_size: spacing.icon_width,
check_size: spacing.icon_width_inner,
checkbox_frame: Frame {
fill: widget_visuals.bg_fill,
corner_radius: widget_visuals.corner_radius,
stroke: widget_visuals.bg_stroke,
..Default::default()
},
text_style: ws.text,
check_stroke: ws.stroke,
}
}
}
impl ThemeStyle<LabelStyle> for DefaultStyle {
fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> LabelStyle {
let ws: BaseStyle = themes.get(classes, state);
LabelStyle {
frame: Frame {
fill: ws.frame.fill,
inner_margin: 0.0.into(),
outer_margin: 0.0.into(),
stroke: Stroke::NONE,
shadow: Shadow::NONE,
corner_radius: 0.into(),
},
text: ws.text,
wrap_mode: TextWrapMode::Wrap,
}
}
}
impl ThemeStyle<SeparatorStyle> for DefaultStyle {
fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> SeparatorStyle {
let ws: BaseStyle = themes.get(classes, state);
SeparatorStyle {
spacing: 6.0,
stroke: ws.frame.stroke,
}
}
} }
impl Ui { impl Ui {
/// Access the installed theme plugin if there is one and fetch the requested widget style if it exist. /// Access the register theme and fetch the requested [`WidgetStyle`].
/// Fallback to the default style if not found.
/// ///
/// Requested widget style must implement [`StyleStruct`]. /// Requested widget style must implement [`WidgetStyle`].
pub fn widget_style<S: StyleStruct + Clone + 'static>( pub fn widget_style<S: WidgetStyle + Clone + 'static>(
&self, &self,
id: crate::Id, id: crate::Id,
classes: &Classes, classes: &Classes,
) -> S { ) -> S {
// Fetch the current state of the widget // Fetch the current state of the widget
let state = self let state = self
.ctx()
.read_response(id) .read_response(id)
.map(|r| r.widget_state()) .map(|r| r.widget_state())
.unwrap_or_default(); .unwrap_or_default();
if let Some(style) = self.get_style::<S>(classes, state) { self.get_widget_style::<S>(classes, state)
style
} else {
S::default_style(classes, state)
}
} }
} }
#[derive(Default)] pub struct Themes {
pub(crate) struct Themes {
themes: IdTypeMap, themes: IdTypeMap,
} }
type ThemeWrap<S> = Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>;
impl Default for Themes {
/// Register the default egui theme
fn default() -> Self {
let mut themes = IdTypeMap::default();
themes.insert_temp::<ThemeWrap<BaseStyle>>(
Id::NULL,
Arc::new(Mutex::new(Box::new(DefaultStyle))),
);
themes.insert_temp::<ThemeWrap<ButtonStyle>>(
Id::NULL,
Arc::new(Mutex::new(Box::new(DefaultStyle))),
);
Self { themes }
}
}
impl Themes { impl Themes {
/// Register a theme and the style associated /// Register a [`ThemeStyle`](crate::theme_plugin::ThemeStyle) for the specified widget [`WidgetStyle`](WidgetStyle) `S`
pub(crate) fn register<S: StyleStruct + 'static>( ///
/// Existing themes are overwritten if `force` is `true` or the new theme differs.
pub(crate) fn register<S: WidgetStyle + 'static>(
&mut self, &mut self,
theme: impl ThemeStyle<S> + Send + Sync + 'static, theme: impl ThemeStyle<S> + Send + Sync + 'static,
force: bool, force: bool,
@@ -84,10 +245,11 @@ impl Themes {
&& self && self
.themes .themes
.get_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>(Id::NULL) .get_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>(Id::NULL)
.is_some() .is_some_and(|t| t.lock().theme_type_id() == theme.theme_type_id())
{ {
return; return;
} }
self.themes self.themes
.insert_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>( .insert_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>(
Id::NULL, Id::NULL,
@@ -96,14 +258,13 @@ impl Themes {
} }
/// Fetch the style of the current theme /// Fetch the style of the current theme
pub(crate) fn get<S: StyleStruct + 'static>( pub fn get<S: WidgetStyle + 'static>(&self, classes: &Classes, state: WidgetState) -> S {
&self,
classes: &Classes,
state: WidgetState,
) -> Option<S> {
let v = self let v = self
.themes .themes
.get_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>(Id::NULL); .get_temp::<Arc<Mutex<Box<dyn ThemeStyle<S> + Send + Sync>>>>(Id::NULL);
v.map(|engine| engine.lock().style(classes, state))
v.unwrap_or_else(|| panic!("A style should be set for {:?}", std::any::type_name::<S>()))
.lock()
.style(self, classes, state)
} }
} }

View File

@@ -3,22 +3,16 @@ use std::{
fmt::{self, Debug}, fmt::{self, Debug},
}; };
use emath::Vec2; use epaint::{Color32, FontId, Stroke, text::TextWrapMode};
use epaint::{Color32, FontId, Shadow, Stroke, text::TextWrapMode};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
Frame, Response, Spacing, Style, TextBuffer as _, TextStyle, Visuals, Frame, Response, TextBuffer as _,
style::{WidgetVisuals, Widgets}, style::{WidgetVisuals, Widgets},
}; };
/// Each dedicated style must implement this trait to be used in the theme plugin system /// Each dedicated style must implement this trait to be used in the theme plugin system
pub trait StyleStruct: Debug + Clone + Send + Sync + std::any::Any + 'static { pub trait WidgetStyle: Debug + Clone + Send + Sync + std::any::Any + 'static {}
/// The default style for this struct based on classes and state of the widget.
fn default_style(classes: &Classes, state: WidgetState) -> Self
where
Self: Sized;
}
/// General text style /// General text style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -36,7 +30,7 @@ pub struct TextVisuals {
/// General widget style /// General widget style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WidgetStyle { pub struct BaseStyle {
pub frame: Frame, pub frame: Frame,
pub text: TextVisuals, pub text: TextVisuals,
@@ -44,36 +38,7 @@ pub struct WidgetStyle {
pub stroke: Stroke, pub stroke: Stroke,
} }
impl StyleStruct for WidgetStyle { impl WidgetStyle for BaseStyle {}
fn default_style(_classes: &Classes, state: WidgetState) -> Self {
let visuals = Widgets::dark();
let spacing = Spacing::default();
let visuals = match state {
WidgetState::Noninteractive => visuals.noninteractive,
WidgetState::Inactive => visuals.inactive,
WidgetState::Hovered => visuals.hovered,
WidgetState::Active => visuals.active,
};
Self {
frame: Frame {
fill: visuals.bg_fill,
stroke: visuals.bg_stroke,
corner_radius: visuals.corner_radius,
inner_margin: spacing.button_padding.into(),
..Default::default()
},
stroke: visuals.fg_stroke,
text: TextVisuals {
color: visuals.text_color(),
font_id: TextStyle::Button.resolve(&Style::default()),
strikethrough: Stroke::NONE,
underline: Stroke::NONE,
},
}
}
}
/// Dedicated button style /// Dedicated button style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -82,43 +47,7 @@ pub struct ButtonStyle {
pub text_style: TextVisuals, pub text_style: TextVisuals,
} }
impl StyleStruct for ButtonStyle { impl WidgetStyle for ButtonStyle {}
fn default_style(classes: &Classes, state: WidgetState) -> Self {
let widget_visuals = Widgets::dark();
let spacing = Spacing::default();
let mut widget_visuals = match state {
WidgetState::Noninteractive => widget_visuals.noninteractive,
WidgetState::Inactive => widget_visuals.inactive,
WidgetState::Hovered => widget_visuals.hovered,
WidgetState::Active => widget_visuals.active,
};
let mut ws = WidgetStyle::default_style(classes, state);
if classes.has(SELECTED_CLASS) {
let visuals = Visuals::default();
widget_visuals.weak_bg_fill = visuals.selection.bg_fill;
widget_visuals.bg_fill = visuals.selection.bg_fill;
widget_visuals.fg_stroke = visuals.selection.stroke;
ws.text.color = visuals.selection.stroke.color;
}
Self {
frame: Frame {
fill: widget_visuals.weak_bg_fill,
stroke: widget_visuals.bg_stroke,
corner_radius: widget_visuals.corner_radius,
outer_margin: (-Vec2::splat(widget_visuals.expansion)).into(),
inner_margin: (spacing.button_padding + Vec2::splat(widget_visuals.expansion)
- Vec2::splat(widget_visuals.bg_stroke.width))
.into(),
..Default::default()
},
text_style: ws.text,
}
}
}
/// Dedicated checkbox style /// Dedicated checkbox style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -142,35 +71,7 @@ pub struct CheckboxStyle {
pub check_stroke: Stroke, pub check_stroke: Stroke,
} }
impl StyleStruct for CheckboxStyle { impl WidgetStyle for CheckboxStyle {}
fn default_style(classes: &Classes, state: WidgetState) -> Self {
let widget_visuals = Widgets::dark();
let spacing = Spacing::default();
let widget_visuals = match state {
WidgetState::Noninteractive => widget_visuals.noninteractive,
WidgetState::Inactive => widget_visuals.inactive,
WidgetState::Hovered => widget_visuals.hovered,
WidgetState::Active => widget_visuals.active,
};
let ws = WidgetStyle::default_style(classes, state);
Self {
frame: Frame::new(),
checkbox_size: spacing.icon_width,
check_size: spacing.icon_width_inner,
checkbox_frame: Frame {
fill: widget_visuals.bg_fill,
corner_radius: widget_visuals.corner_radius,
stroke: widget_visuals.bg_stroke,
..Default::default()
},
text_style: ws.text,
check_stroke: ws.stroke,
}
}
}
/// Dedicated label style /// Dedicated label style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -185,6 +86,8 @@ pub struct LabelStyle {
pub wrap_mode: TextWrapMode, pub wrap_mode: TextWrapMode,
} }
impl WidgetStyle for LabelStyle {}
/// Dedicated separator style /// Dedicated separator style
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SeparatorStyle { pub struct SeparatorStyle {
@@ -195,6 +98,8 @@ pub struct SeparatorStyle {
pub stroke: Stroke, pub stroke: Stroke,
} }
impl WidgetStyle for SeparatorStyle {}
/// The different state of a widget can be /// The different state of a widget can be
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum WidgetState { pub enum WidgetState {
@@ -231,109 +136,6 @@ impl Response {
} }
} }
impl Style {
/// The general widget style. The style is computed according to the classes and state of the widget.
pub fn widget_style(&self, _classes: &Classes, state: WidgetState) -> WidgetStyle {
let visuals = self.visuals.widgets.state(state);
let font_id = self.override_font_id.clone();
WidgetStyle {
frame: Frame {
fill: visuals.bg_fill,
stroke: visuals.bg_stroke,
corner_radius: visuals.corner_radius,
inner_margin: self.spacing.button_padding.into(),
..Default::default()
},
stroke: visuals.fg_stroke,
text: TextVisuals {
color: self
.visuals
.override_text_color
.unwrap_or_else(|| visuals.text_color()),
font_id: font_id.unwrap_or_else(|| TextStyle::Body.resolve(self)),
strikethrough: Stroke::NONE,
underline: Stroke::NONE,
},
}
}
/// The dedicated button style. The style is computed according to the classes and state of the widget.
/// It depend on the general widget style.
pub fn button_style(&self, classes: &Classes, state: WidgetState) -> ButtonStyle {
let mut visuals = *self.visuals.widgets.state(state);
let mut ws = self.widget_style(classes, state);
if classes.has(SELECTED_CLASS) {
visuals.weak_bg_fill = self.visuals.selection.bg_fill;
visuals.bg_fill = self.visuals.selection.bg_fill;
visuals.fg_stroke = self.visuals.selection.stroke;
ws.text.color = self.visuals.selection.stroke.color;
}
ButtonStyle {
frame: Frame {
fill: visuals.weak_bg_fill,
stroke: visuals.bg_stroke,
corner_radius: visuals.corner_radius,
outer_margin: (-Vec2::splat(visuals.expansion)).into(),
inner_margin: (self.spacing.button_padding + Vec2::splat(visuals.expansion)
- Vec2::splat(visuals.bg_stroke.width))
.into(),
..Default::default()
},
text_style: ws.text,
}
}
/// The dedicated checkbox style. The style is computed according to the classes and state of the widget.
/// It depend on the general widget style.
pub fn checkbox_style(&self, classes: &Classes, state: WidgetState) -> CheckboxStyle {
let visuals = self.visuals.widgets.state(state);
let ws = self.widget_style(classes, state);
CheckboxStyle {
frame: Frame::new(),
checkbox_size: self.spacing.icon_width,
check_size: self.spacing.icon_width_inner,
checkbox_frame: Frame {
fill: visuals.bg_fill,
corner_radius: visuals.corner_radius,
stroke: visuals.bg_stroke,
..Default::default()
},
text_style: ws.text,
check_stroke: ws.stroke,
}
}
/// The dedicated label style. The style is computed according to the classes and state of the widget.
/// It depend on the general widget style.
pub fn label_style(&self, classes: &Classes, state: WidgetState) -> LabelStyle {
let ws = self.widget_style(classes, state);
LabelStyle {
frame: Frame {
fill: ws.frame.fill,
inner_margin: 0.0.into(),
outer_margin: 0.0.into(),
stroke: Stroke::NONE,
shadow: Shadow::NONE,
corner_radius: 0.into(),
},
text: ws.text,
wrap_mode: TextWrapMode::Wrap,
}
}
/// The dedicated separator style. The style is computed according to the classes and state of the widget.
/// It depend on the general widget style.
pub fn separator_style(&self, _classes: &Classes, _state: WidgetState) -> SeparatorStyle {
let visuals = self.visuals.noninteractive();
SeparatorStyle {
spacing: 6.0,
stroke: visuals.bg_stroke,
}
}
}
/// The root class is a special class present on every top-level [`crate::Ui`]. /// The root class is a special class present on every top-level [`crate::Ui`].
pub const ROOT_CLASS: &str = "root"; pub const ROOT_CLASS: &str = "root";

View File

@@ -328,7 +328,6 @@ impl<'a> Button<'a> {
classes.add_class_if(SELECTED_CLASS, selected.unwrap_or(false)); classes.add_class_if(SELECTED_CLASS, selected.unwrap_or(false));
// let ButtonStyle { frame, text_style } = ui.style().button_style(&classes, state);
let ButtonStyle { frame, text_style } = ui.widget_style(id, &classes); let ButtonStyle { frame, text_style } = ui.widget_style(id, &classes);
let mut button_padding = if has_frame_margin { let mut button_padding = if has_frame_margin {

View File

@@ -80,7 +80,7 @@ impl Widget for Checkbox<'_> {
frame, frame,
check_stroke, check_stroke,
text_style, text_style,
} = ui.style().checkbox_style(&classes, state); } = ui.widget_style(id, &classes);
let mut min_size = Vec2::splat(ui.spacing().interact_size.y); let mut min_size = Vec2::splat(ui.spacing().interact_size.y);
min_size.y = min_size.y.at_least(checkbox_size); min_size.y = min_size.y.at_least(checkbox_size);

View File

@@ -106,7 +106,7 @@ impl Widget for Separator {
let SeparatorStyle { let SeparatorStyle {
spacing: spacing_style, spacing: spacing_style,
stroke, stroke,
} = ui.style().separator_style(&classes, state); } = ui.widget_style(id, &classes);
// override the spacing if not set // override the spacing if not set
let spacing = spacing.unwrap_or(spacing_style); let spacing = spacing.unwrap_or(spacing_style);

View File

@@ -2,9 +2,9 @@ use std::collections::HashMap;
use eframe::egui::{ use eframe::egui::{
Color32, Color32,
theme_plugin::{ThemeCache, ThemeStyle}, theme_plugin::{ThemeCache, ThemeStyle, Themes},
widget_style::{ widget_style::{
ButtonStyle, Classes, HasClasses as _, StyleStruct as _, WidgetState, WidgetStyle, BaseStyle, ButtonStyle, Classes, HasClasses as _, WidgetState, WidgetStyle as _,
}, },
}; };
use logos::Logos; use logos::Logos;
@@ -79,16 +79,16 @@ impl ESSEngine {
} }
/// This implementation basically do nothing. This is only the minimum requirement with caching. /// This implementation basically do nothing. This is only the minimum requirement with caching.
impl ThemeStyle<WidgetStyle> for ESSEngine { impl ThemeStyle<BaseStyle> for ESSEngine {
fn style(&mut self, classes: &Classes, state: WidgetState) -> WidgetStyle { fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> BaseStyle {
self.cache.get(classes, state, || { let current = themes.get::<BaseStyle>(classes, state);
WidgetStyle::default_style(classes, state) self.cache
}) .get(classes, state, || BaseStyle::default_style(classes, state))
} }
} }
impl ThemeStyle<ButtonStyle> for ESSEngine { impl ThemeStyle<ButtonStyle> for ESSEngine {
fn style(&mut self, classes: &Classes, state: WidgetState) -> ButtonStyle { fn style(&mut self, themes: &Themes, classes: &Classes, state: WidgetState) -> ButtonStyle {
self.cache.get(classes, state, || { self.cache.get(classes, state, || {
let mut default = ButtonStyle::default_style(classes, state); let mut default = ButtonStyle::default_style(classes, state);
for classe in classes.list() { for classe in classes.list() {

View File

@@ -1,7 +1,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![expect(rustdoc::missing_crate_level_docs)] // it's an example #![expect(rustdoc::missing_crate_level_docs)] // it's an example
use eframe::egui::widget_style::WidgetStyle; use eframe::egui::widget_style::BaseStyle;
use eframe::egui::{ use eframe::egui::{
self, Button, Frame, Margin, Panel, UiBuilder, self, Button, Frame, Margin, Panel, UiBuilder,
widget_style::{ButtonStyle, HasClasses as _}, widget_style::{ButtonStyle, HasClasses as _},
@@ -36,8 +36,8 @@ fn main() -> eframe::Result {
eframe::run_ui_native("My egui App", options, move |ui, _frame| { eframe::run_ui_native("My egui App", options, move |ui, _frame| {
// Register the theme plugin and which style they implement // Register the theme plugin and which style they implement
if let Ok(engine) = ESSEngine::try_parse(&style_code) { if let Ok(engine) = ESSEngine::try_parse(&style_code) {
ui.add_theme::<WidgetStyle>(engine.clone()); ui.add_widget_theme::<BaseStyle>(engine.clone());
ui.add_theme::<ButtonStyle>(engine); ui.add_widget_theme::<ButtonStyle>(engine);
} }
ui.scope_builder(UiBuilder::new().with_class("body"), |ui| { ui.scope_builder(UiBuilder::new().with_class("body"), |ui| {
@@ -54,8 +54,8 @@ fn main() -> eframe::Result {
&& let Ok(engine) = ESSEngine::try_parse(&style_code) && let Ok(engine) = ESSEngine::try_parse(&style_code)
{ {
// Overwrite the current theme with the new one.clear // Overwrite the current theme with the new one.clear
ui.replace_theme::<WidgetStyle>(engine.clone()); ui.replace_widget_theme::<BaseStyle>(engine.clone());
ui.replace_theme::<ButtonStyle>(engine); ui.replace_widget_theme::<ButtonStyle>(engine);
} }
}); });
}); });