From 887b8e2fe370137060f5ab283dbcaf2d3d5e9140 Mon Sep 17 00:00:00 2001 From: adrien <221212@umons.ac.be> Date: Wed, 25 Feb 2026 14:14:12 +0100 Subject: [PATCH] Transfer to UiStack & rename to Classes --- crates/egui/src/containers/panel.rs | 4 - crates/egui/src/ui.rs | 51 +----- crates/egui/src/ui_builder.rs | 10 +- crates/egui/src/ui_stack.rs | 2 + crates/egui/src/widget_style.rs | 253 +++++++++------------------ crates/egui/src/widgets/button.rs | 23 ++- crates/egui/src/widgets/checkbox.rs | 21 ++- crates/egui/src/widgets/separator.rs | 21 ++- examples/styling_engine/src/main.rs | 77 +++++--- 9 files changed, 183 insertions(+), 279 deletions(-) diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 833c51ce9..6281e6b41 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -15,8 +15,6 @@ //! //! Add your [`crate::Window`]:s after any top-level panels. -use std::sync::Arc; - use emath::{GuiRounding as _, Pos2}; use crate::{ @@ -1029,12 +1027,10 @@ impl CentralPanel { .max_rect(panel_rect) .layout(Layout::top_down(Align::Min)), ); - let style_stack = ui.style_stack_mut().clone(); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) let frame = frame.unwrap_or_else(|| Frame::central_panel(ui.style())); let response = frame.show(&mut panel_ui, |ui| { - ui.style_stack_mut().parent = Some(Arc::new(style_stack)); ui.expand_to_include_rect(ui.max_rect()); // Expand frame to include it all add_contents(ui) }); diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 05507fff6..e7905c87d 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -7,7 +7,6 @@ use emath::GuiRounding as _; use epaint::mutex::RwLock; use crate::containers::menu; -use crate::widget_style::{HasModifiers, StyleModifiers, StyleStack}; use crate::{containers::*, ecolor::*, layout::*, placer::Placer, widgets::*, *}; // ---------------------------------------------------------------------------- @@ -90,9 +89,6 @@ pub struct Ui { /// This is an optimization, so we don't call [`Ui::remember_min_rect`] multiple times at the /// end of a [`Ui::scope`]. min_rect_already_remembered: bool, - - /// test - style_stack: Arc, } /// Allow using [`Ui`] like a [`Context`]. @@ -127,6 +123,7 @@ impl Ui { style, sense, accessibility_parent, + classes: modifiers, } = ui_builder; let layer_id = layer_id.unwrap_or_else(LayerId::background); @@ -142,6 +139,7 @@ impl Ui { let disabled = disabled || invisible; let style = style.unwrap_or_else(|| ctx.global_style()); let sense = sense.unwrap_or_else(Sense::hover); + let modifiers = modifiers.unwrap_or_default(); // Temporary use of user tags as proof of concept ui_stack_info = ui_stack_info.with_tag("root"); @@ -154,11 +152,7 @@ impl Ui { parent: None, min_rect: placer.min_rect(), max_rect: placer.max_rect(), - }; - - let style_stack = StyleStack { - modifiers: StyleModifiers::default(), - parent: None, + classes: modifiers, }; let mut ui = Ui { @@ -174,7 +168,6 @@ impl Ui { stack: Arc::new(ui_stack), sense, min_rect_already_remembered: false, - style_stack: Arc::new(style_stack), }; if let Some(accessibility_parent) = accessibility_parent { @@ -275,6 +268,7 @@ impl Ui { style, sense, accessibility_parent, + classes: modifiers, } = ui_builder; let mut painter = self.painter.clone(); @@ -292,6 +286,7 @@ impl Ui { let sizing_pass = self.sizing_pass || sizing_pass; let style = style.unwrap_or_else(|| Arc::clone(&self.style)); let sense = sense.unwrap_or_else(Sense::hover); + let modifiers = modifiers.unwrap_or_default(); if sizing_pass { // During the sizing pass we want widgets to use up as little space as possible, @@ -323,11 +318,7 @@ impl Ui { parent: Some(Arc::clone(&self.stack)), min_rect: placer.min_rect(), max_rect: placer.max_rect(), - }; - - let style_stack = StyleStack { - modifiers: StyleModifiers::default(), - parent: Some(Arc::clone(&self.style_stack)), + classes: modifiers, }; let mut child_ui = Ui { @@ -343,7 +334,6 @@ impl Ui { stack: Arc::new(ui_stack), sense, min_rect_already_remembered: false, - style_stack: Arc::new(style_stack), }; if disabled { @@ -3166,35 +3156,6 @@ impl Drop for Ui { } } -impl HasModifiers for Ui { - fn modifiers(&self) -> &StyleModifiers { - &self.style_stack.modifiers - } - - fn modifiers_mut(&mut self) -> &mut StyleModifiers { - &mut self.style_stack_mut().modifiers - } -} - -impl Ui { - /// borrow internal [`StyleStack`]. - /// Allow the access to the modifiers of the ui's ancestors - /// - /// Example: - /// ``` - /// # egui::__run_test_ui(|ui| { - /// ui.style_stack().parent_has("test"); - /// # }); - /// ``` - pub fn style_stack(&self) -> &StyleStack { - &self.style_stack - } - - pub(crate) fn style_stack_mut(&mut self) -> &mut StyleStack { - Arc::make_mut(&mut self.style_stack) - } -} - /// Show this rectangle to the user if certain debug options are set. #[cfg(debug_assertions)] fn register_rect(ui: &Ui, rect: Rect) { diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 686fdcb47..adbed444c 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -1,8 +1,8 @@ use std::{hash::Hash, sync::Arc}; -use crate::ClosableTag; #[expect(unused_imports)] // Used for doclinks use crate::Ui; +use crate::{ClosableTag, widget_style::Classes}; use crate::{Id, LayerId, Layout, Rect, Sense, Style, UiStackInfo}; /// Build a [`Ui`] as the child of another [`Ui`]. @@ -25,6 +25,7 @@ pub struct UiBuilder { pub style: Option>, pub sense: Option, pub accessibility_parent: Option, + pub classes: Option, } impl UiBuilder { @@ -191,4 +192,11 @@ impl UiBuilder { self.accessibility_parent = Some(parent_id); self } + + /// Set classes for this [`Ui`]. + #[inline] + pub fn classes(mut self, classes: Classes) -> Self { + self.classes = Some(classes); + self + } } diff --git a/crates/egui/src/ui_stack.rs b/crates/egui/src/ui_stack.rs index 0251c80f6..15adb8e30 100644 --- a/crates/egui/src/ui_stack.rs +++ b/crates/egui/src/ui_stack.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use std::{any::Any, iter::FusedIterator}; +use crate::widget_style::Classes; use crate::{Direction, Frame, Id, Rect}; /// What kind is this [`crate::Ui`]? @@ -210,6 +211,7 @@ pub struct UiStack { pub min_rect: Rect, pub max_rect: Rect, pub parent: Option>, + pub classes: Classes, } // these methods act on this specific node diff --git a/crates/egui/src/widget_style.rs b/crates/egui/src/widget_style.rs index 876744fc1..24add2b6d 100644 --- a/crates/egui/src/widget_style.rs +++ b/crates/egui/src/widget_style.rs @@ -1,10 +1,10 @@ -use std::{iter::FusedIterator, sync::Arc}; +use std::{borrow::Cow, fmt}; use emath::Vec2; use epaint::{Color32, FontId, Shadow, Stroke, text::TextWrapMode}; use crate::{ - Frame, Response, Style, TextStyle, Theme, + Frame, Response, Style, TextBuffer as _, TextStyle, style::{WidgetVisuals, Widgets}, }; @@ -109,8 +109,8 @@ impl Response { } impl Style { - pub fn widget_style(&self, modifier: &StyleModifiers) -> WidgetStyle { - let visuals = self.visuals.widgets.state(modifier.state); + 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 { @@ -133,11 +133,11 @@ impl Style { } } - pub fn button_style(&self, modifier: &StyleModifiers) -> ButtonStyle { - let mut visuals = *self.visuals.widgets.state(modifier.state); - let mut ws = self.widget_style(modifier); + 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 modifier.has("selected") { + if classes.has("selected") { visuals.weak_bg_fill = self.visuals.selection.bg_fill; visuals.bg_fill = self.visuals.selection.bg_fill; visuals.fg_stroke = self.visuals.selection.stroke; @@ -159,9 +159,9 @@ impl Style { } } - pub fn checkbox_style(&self, modifier: &StyleModifiers) -> CheckboxStyle { - let visuals = self.visuals.widgets.state(modifier.state); - let ws = self.widget_style(modifier); + 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, @@ -177,8 +177,8 @@ impl Style { } } - pub fn label_style(&self, modifier: &StyleModifiers) -> LabelStyle { - let ws = self.widget_style(modifier); + pub fn label_style(&self, classes: &Classes, state: WidgetState) -> LabelStyle { + let ws = self.widget_style(classes, state); LabelStyle { frame: Frame { fill: ws.frame.fill, @@ -193,7 +193,7 @@ impl Style { } } - pub fn separator_style(&self, _modifier: &StyleModifiers) -> SeparatorStyle { + pub fn separator_style(&self, _classes: &Classes, _state: WidgetState) -> SeparatorStyle { let visuals = self.visuals.noninteractive(); SeparatorStyle { spacing: 6.0, @@ -202,123 +202,93 @@ impl Style { } } -pub type WidgetStyleModifier = String; +pub type ClassName = Cow<'static, str>; -/// For now we use Vec for the modifiers but later we could use `SmallVev` for performance #[derive(Debug, Default, Clone)] -pub struct StyleModifiers { - modifiers: Vec, - theme: Option, - state: WidgetState, +pub struct Classes { + classes: Vec, } -impl StyleModifiers { - /// Add multiples modifiers in one method and return the list for method chaining - #[inline] - pub fn with_modifiers(mut self, modifiers: &[WidgetStyleModifier]) -> Self { - self.modifiers.append(&mut modifiers.to_vec()); - self - } - - /// Add a single modifier and return the list for method chaining - #[inline] - pub fn with_modifier(mut self, modifier: impl Into) -> Self { - self.modifiers.push(modifier.into()); - self - } - +impl Classes { /// Add a class to the list if the condition is true #[inline] - pub fn add_if(&mut self, modifier: impl Into, condition: bool) { + fn add_if(&mut self, class: impl Into, condition: bool) { if condition { - self.modifiers.push(modifier.into()); + self.classes.push(class.into()); } } +} + +impl HasClasses for Classes { + fn classes(&self) -> &Classes { + self + } + + fn classes_mut(&mut self) -> &mut Classes { + self + } +} + +impl std::fmt::Display for Classes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.classes.iter().for_each(|class| { + let _ = f.write_str(class.as_str()); + }); + f.write_str("") + } +} + +/// Any widgets supporting [`Classes`] must implement this trait +pub trait HasClasses { + fn classes(&self) -> &Classes; + + fn classes_mut(&mut self) -> &mut Classes; - /// Add a class to the list and return the list for method chaining #[inline] - pub fn with_if(mut self, modifier: impl Into, condition: bool) -> Self { - self.add_if(modifier.into(), condition); + fn with_class(mut self, class: impl Into) -> Self + where + Self: Sized, + { + self.classes_mut().add_if(class.into(), true); + self + } + + #[inline] + fn with_class_if(mut self, class: impl Into, condition: bool) -> Self + where + Self: Sized, + { + self.classes_mut().add_if(class.into(), condition); + self + } + + #[inline] + fn add_class(&mut self, class: impl Into) -> &mut Self + where + Self: Sized, + { + self.classes_mut().add_if(class.into(), true); + self + } + + #[inline] + fn add_class_if(&mut self, class: impl Into, condition: bool) -> &mut Self + where + Self: Sized, + { + self.classes_mut().add_if(class.into(), condition); self } /// Return true if the modifier is present in the list - pub fn has(&self, modifier: impl Into) -> bool { - self.modifiers.contains(&modifier.into()) - } - - pub fn with_theme(&mut self, theme: Theme) { - self.theme = Some(theme); - } - - pub fn with_state(&mut self, state: WidgetState) { - self.state = state; - } - - pub fn list(&self) -> Vec { - self.modifiers.clone() - } -} - -/// Any widgets supporting [`StyleModifiers`] must implement this trait -pub trait HasModifiers { - fn modifiers(&self) -> &StyleModifiers; - - fn modifiers_mut(&mut self) -> &mut StyleModifiers; - - #[inline] - fn with_modifier(mut self, modifier: impl Into) -> Self - where - Self: Sized, - { - self.modifiers_mut().add_if(modifier.into(), true); - self - } - - #[inline] - fn with_modifier_if(mut self, modifier: impl Into, condition: bool) -> Self - where - Self: Sized, - { - self.modifiers_mut().add_if(modifier.into(), condition); - self - } - - #[inline] - fn add_modifier(&mut self, modifier: impl Into) -> &mut Self - where - Self: Sized, - { - self.modifiers_mut().add_if(modifier.into(), true); - self - } - - #[inline] - fn add_modifier_if( - &mut self, - modifier: impl Into, - condition: bool, - ) -> &mut Self - where - Self: Sized, - { - self.modifiers_mut().add_if(modifier.into(), condition); - self - } - - #[inline] - fn theme(mut self, theme: Theme) -> Self - where - Self: Sized, - { - self.modifiers_mut().with_theme(theme); - self + fn has(&self, class: impl Into) -> bool { + self.classes().classes.contains(&class.into()) } } /// Add a shortcut to add modifiers. The syntax is `add_modifiers`!(Name: (modifier1, modifier2, modifier3,)) for any number of modifiers #[macro_export] -macro_rules! add_modifiers { +macro_rules! define_modifiers { ($trait_name:ident: ($( $name:ident )+),?) => { pub trait $trait_name { @@ -333,67 +303,8 @@ macro_rules! add_modifiers { { #[inline] $(fn $name(mut self) -> Self { - self.with_modifier(stringify!($name)) + self.with_class(stringify!($name)) })? } }; } - -#[derive(Debug, Clone)] -pub struct StyleStack { - pub modifiers: StyleModifiers, - pub parent: Option>, -} - -// these methods act on the entire stack -impl StyleStack { - /// Return an iterator that walks the stack from this node to the root. - #[expect(clippy::iter_without_into_iter)] - pub fn iter(&self) -> StyleStackIterator<'_> { - StyleStackIterator { next: Some(self) } - } - - /// Check if any of the ancestor has the modifiers - pub fn ancestors_have(&self, modifier: impl Into) -> bool { - let modifier = modifier.into(); - self.iter() - .any(|parent| parent.modifiers.has(modifier.clone())) - } - - /// Check if the direct parent has the modifiers - pub fn parent_has(&self, modifier: impl Into) -> bool { - if let Some(parent) = &self.parent { - parent.modifiers.has(modifier) - } else { - false - } - } - - pub fn parent_modifiers(&self) -> Option<&StyleModifiers> { - self.parent.as_ref().map(|parent| &parent.modifiers) - } - - pub fn ancestors_modifiers(&self) -> Vec<&StyleModifiers> { - self.iter().map(|f| &f.modifiers).collect() - } -} - -/// Iterator that walks up a stack of `StackFrame`s. -/// -/// See [`StyleStack::iter`]. -pub struct StyleStackIterator<'a> { - next: Option<&'a StyleStack>, -} - -impl<'a> Iterator for StyleStackIterator<'a> { - type Item = &'a StyleStack; - - #[inline] - fn next(&mut self) -> Option { - let current = self.next; - self.next = current.and_then(|style| style.parent.as_deref()); - current - } -} - -impl FusedIterator for StyleStackIterator<'_> {} diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 6c6f3e582..c26ae3096 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -4,7 +4,7 @@ use crate::{ Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame, Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType, - widget_style::{ButtonStyle, HasModifiers, StyleModifiers, WidgetState}, + widget_style::{ButtonStyle, Classes, HasClasses, WidgetState}, }; /// Clickable button with text. @@ -38,7 +38,7 @@ pub struct Button<'a> { selected: bool, image_tint_follows_text_color: bool, limit_image_size: bool, - modifiers: StyleModifiers, + classes: Classes, } impl<'a> Button<'a> { @@ -57,7 +57,7 @@ impl<'a> Button<'a> { selected: false, image_tint_follows_text_color: false, limit_image_size: false, - modifiers: StyleModifiers::default(), + classes: Classes::default(), } } @@ -275,7 +275,7 @@ impl<'a> Button<'a> { selected, image_tint_follows_text_color, limit_image_size, - mut modifiers, + mut classes, } = self; // Min size height always equal or greater than interact size if not small @@ -300,11 +300,10 @@ impl<'a> Button<'a> { let id = ui.next_auto_id(); let response: Option = ui.ctx().read_response(id); let state = response.map(|r| r.widget_state()).unwrap_or_default(); - modifiers.with_state(state); - modifiers.add_if("selected", selected); + classes.add_class_if("selected", selected); - let ButtonStyle { frame, text_style } = ui.style().button_style(&modifiers); + let ButtonStyle { frame, text_style } = ui.style().button_style(&classes, state); let mut button_padding = if has_frame_margin { frame.inner_margin @@ -376,12 +375,12 @@ impl Widget for Button<'_> { } } -impl HasModifiers for Button<'_> { - fn modifiers(&self) -> &StyleModifiers { - &self.modifiers +impl HasClasses for Button<'_> { + fn classes(&self) -> &Classes { + &self.classes } - fn modifiers_mut(&mut self) -> &mut StyleModifiers { - &mut self.modifiers + fn classes_mut(&mut self) -> &mut Classes { + &mut self.classes } } diff --git a/crates/egui/src/widgets/checkbox.rs b/crates/egui/src/widgets/checkbox.rs index 942a8cb68..194bc0688 100644 --- a/crates/egui/src/widgets/checkbox.rs +++ b/crates/egui/src/widgets/checkbox.rs @@ -3,7 +3,7 @@ use emath::Rect; use crate::{ Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget, WidgetInfo, WidgetType, epaint, pos2, - widget_style::{CheckboxStyle, HasModifiers, StyleModifiers}, + widget_style::{CheckboxStyle, Classes, HasClasses}, }; // TODO(emilk): allow checkbox without a text label @@ -24,7 +24,7 @@ pub struct Checkbox<'a> { checked: &'a mut bool, atoms: Atoms<'a>, indeterminate: bool, - modifiers: StyleModifiers, + classes: Classes, } impl<'a> Checkbox<'a> { @@ -33,7 +33,7 @@ impl<'a> Checkbox<'a> { checked, atoms: atoms.into_atoms(), indeterminate: false, - modifiers: StyleModifiers::default(), + classes: Classes::default(), } } @@ -58,14 +58,13 @@ impl Widget for Checkbox<'_> { checked, mut atoms, indeterminate, - modifiers: mut modifier, + classes, } = self; // Get the widget style by reading the response from the previous pass let id = ui.next_auto_id(); let response: Option = ui.ctx().read_response(id); let state = response.map(|r| r.widget_state()).unwrap_or_default(); - modifier.with_state(state); let CheckboxStyle { check_size, @@ -74,7 +73,7 @@ impl Widget for Checkbox<'_> { frame, check_stroke, text_style, - } = ui.style().checkbox_style(&modifier); + } = ui.style().checkbox_style(&classes, state); let mut min_size = Vec2::splat(ui.spacing().interact_size.y); min_size.y = min_size.y.at_least(checkbox_size); @@ -159,12 +158,12 @@ impl Widget for Checkbox<'_> { } } -impl HasModifiers for Checkbox<'_> { - fn modifiers(&self) -> &crate::widget_style::StyleModifiers { - &self.modifiers +impl HasClasses for Checkbox<'_> { + fn classes(&self) -> &Classes { + &self.classes } - fn modifiers_mut(&mut self) -> &mut crate::widget_style::StyleModifiers { - &mut self.modifiers + fn classes_mut(&mut self) -> &mut Classes { + &mut self.classes } } diff --git a/crates/egui/src/widgets/separator.rs b/crates/egui/src/widgets/separator.rs index 082be07a4..48d02fe8e 100644 --- a/crates/egui/src/widgets/separator.rs +++ b/crates/egui/src/widgets/separator.rs @@ -1,6 +1,6 @@ use crate::{ Response, Sense, Ui, Vec2, Widget, vec2, - widget_style::{HasModifiers, SeparatorStyle, StyleModifiers}, + widget_style::{Classes, HasClasses, SeparatorStyle}, }; /// A visual separator. A horizontal or vertical line (depending on [`crate::Layout`]). @@ -19,7 +19,7 @@ pub struct Separator { spacing: Option, grow: f32, is_horizontal_line: Option, - modifiers: StyleModifiers, + classes: Classes, } impl Default for Separator { @@ -28,7 +28,7 @@ impl Default for Separator { spacing: None, grow: 0.0, is_horizontal_line: None, - modifiers: StyleModifiers::default(), + classes: Classes::default(), } } } @@ -96,18 +96,17 @@ impl Widget for Separator { spacing, grow, is_horizontal_line, - modifiers: mut modifier, + classes, } = self; // Get the widget style by reading the response from the previous pass let id = ui.next_auto_id(); let response: Option = ui.ctx().read_response(id); let state = response.map(|r| r.widget_state()).unwrap_or_default(); - modifier.with_state(state); let SeparatorStyle { spacing: spacing_style, stroke, - } = ui.style().separator_style(&modifier); + } = ui.style().separator_style(&classes, state); // override the spacing if not set let spacing = spacing.unwrap_or(spacing_style); @@ -150,12 +149,12 @@ impl Widget for Separator { } } -impl HasModifiers for Separator { - fn modifiers(&self) -> &crate::widget_style::StyleModifiers { - &self.modifiers +impl HasClasses for Separator { + fn classes(&self) -> &Classes { + &self.classes } - fn modifiers_mut(&mut self) -> &mut crate::widget_style::StyleModifiers { - &mut self.modifiers + fn classes_mut(&mut self) -> &mut Classes { + &mut self.classes } } diff --git a/examples/styling_engine/src/main.rs b/examples/styling_engine/src/main.rs index 2446c3385..eb311fde5 100644 --- a/examples/styling_engine/src/main.rs +++ b/examples/styling_engine/src/main.rs @@ -1,7 +1,10 @@ #![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 -use eframe::egui::{self, Button, Frame, Grid, Margin, Panel, widget_style::HasModifiers as _}; +use eframe::egui::{ + self, Frame, Grid, Margin, Panel, UiBuilder, + widget_style::{Classes, HasClasses as _}, +}; fn main() -> eframe::Result { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). @@ -14,31 +17,57 @@ fn main() -> eframe::Result { let mut style_code = "// future style code live editor".to_owned(); eframe::run_ui_native("My egui App", options, move |ui, _frame| { - // Add a modifier to this ui - ui.add_modifier("body"); - ui.label("body"); - egui::CentralPanel::default().show_inside(ui, |ui| { - // Add a modifier to this ui - ui.add_modifier("central panel"); - ui.label("central panel"); + ui.scope_builder( + UiBuilder::new().classes(Classes::default().with_class("body")), + |ui| { + ui.label("body"); + ui.label("central panel"); - Panel::left("style_code").show_inside(ui, |ui| { - ui.add_modifier("style code editor"); - ui.label("style code editor"); + Panel::left("style_code").show_inside(ui, |ui| { + ui.scope_builder( + UiBuilder::new().classes(Classes::default().with_class("panel_left")), + |ui| { + ui.label("style code editor"); - ui.text_edit_multiline(&mut style_code); - }); + ui.text_edit_multiline(&mut style_code); + }, + ); + }); - Grid::new("grid").show(ui, |ui| { - ui.add_modifier("grid"); - - Frame::new().inner_margin(Margin::same(10)).show(ui, |ui| { - ui.add_modifier("frame1"); - - ui.add(Button::new("button1").with_modifier("button1")); - ui.add(Button::new("button2").with_modifier("button2")); - }) - }); - }); + Grid::new("grid").show(ui, |ui| { + ui.scope_builder( + UiBuilder::new().classes(Classes::default().with_class("grid")), + |ui| { + Frame::new().inner_margin(Margin::same(10)).show(ui, |ui| { + ui.scope_builder( + UiBuilder::new() + .classes(Classes::default().with_class("frame1")), + |ui| { + let mut parent = Some(ui.stack()); + let mut text = vec![]; + let mut i: i32 = 0; + while let Some(p) = parent { + text.push(format!( + "{}{}class : '{}', kind : {:?}", + " ".repeat((2 * 0_i32.max(i - 1)) as usize), + if i > 0 { "\\- " } else { "" }, + p.classes, + p.kind() + )); + i += 1; + parent = p.parent.as_ref(); + } + ui.label(format!( + "Current hierarchy :\n{}", + text.join("\n") + )); + }, + ) + }) + }, + ); + }); + }, + ); }) }