diff --git a/crates/egui/src/theme/default_style.rs b/crates/egui/src/theme/default_style.rs index 821e74e47..ec4804004 100644 --- a/crates/egui/src/theme/default_style.rs +++ b/crates/egui/src/theme/default_style.rs @@ -56,7 +56,10 @@ impl StyleProvider for DefaultStyle { inner_margin.bottom = 0; } - let frame = if classes.has_class(Button::CLASS_NO_FRAME) { + let has_frame = classes.has_class(Button::CLASS_FRAME) + || (!classes.has_class(Button::CLASS_NO_FRAME) && style.visuals.button_frame); + + let frame = if !has_frame { // No frame at all: the button takes up no more room than its contents. Frame::new() } else if classes.has_class(Button::CLASS_NO_FRAME_WHEN_INACTIVE) @@ -76,6 +79,11 @@ impl StyleProvider for DefaultStyle { }; ButtonStyle { + min_size: if classes.has_class(Button::CLASS_SMALL) { + Vec2::ZERO + } else { + Vec2::new(0.0, spacing.interact_size.y) + }, frame, text_style: text_visuals(style, &widget_visuals), } diff --git a/crates/egui/src/widget_style/mod.rs b/crates/egui/src/widget_style/mod.rs index 185ece014..f945116b9 100644 --- a/crates/egui/src/widget_style/mod.rs +++ b/crates/egui/src/widget_style/mod.rs @@ -14,7 +14,7 @@ pub mod class { use core::fmt::Debug; -use epaint::{Color32, FontId, Stroke}; +use epaint::{Color32, FontId, Stroke, Vec2}; use crate::{ Context, Frame, Response, Style, UiStack, @@ -37,6 +37,9 @@ pub struct TextVisuals { /// Dedicated button style #[derive(Debug, Clone)] pub struct ButtonStyle { + /// The minimum size of the button before any per-button override. + pub min_size: Vec2, + pub frame: Frame, pub text_style: TextVisuals, } diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index e26336ee0..2ba5fe9c9 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -28,7 +28,6 @@ pub struct Button<'a> { layout: AtomLayout<'a>, fill: Option, stroke: Option, - frame: Option, min_size: Vec2, corner_radius: Option, selected: Option, @@ -47,6 +46,9 @@ impl<'a> Button<'a> { /// Present on a button that should have no frame at all. pub const CLASS_NO_FRAME: &'static str = "egui::no_frame"; + /// Present on a button that should have a frame, even when the global default is frameless. + pub const CLASS_FRAME: &'static str = "egui::frame"; + /// Present on a button that should have no frame while it is inactive. pub const CLASS_NO_FRAME_WHEN_INACTIVE: &'static str = "egui::button::no_frame_when_inactive"; @@ -57,7 +59,6 @@ impl<'a> Button<'a> { .fallback_font(TextStyle::Button), fill: None, stroke: None, - frame: None, min_size: Vec2::ZERO, corner_radius: None, selected: None, @@ -150,7 +151,7 @@ impl<'a> Button<'a> { #[inline] pub fn fill(mut self, fill: impl Into) -> Self { self.fill = Some(fill.into()); - self + self.frame(true) } /// Override button stroke. Note that this will override any on-hover effects. @@ -158,8 +159,7 @@ impl<'a> Button<'a> { #[inline] pub fn stroke(mut self, stroke: impl Into) -> Self { self.stroke = Some(stroke.into()); - self.frame = Some(true); - self + self.frame(true) } /// Make this a small button, suitable for embedding into text. @@ -174,13 +174,13 @@ impl<'a> Button<'a> { /// Turn off the frame /// - /// If `false`, this adds the built-in [`Self::CLASS_NO_FRAME`], which with the default style - /// removes the fill, the stroke and the margin. + /// This adds either the built-in [`Self::CLASS_FRAME`] or [`Self::CLASS_NO_FRAME`] class. + /// With the default style, the latter removes the fill, the stroke and the margin. /// /// Default: `ui.visuals().button_frame`. #[inline] pub fn frame(mut self, frame: bool) -> Self { - self.frame = Some(frame); + self.set_class(Self::CLASS_FRAME, frame); self.set_class(Self::CLASS_NO_FRAME, !frame); self } @@ -317,20 +317,14 @@ impl<'a> Button<'a> { mut layout, fill, stroke, - frame, mut min_size, corner_radius, selected, image_tint_follows_text_color, limit_image_size, - mut classes, + classes, } = self; - // Min size height always equal or greater than interact size if not small - if !classes.has_class(Self::CLASS_SMALL) { - min_size.y = min_size.y.at_least(ui.spacing().interact_size.y); - } - if limit_image_size { layout.map_atoms(|atom| { if matches!(&atom.kind, AtomKind::Image(_)) { @@ -343,18 +337,15 @@ impl<'a> Button<'a> { let text = layout.text().map(String::from); - // An explicit `frame` call already updated the classes at the call site, preserving - // its order relative to user classes. Only apply the global default here. - if frame.is_none() { - classes.add_class_if(Self::CLASS_NO_FRAME, !ui.visuals().button_frame); - } - let id = ui.next_auto_id(); let ButtonStyle { mut frame, text_style, + min_size: style_min_size, } = ui.widget_style(id, &classes); + min_size = min_size.at_least(style_min_size); + // Override global style by local style if let Some(fill) = fill { frame = frame.fill(fill); diff --git a/examples/styling_engine/src/main.rs b/examples/styling_engine/src/main.rs index b4df71710..43a87710a 100644 --- a/examples/styling_engine/src/main.rs +++ b/examples/styling_engine/src/main.rs @@ -60,6 +60,7 @@ impl StyleProvider for MyTheme { }; ButtonStyle { + min_size: egui::vec2(0.0, style.spacing.interact_size.y), frame: Frame::new() .fill(fill) .corner_radius(self.corner_radius)