mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Add Classes to UiBuilder and some Widgets (#7843)
* Closes part of <https://github.com/emilk/egui/issues/3284> Add a class system toward a CSS-like styling. Widget and Ui can implement the trait `HasClasses` which can be used later by theme engines to compute a style based on the set of classes the component has. --------- Co-authored-by: adrien <221212@umons.ac.be> Co-authored-by: Adrien Zianne <adrien@iq002.ipa.iqrypto.com> Co-authored-by: lucasmerlin <hi@lucasmerlin.me>
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
use std::{any::Any, hash::Hash, ops::Deref, sync::Arc};
|
use std::{any::Any, hash::Hash, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use crate::containers::menu;
|
use crate::containers::menu;
|
||||||
|
use crate::widget_style::{HasClasses as _, ROOT_CLASS};
|
||||||
use crate::{containers::*, ecolor::*, layout::*, placer::Placer, widgets::*, *};
|
use crate::{containers::*, ecolor::*, layout::*, placer::Placer, widgets::*, *};
|
||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -117,6 +118,7 @@ impl Ui {
|
|||||||
style,
|
style,
|
||||||
sense,
|
sense,
|
||||||
accessibility_parent,
|
accessibility_parent,
|
||||||
|
classes,
|
||||||
} = ui_builder;
|
} = ui_builder;
|
||||||
|
|
||||||
let layer_id = layer_id.unwrap_or_else(LayerId::background);
|
let layer_id = layer_id.unwrap_or_else(LayerId::background);
|
||||||
@@ -132,6 +134,7 @@ impl Ui {
|
|||||||
let disabled = disabled || invisible;
|
let disabled = disabled || invisible;
|
||||||
let style = style.unwrap_or_else(|| ctx.global_style());
|
let style = style.unwrap_or_else(|| ctx.global_style());
|
||||||
let sense = sense.unwrap_or_else(Sense::hover);
|
let sense = sense.unwrap_or_else(Sense::hover);
|
||||||
|
let classes = classes.with_class(ROOT_CLASS);
|
||||||
|
|
||||||
let placer = Placer::new(max_rect, layout);
|
let placer = Placer::new(max_rect, layout);
|
||||||
let ui_stack = UiStack {
|
let ui_stack = UiStack {
|
||||||
@@ -141,7 +144,9 @@ impl Ui {
|
|||||||
parent: None,
|
parent: None,
|
||||||
min_rect: placer.min_rect(),
|
min_rect: placer.min_rect(),
|
||||||
max_rect: placer.max_rect(),
|
max_rect: placer.max_rect(),
|
||||||
|
classes,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ui = Ui {
|
let mut ui = Ui {
|
||||||
id,
|
id,
|
||||||
unique_id: id,
|
unique_id: id,
|
||||||
@@ -214,6 +219,7 @@ impl Ui {
|
|||||||
style,
|
style,
|
||||||
sense,
|
sense,
|
||||||
accessibility_parent,
|
accessibility_parent,
|
||||||
|
classes,
|
||||||
} = ui_builder;
|
} = ui_builder;
|
||||||
|
|
||||||
let mut painter = self.painter.clone();
|
let mut painter = self.painter.clone();
|
||||||
@@ -262,7 +268,9 @@ impl Ui {
|
|||||||
parent: Some(Arc::clone(&self.stack)),
|
parent: Some(Arc::clone(&self.stack)),
|
||||||
min_rect: placer.min_rect(),
|
min_rect: placer.min_rect(),
|
||||||
max_rect: placer.max_rect(),
|
max_rect: placer.max_rect(),
|
||||||
|
classes,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut child_ui = Ui {
|
let mut child_ui = Ui {
|
||||||
id: stable_id,
|
id: stable_id,
|
||||||
unique_id,
|
unique_id,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use std::{hash::Hash, sync::Arc};
|
use std::{hash::Hash, sync::Arc};
|
||||||
|
|
||||||
use crate::ClosableTag;
|
|
||||||
#[expect(unused_imports)] // Used for doclinks
|
#[expect(unused_imports)] // Used for doclinks
|
||||||
use crate::Ui;
|
use crate::Ui;
|
||||||
|
use crate::widget_style::HasClasses;
|
||||||
|
use crate::{ClosableTag, widget_style::Classes};
|
||||||
use crate::{Id, LayerId, Layout, Rect, Sense, Style, UiStackInfo};
|
use crate::{Id, LayerId, Layout, Rect, Sense, Style, UiStackInfo};
|
||||||
|
|
||||||
/// Build a [`Ui`] as the child of another [`Ui`].
|
/// Build a [`Ui`] as the child of another [`Ui`].
|
||||||
@@ -25,6 +26,7 @@ pub struct UiBuilder {
|
|||||||
pub style: Option<Arc<Style>>,
|
pub style: Option<Arc<Style>>,
|
||||||
pub sense: Option<Sense>,
|
pub sense: Option<Sense>,
|
||||||
pub accessibility_parent: Option<Id>,
|
pub accessibility_parent: Option<Id>,
|
||||||
|
pub classes: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiBuilder {
|
impl UiBuilder {
|
||||||
@@ -192,3 +194,13 @@ impl UiBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasClasses for UiBuilder {
|
||||||
|
fn classes(&self) -> &Classes {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classes_mut(&mut self) -> &mut Classes {
|
||||||
|
&mut self.classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{any::Any, iter::FusedIterator};
|
use std::{any::Any, iter::FusedIterator};
|
||||||
|
|
||||||
|
use crate::widget_style::Classes;
|
||||||
use epaint::Color32;
|
use epaint::Color32;
|
||||||
|
|
||||||
use crate::{Direction, Frame, Id, Rect};
|
use crate::{Direction, Frame, Id, Rect};
|
||||||
@@ -212,6 +213,7 @@ pub struct UiStack {
|
|||||||
pub min_rect: Rect,
|
pub min_rect: Rect,
|
||||||
pub max_rect: Rect,
|
pub max_rect: Rect,
|
||||||
pub parent: Option<Arc<Self>>,
|
pub parent: Option<Arc<Self>>,
|
||||||
|
pub classes: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
// these methods act on this specific node
|
// these methods act on this specific node
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
use std::{borrow::Cow, fmt};
|
||||||
|
|
||||||
use emath::Vec2;
|
use emath::Vec2;
|
||||||
use epaint::{Color32, FontId, Shadow, Stroke, text::TextWrapMode};
|
use epaint::{Color32, FontId, Shadow, Stroke, text::TextWrapMode};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Frame, Response, Style, TextStyle,
|
Frame, Response, Style, TextBuffer as _, TextStyle,
|
||||||
style::{WidgetVisuals, Widgets},
|
style::{WidgetVisuals, Widgets},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,11 +31,13 @@ pub struct WidgetStyle {
|
|||||||
pub stroke: Stroke,
|
pub stroke: Stroke,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dedicated button style
|
||||||
pub struct ButtonStyle {
|
pub struct ButtonStyle {
|
||||||
pub frame: Frame,
|
pub frame: Frame,
|
||||||
pub text_style: TextVisuals,
|
pub text_style: TextVisuals,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dedicated checkbox style
|
||||||
pub struct CheckboxStyle {
|
pub struct CheckboxStyle {
|
||||||
/// Frame around
|
/// Frame around
|
||||||
pub frame: Frame,
|
pub frame: Frame,
|
||||||
@@ -53,6 +58,7 @@ pub struct CheckboxStyle {
|
|||||||
pub check_stroke: Stroke,
|
pub check_stroke: Stroke,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dedicated label style
|
||||||
pub struct LabelStyle {
|
pub struct LabelStyle {
|
||||||
/// Frame around
|
/// Frame around
|
||||||
pub frame: Frame,
|
pub frame: Frame,
|
||||||
@@ -64,6 +70,7 @@ pub struct LabelStyle {
|
|||||||
pub wrap_mode: TextWrapMode,
|
pub wrap_mode: TextWrapMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dedicated separator style
|
||||||
pub struct SeparatorStyle {
|
pub struct SeparatorStyle {
|
||||||
/// How much space is allocated in the layout direction
|
/// How much space is allocated in the layout direction
|
||||||
pub spacing: f32,
|
pub spacing: f32,
|
||||||
@@ -72,6 +79,7 @@ pub struct SeparatorStyle {
|
|||||||
pub stroke: Stroke,
|
pub stroke: Stroke,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The different state of a widget can be
|
||||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum WidgetState {
|
pub enum WidgetState {
|
||||||
Noninteractive,
|
Noninteractive,
|
||||||
@@ -82,6 +90,7 @@ pub enum WidgetState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widgets {
|
impl Widgets {
|
||||||
|
/// The widget visuals according to the state
|
||||||
pub fn state(&self, state: WidgetState) -> &WidgetVisuals {
|
pub fn state(&self, state: WidgetState) -> &WidgetVisuals {
|
||||||
match state {
|
match state {
|
||||||
WidgetState::Noninteractive => &self.noninteractive,
|
WidgetState::Noninteractive => &self.noninteractive,
|
||||||
@@ -107,7 +116,8 @@ impl Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn widget_style(&self, state: WidgetState) -> WidgetStyle {
|
/// 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 visuals = self.visuals.widgets.state(state);
|
||||||
let font_id = self.override_font_id.clone();
|
let font_id = self.override_font_id.clone();
|
||||||
WidgetStyle {
|
WidgetStyle {
|
||||||
@@ -131,11 +141,13 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn button_style(&self, state: WidgetState, selected: bool) -> ButtonStyle {
|
/// 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 visuals = *self.visuals.widgets.state(state);
|
||||||
let mut ws = self.widget_style(state);
|
let mut ws = self.widget_style(classes, state);
|
||||||
|
|
||||||
if selected {
|
if classes.has(SELECTED_CLASS) {
|
||||||
visuals.weak_bg_fill = self.visuals.selection.bg_fill;
|
visuals.weak_bg_fill = self.visuals.selection.bg_fill;
|
||||||
visuals.bg_fill = self.visuals.selection.bg_fill;
|
visuals.bg_fill = self.visuals.selection.bg_fill;
|
||||||
visuals.fg_stroke = self.visuals.selection.stroke;
|
visuals.fg_stroke = self.visuals.selection.stroke;
|
||||||
@@ -157,9 +169,11 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkbox_style(&self, state: WidgetState) -> CheckboxStyle {
|
/// 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 visuals = self.visuals.widgets.state(state);
|
||||||
let ws = self.widget_style(state);
|
let ws = self.widget_style(classes, state);
|
||||||
CheckboxStyle {
|
CheckboxStyle {
|
||||||
frame: Frame::new(),
|
frame: Frame::new(),
|
||||||
checkbox_size: self.spacing.icon_width,
|
checkbox_size: self.spacing.icon_width,
|
||||||
@@ -168,8 +182,6 @@ impl Style {
|
|||||||
fill: visuals.bg_fill,
|
fill: visuals.bg_fill,
|
||||||
corner_radius: visuals.corner_radius,
|
corner_radius: visuals.corner_radius,
|
||||||
stroke: visuals.bg_stroke,
|
stroke: visuals.bg_stroke,
|
||||||
// Use the inner_margin for the expansion
|
|
||||||
inner_margin: visuals.expansion.into(),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
text_style: ws.text,
|
text_style: ws.text,
|
||||||
@@ -177,8 +189,10 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label_style(&self, state: WidgetState) -> LabelStyle {
|
/// The dedicated label style. The style is computed according to the classes and state of the widget.
|
||||||
let ws = self.widget_style(state);
|
/// 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 {
|
LabelStyle {
|
||||||
frame: Frame {
|
frame: Frame {
|
||||||
fill: ws.frame.fill,
|
fill: ws.frame.fill,
|
||||||
@@ -193,7 +207,9 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn separator_style(&self, _state: WidgetState) -> SeparatorStyle {
|
/// 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();
|
let visuals = self.visuals.noninteractive();
|
||||||
SeparatorStyle {
|
SeparatorStyle {
|
||||||
spacing: 6.0,
|
spacing: 6.0,
|
||||||
@@ -201,3 +217,102 @@ impl Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The root class is a special class present on every top-level [`crate::Ui`].
|
||||||
|
pub const ROOT_CLASS: &str = "root";
|
||||||
|
|
||||||
|
/// The selected class is a special class present on selected [`crate::Button`].
|
||||||
|
pub const SELECTED_CLASS: &str = "selected";
|
||||||
|
|
||||||
|
/// A class is a static string identifier.
|
||||||
|
pub type ClassName = Cow<'static, str>;
|
||||||
|
|
||||||
|
/// Classes are string identifier that can be set on widget/Ui.
|
||||||
|
///
|
||||||
|
/// This can be used by styling engine to compute a different style
|
||||||
|
/// based on the set of classes present on the widget/Ui.
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct Classes {
|
||||||
|
classes: SmallVec<[ClassName; 5]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Classes {
|
||||||
|
/// Add a class to the list if the condition is true
|
||||||
|
#[inline]
|
||||||
|
fn add_if(&mut self, class: impl Into<ClassName>, condition: bool) {
|
||||||
|
if condition {
|
||||||
|
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 the given class by consuming [`self`]
|
||||||
|
#[inline]
|
||||||
|
fn with_class(mut self, class: impl Into<ClassName>) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.classes_mut().add_if(class.into(), true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the given class by consuming [`self`] if the condition is true
|
||||||
|
#[inline]
|
||||||
|
fn with_class_if(mut self, class: impl Into<ClassName>, condition: bool) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.classes_mut().add_if(class.into(), condition);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the given class in-place
|
||||||
|
#[inline]
|
||||||
|
fn add_class(&mut self, class: impl Into<ClassName>) -> &mut Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.classes_mut().add_if(class.into(), true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the given class in-place if the condition is true
|
||||||
|
#[inline]
|
||||||
|
fn add_class_if(&mut self, class: impl Into<ClassName>, condition: bool) -> &mut Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.classes_mut().add_if(class.into(), condition);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the class is present
|
||||||
|
fn has(&self, class: impl Into<ClassName>) -> bool {
|
||||||
|
self.classes().classes.contains(&class.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
||||||
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,
|
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,
|
||||||
Widget, WidgetInfo, WidgetText, WidgetType,
|
Widget, WidgetInfo, WidgetText, WidgetType,
|
||||||
widget_style::{ButtonStyle, WidgetState},
|
widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Clickable button with text.
|
/// Clickable button with text.
|
||||||
@@ -38,6 +38,7 @@ pub struct Button<'a> {
|
|||||||
selected: bool,
|
selected: bool,
|
||||||
image_tint_follows_text_color: bool,
|
image_tint_follows_text_color: bool,
|
||||||
limit_image_size: bool,
|
limit_image_size: bool,
|
||||||
|
classes: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Button<'a> {
|
impl<'a> Button<'a> {
|
||||||
@@ -56,6 +57,7 @@ impl<'a> Button<'a> {
|
|||||||
selected: false,
|
selected: false,
|
||||||
image_tint_follows_text_color: false,
|
image_tint_follows_text_color: false,
|
||||||
limit_image_size: false,
|
limit_image_size: false,
|
||||||
|
classes: Classes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +288,7 @@ impl<'a> Button<'a> {
|
|||||||
selected,
|
selected,
|
||||||
image_tint_follows_text_color,
|
image_tint_follows_text_color,
|
||||||
limit_image_size,
|
limit_image_size,
|
||||||
|
mut classes,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Min size height always equal or greater than interact size if not small
|
// Min size height always equal or greater than interact size if not small
|
||||||
@@ -311,7 +314,9 @@ impl<'a> Button<'a> {
|
|||||||
let response: Option<Response> = ui.ctx().read_response(id);
|
let response: Option<Response> = ui.ctx().read_response(id);
|
||||||
let state = response.map(|r| r.widget_state()).unwrap_or_default();
|
let state = response.map(|r| r.widget_state()).unwrap_or_default();
|
||||||
|
|
||||||
let ButtonStyle { frame, text_style } = ui.style().button_style(state, selected);
|
classes.add_class_if(SELECTED_CLASS, selected);
|
||||||
|
|
||||||
|
let ButtonStyle { frame, text_style } = ui.style().button_style(&classes, state);
|
||||||
|
|
||||||
let mut button_padding = if has_frame_margin {
|
let mut button_padding = if has_frame_margin {
|
||||||
frame.inner_margin
|
frame.inner_margin
|
||||||
@@ -388,3 +393,13 @@ impl Widget for Button<'_> {
|
|||||||
self.atom_ui(ui).response
|
self.atom_ui(ui).response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasClasses for Button<'_> {
|
||||||
|
fn classes(&self) -> &Classes {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classes_mut(&mut self) -> &mut Classes {
|
||||||
|
&mut self.classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use emath::Rect;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget,
|
Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget,
|
||||||
WidgetInfo, WidgetType, epaint, pos2, widget_style::CheckboxStyle,
|
WidgetInfo, WidgetType, epaint, pos2,
|
||||||
|
widget_style::{CheckboxStyle, Classes, HasClasses},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(emilk): allow checkbox without a text label
|
// TODO(emilk): allow checkbox without a text label
|
||||||
@@ -23,6 +24,7 @@ pub struct Checkbox<'a> {
|
|||||||
checked: &'a mut bool,
|
checked: &'a mut bool,
|
||||||
atoms: Atoms<'a>,
|
atoms: Atoms<'a>,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
|
classes: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Checkbox<'a> {
|
impl<'a> Checkbox<'a> {
|
||||||
@@ -31,6 +33,7 @@ impl<'a> Checkbox<'a> {
|
|||||||
checked,
|
checked,
|
||||||
atoms: atoms.into_atoms(),
|
atoms: atoms.into_atoms(),
|
||||||
indeterminate: false,
|
indeterminate: false,
|
||||||
|
classes: Classes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ impl Widget for Checkbox<'_> {
|
|||||||
checked,
|
checked,
|
||||||
mut atoms,
|
mut atoms,
|
||||||
indeterminate,
|
indeterminate,
|
||||||
|
classes,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Get the widget style by reading the response from the previous pass
|
// Get the widget style by reading the response from the previous pass
|
||||||
@@ -69,7 +73,7 @@ impl Widget for Checkbox<'_> {
|
|||||||
frame,
|
frame,
|
||||||
check_stroke,
|
check_stroke,
|
||||||
text_style,
|
text_style,
|
||||||
} = ui.style().checkbox_style(state);
|
} = ui.style().checkbox_style(&classes, state);
|
||||||
|
|
||||||
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);
|
||||||
@@ -153,3 +157,13 @@ impl Widget for Checkbox<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasClasses for Checkbox<'_> {
|
||||||
|
fn classes(&self) -> &Classes {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classes_mut(&mut self) -> &mut Classes {
|
||||||
|
&mut self.classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{Response, Sense, Ui, Vec2, Widget, vec2, widget_style::SeparatorStyle};
|
use crate::{
|
||||||
|
Response, Sense, Ui, Vec2, Widget, vec2,
|
||||||
|
widget_style::{Classes, HasClasses, SeparatorStyle},
|
||||||
|
};
|
||||||
|
|
||||||
/// A visual separator. A horizontal or vertical line (depending on [`crate::Layout`]).
|
/// A visual separator. A horizontal or vertical line (depending on [`crate::Layout`]).
|
||||||
///
|
///
|
||||||
@@ -16,6 +19,7 @@ pub struct Separator {
|
|||||||
spacing: Option<f32>,
|
spacing: Option<f32>,
|
||||||
grow: f32,
|
grow: f32,
|
||||||
is_horizontal_line: Option<bool>,
|
is_horizontal_line: Option<bool>,
|
||||||
|
classes: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Separator {
|
impl Default for Separator {
|
||||||
@@ -24,6 +28,7 @@ impl Default for Separator {
|
|||||||
spacing: None,
|
spacing: None,
|
||||||
grow: 0.0,
|
grow: 0.0,
|
||||||
is_horizontal_line: None,
|
is_horizontal_line: None,
|
||||||
|
classes: Classes::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +96,7 @@ impl Widget for Separator {
|
|||||||
spacing,
|
spacing,
|
||||||
grow,
|
grow,
|
||||||
is_horizontal_line,
|
is_horizontal_line,
|
||||||
|
classes,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Get the widget style by reading the response from the previous pass
|
// Get the widget style by reading the response from the previous pass
|
||||||
@@ -100,7 +106,7 @@ impl Widget for Separator {
|
|||||||
let SeparatorStyle {
|
let SeparatorStyle {
|
||||||
spacing: spacing_style,
|
spacing: spacing_style,
|
||||||
stroke,
|
stroke,
|
||||||
} = ui.style().separator_style(state);
|
} = ui.style().separator_style(&classes, state);
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -142,3 +148,13 @@ impl Widget for Separator {
|
|||||||
response
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasClasses for Separator {
|
||||||
|
fn classes(&self) -> &Classes {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classes_mut(&mut self) -> &mut Classes {
|
||||||
|
&mut self.classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user