diff --git a/crates/egui/src/ui_stack.rs b/crates/egui/src/ui_stack.rs index f6b4865be..d2426611a 100644 --- a/crates/egui/src/ui_stack.rs +++ b/crates/egui/src/ui_stack.rs @@ -1,7 +1,7 @@ use core::{any::Any, iter::FusedIterator}; use std::sync::Arc; -use crate::widget_style::Classes; +use crate::widget_style::{ClassName, Classes, HasClasses as _}; use epaint::Color32; use crate::{Direction, Frame, Id, Rect}; @@ -290,6 +290,21 @@ impl UiStack { pub fn contained_in(&self, kind: UiKind) -> bool { self.iter().any(|frame| frame.kind() == Some(kind)) } + + /// Does this node, or any [`crate::Ui`] up the stack, carry this class? + pub fn has_class(&self, class: impl Into) -> bool { + let class = class.into(); + self.iter().any(|node| node.classes.has(class.clone())) + } + + /// Read a value from the classes up the stack, nearest node first. + /// + /// This is how an inherited property works in a stylesheet: a container states it once, and + /// everything inside picks it up. The nearest [`crate::Ui`] wins, so an inner container can + /// override an outer one. + pub fn inherited(&self, from_classes: impl Fn(&Classes) -> Option) -> Option { + self.iter().find_map(|node| from_classes(&node.classes)) + } } // ---------------------------------------------------------------------------- diff --git a/crates/egui/src/widget_style/classes.rs b/crates/egui/src/widget_style/classes.rs index 77f2943f8..59a9c2d53 100644 --- a/crates/egui/src/widget_style/classes.rs +++ b/crates/egui/src/widget_style/classes.rs @@ -121,4 +121,15 @@ pub trait HasClasses { fn as_slice(&self) -> &[ClassName] { &self.classes().classes } + + /// The last class that names a `T`, if any. + /// + /// Scanning backwards is what makes the last class win, so a widget given two classes of the + /// same kind takes the one set last — the way a later stylesheet rule overrides an earlier one. + fn last_class(&self, from_class_name: impl Fn(&str) -> Option) -> Option { + self.as_slice() + .iter() + .rev() + .find_map(|class| from_class_name(class)) + } } diff --git a/crates/egui/src/widget_style/mod.rs b/crates/egui/src/widget_style/mod.rs index ea2e234ab..8201735a5 100644 --- a/crates/egui/src/widget_style/mod.rs +++ b/crates/egui/src/widget_style/mod.rs @@ -14,7 +14,7 @@ use emath::Vec2; use epaint::{Color32, FontId, Stroke, text::TextWrapMode}; use crate::{ - Context, Frame, Response, Style, UiStack, + Context, Frame, Response, Style, TextStyle, UiStack, style::{WidgetVisuals, Widgets}, }; @@ -35,6 +35,24 @@ pub struct TextVisuals { pub strikethrough: Stroke, } +impl TextVisuals { + /// The undecorated body text of a [`Style`], as a starting point for a widget's own text. + /// + /// The color is the style's ordinary text color; a widget that paints its text in a color of + /// its own overrides it. + pub fn from_style(style: &Style) -> Self { + Self { + color: style.visuals.text_color(), + font_id: style + .override_font_id + .clone() + .unwrap_or_else(|| TextStyle::Body.resolve(style)), + underline: Stroke::NONE, + strikethrough: Stroke::NONE, + } + } +} + /// General widget style #[derive(Debug, Clone)] pub struct BaseStyle { @@ -176,3 +194,22 @@ pub struct StyleArgs<'a> { pub style: &'a Style, pub ctx: &'a Context, } + +impl StyleArgs<'_> { + /// Does the widget, or any [`crate::Ui`] it sits in, carry this class? + /// + /// This is the equivalent of a descendant selector (`.parent .child`): it lets a container + /// style everything inside it without having to touch each widget. Use + /// [`HasClasses::has`] on [`Self::classes`] instead for a class only ever set on a widget. + pub fn has_class(&self, class: impl Into) -> bool { + let class = class.into(); + self.classes.has(class.clone()) || self.stack.has_class(class) + } + + /// Read a value from the widget's own classes, falling back to the [`crate::Ui`]s it sits in. + /// + /// See [`UiStack::inherited`] for how the fallback resolves. + pub fn inherited(&self, from_classes: impl Fn(&Classes) -> Option) -> Option { + from_classes(self.classes).or_else(|| self.stack.inherited(from_classes)) + } +}