From bf7a71d0dabf7496d86042d5a9ccd442be885330 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Fri, 21 Aug 2026 15:59:41 +0200 Subject: [PATCH] Let the theme decide a button's minimum size --- crates/egui/src/theme/default_style.rs | 3 +++ crates/egui/src/widget_style/mod.rs | 7 +++++++ crates/egui/src/widgets/button.rs | 21 +++++++++++++-------- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/egui/src/theme/default_style.rs b/crates/egui/src/theme/default_style.rs index 551f1337e..d27ba0a66 100644 --- a/crates/egui/src/theme/default_style.rs +++ b/crates/egui/src/theme/default_style.rs @@ -88,6 +88,9 @@ impl StyleProvider for DefaultStyle { ..Default::default() }, text_style: ws.text, + // Historically only the height was floored, so that a button is at least as tall as + // any other interactive widget on the same row. + min_size: Vec2::new(0.0, spacing.interact_size.y), } } } diff --git a/crates/egui/src/widget_style/mod.rs b/crates/egui/src/widget_style/mod.rs index 77376a87d..ff699ec35 100644 --- a/crates/egui/src/widget_style/mod.rs +++ b/crates/egui/src/widget_style/mod.rs @@ -10,6 +10,7 @@ pub use self::classes::{ use core::fmt::Debug; +use emath::Vec2; use epaint::{Color32, FontId, Stroke, text::TextWrapMode}; use crate::{ @@ -51,6 +52,12 @@ impl WidgetStyle for BaseStyle {} pub struct ButtonStyle { pub frame: Frame, pub text_style: TextVisuals, + + /// How small the button may get, before its contents are taken into account. + /// + /// Ignored by a [`crate::Button::small`] button, which sizes itself purely from its contents + /// and its own [`crate::Button::min_size`]. + pub min_size: Vec2, } impl WidgetStyle for ButtonStyle {} diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index d7f6c8a0c..88195518d 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -2,8 +2,8 @@ use epaint::Margin; use crate::{ Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Atoms, Color32, CornerRadius, - Frame, Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, - Vec2, Widget, WidgetInfo, WidgetText, WidgetType, + Frame, Image, IntoAtoms, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, + WidgetInfo, WidgetText, WidgetType, widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState}, }; @@ -303,11 +303,6 @@ impl<'a> Button<'a> { mut classes, } = self; - // Min size height always equal or greater than interact size if not small - if !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(_)) { @@ -328,7 +323,17 @@ impl<'a> Button<'a> { classes.add_class_if(SELECTED_CLASS, selected.unwrap_or(false)); - let ButtonStyle { frame, text_style } = ui.widget_style(id, &classes); + let ButtonStyle { + frame, + text_style, + min_size: style_min_size, + } = ui.widget_style(id, &classes); + + // The theme decides how small a button may get — unless it is a `small` one, which sizes + // itself purely from its contents. + if !small { + min_size = min_size.max(style_min_size); + } let mut button_padding = if has_frame_margin { frame.inner_margin