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

Add some more helpers

This commit is contained in:
Lucas Meurer
2026-08-24 15:27:29 +02:00
parent 487c8f1dda
commit 5c08eb5684
3 changed files with 65 additions and 2 deletions

View File

@@ -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<ClassName>) -> 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<T>(&self, from_classes: impl Fn(&Classes) -> Option<T>) -> Option<T> {
self.iter().find_map(|node| from_classes(&node.classes))
}
}
// ----------------------------------------------------------------------------

View File

@@ -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<T>(&self, from_class_name: impl Fn(&str) -> Option<T>) -> Option<T> {
self.as_slice()
.iter()
.rev()
.find_map(|class| from_class_name(class))
}
}

View File

@@ -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<ClassName>) -> 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<T>(&self, from_classes: impl Fn(&Classes) -> Option<T>) -> Option<T> {
from_classes(self.classes).or_else(|| self.stack.inherited(from_classes))
}
}