mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Add Frame::expand_in_place, Frame::invisible and TextVisuals constructors
The expansion/stroke correction on a widget's margins was written out by hand. Move it into `Frame::expand_in_place`, which knows the frame's own stroke width, so a caller states the intent instead of the arithmetic. `Frame::invisible` goes with it: a frame that keeps its layout but drops its paint. Two fixes fall out of it in the button style: * A small button zeroed its vertical padding after the correction, which left the frame a `stroke.width - expansion` tall. Zeroing before the correction makes it exactly zero in every state, which is what "must not add any height" means. * The invisible frame of a button that hides its frame when inactive dropped the outer margin and the stroke width, so it was not, in fact, as big as the painted one. It now keeps the whole frame and only drops the paint. Also give `TextVisuals` a `new` and a `from_widget_visuals`, replacing the local `text_visuals` helper, and add `HasClasses::add_classes` and `HasClasses::with_classes`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -298,6 +298,18 @@ impl Frame {
|
||||
self
|
||||
}
|
||||
|
||||
/// Expand the frame without affecting layout.
|
||||
///
|
||||
/// This handles `expansion` by subtracting it from the outer margin and adding it to the
|
||||
/// inner margin. It also corrects for a stroke changing on hover, by subtracting the stroke
|
||||
/// width from `inner_margin`.
|
||||
#[inline]
|
||||
pub fn expand_in_place(mut self, expansion: f32) -> Self {
|
||||
self.outer_margin = self.outer_margin - Margin::from(expansion);
|
||||
self.inner_margin = self.inner_margin + Margin::from(expansion - self.stroke.width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Optional drop-shadow behind the frame.
|
||||
#[inline]
|
||||
pub fn shadow(mut self, shadow: Shadow) -> Self {
|
||||
@@ -316,6 +328,16 @@ impl Frame {
|
||||
self.shadow.color = self.shadow.color.gamma_multiply(opacity);
|
||||
self
|
||||
}
|
||||
|
||||
/// Make this frame invisible by setting background and stroke to transparent.
|
||||
///
|
||||
/// Will not affect layout or contents.
|
||||
pub fn invisible(mut self) -> Self {
|
||||
self.fill = Color32::TRANSPARENT;
|
||||
self.stroke.color = Color32::TRANSPARENT;
|
||||
self.shadow = Shadow::NONE;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Inspectors
|
||||
|
||||
@@ -2,8 +2,7 @@ use emath::Vec2;
|
||||
use epaint::Margin;
|
||||
|
||||
use crate::{
|
||||
Button, Context, Frame, Style, TextStyle,
|
||||
style::WidgetVisuals,
|
||||
Button, Context, Frame, TextStyle,
|
||||
theme::StyleProvider,
|
||||
widget_style::{
|
||||
ButtonStyle, CheckboxStyle, HasClasses as _, SeparatorStyle, StyleArgs, TextVisuals,
|
||||
@@ -30,17 +29,6 @@ impl DefaultStyle {
|
||||
}
|
||||
}
|
||||
|
||||
/// The text of a widget, based on the [`WidgetVisuals`] of its current state.
|
||||
fn text_visuals(style: &Style, widget_visuals: &WidgetVisuals) -> TextVisuals {
|
||||
TextVisuals {
|
||||
color: widget_visuals.text_color(),
|
||||
font_id: style
|
||||
.override_font_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| TextStyle::Body.resolve(style)),
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleProvider<ButtonStyle> for DefaultStyle {
|
||||
fn style(&mut self, modifiers: &StyleArgs<'_>) -> ButtonStyle {
|
||||
let StyleArgs {
|
||||
@@ -59,17 +47,24 @@ impl StyleProvider<ButtonStyle> for DefaultStyle {
|
||||
widget_visuals.fg_stroke = visuals.selection.stroke;
|
||||
}
|
||||
|
||||
let mut inner_margin: Margin = (spacing.button_padding
|
||||
+ Vec2::splat(widget_visuals.expansion)
|
||||
- Vec2::splat(widget_visuals.bg_stroke.width))
|
||||
.into();
|
||||
let mut inner_margin: Margin = spacing.button_padding.into();
|
||||
|
||||
// A small button is meant to be embedded into text, so it must not add any height.
|
||||
// A small button as high as regular text
|
||||
if classes.has_class(Button::CLASS_SMALL) {
|
||||
inner_margin.top = 0;
|
||||
inner_margin.bottom = 0;
|
||||
}
|
||||
|
||||
let painted_frame = Frame {
|
||||
fill: widget_visuals.weak_bg_fill,
|
||||
stroke: widget_visuals.bg_stroke,
|
||||
corner_radius: widget_visuals.corner_radius,
|
||||
inner_margin,
|
||||
..Default::default()
|
||||
}
|
||||
// Ensure changing expansion and stroke don't affect layout:
|
||||
.expand_in_place(widget_visuals.expansion);
|
||||
|
||||
let has_frame = classes.has_class(Button::CLASS_FRAME)
|
||||
|| (!classes.has_class(Button::CLASS_NO_FRAME) && style.visuals.button_frame);
|
||||
|
||||
@@ -79,17 +74,10 @@ impl StyleProvider<ButtonStyle> for DefaultStyle {
|
||||
} else if classes.has_class(Button::CLASS_HIDE_FRAME_WHEN_INACTIVE)
|
||||
&& *state == WidgetState::Inactive
|
||||
{
|
||||
// Invisible, but as big as it will be once the user interacts with it.
|
||||
Frame::new().inner_margin(inner_margin)
|
||||
// Hide the frame, but keep its spacing
|
||||
painted_frame.invisible()
|
||||
} else {
|
||||
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,
|
||||
..Default::default()
|
||||
}
|
||||
painted_frame
|
||||
};
|
||||
|
||||
ButtonStyle {
|
||||
@@ -99,7 +87,7 @@ impl StyleProvider<ButtonStyle> for DefaultStyle {
|
||||
Vec2::new(0.0, spacing.interact_size.y)
|
||||
},
|
||||
frame,
|
||||
text_style: text_visuals(style, &widget_visuals),
|
||||
text_style: TextVisuals::from_widget_visuals(style, TextStyle::Body, &widget_visuals),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +108,7 @@ impl StyleProvider<CheckboxStyle> for DefaultStyle {
|
||||
stroke: widget_visuals.bg_stroke,
|
||||
..Default::default()
|
||||
},
|
||||
text_style: text_visuals(style, &widget_visuals),
|
||||
text_style: TextVisuals::from_widget_visuals(style, TextStyle::Body, &widget_visuals),
|
||||
check_stroke: widget_visuals.fg_stroke,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,18 @@ pub trait HasClasses {
|
||||
self
|
||||
}
|
||||
|
||||
/// Add all the given classes by consuming `self`
|
||||
///
|
||||
/// Useful to forward the classes of a composite widget to the widgets it is built from.
|
||||
#[inline]
|
||||
fn with_classes(mut self, classes: Classes) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.classes_mut().classes.extend(classes.classes);
|
||||
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
|
||||
@@ -98,6 +110,12 @@ pub trait HasClasses {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_classes(&mut self, classes: Classes) -> &mut Self {
|
||||
self.classes_mut().classes.extend(classes.classes);
|
||||
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
|
||||
|
||||
@@ -17,7 +17,7 @@ use core::fmt::Debug;
|
||||
use epaint::{Color32, FontId, Stroke, Vec2};
|
||||
|
||||
use crate::{
|
||||
Context, Frame, Response, Style, UiStack,
|
||||
Context, Frame, Response, Style, TextStyle, UiStack,
|
||||
style::{WidgetVisuals, Widgets},
|
||||
};
|
||||
|
||||
@@ -34,6 +34,30 @@ pub struct TextVisuals {
|
||||
pub color: Color32,
|
||||
}
|
||||
|
||||
impl TextVisuals {
|
||||
/// Text in `color`, using the font of the given [`TextStyle`].
|
||||
///
|
||||
/// `style.override_font_id` wins over `text_style`, if it is set.
|
||||
pub fn new(style: &Style, text_style: TextStyle, color: Color32) -> Self {
|
||||
Self {
|
||||
color,
|
||||
font_id: style
|
||||
.override_font_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| text_style.resolve(style)),
|
||||
}
|
||||
}
|
||||
|
||||
/// The text of a widget, colored by the [`WidgetVisuals`] of its current state.
|
||||
pub fn from_widget_visuals(
|
||||
style: &Style,
|
||||
text_style: TextStyle,
|
||||
widget_visuals: &WidgetVisuals,
|
||||
) -> Self {
|
||||
Self::new(style, text_style, widget_visuals.text_color())
|
||||
}
|
||||
}
|
||||
|
||||
/// Dedicated button style
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ButtonStyle {
|
||||
|
||||
Reference in New Issue
Block a user