mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Give TextEdit a min_size and a gap, via a shared LayoutStyle
`ButtonStyle` carried a `min_size` and a gap; `TextEditStyle` carried neither, so a themed text edit could set its padding but not its height or the gap to its prefix — the height had to travel separately on the widget, and the gap fell back to the ambient `spacing.icon_spacing`. A theme decides the two together, so they move into a `LayoutStyle` that both styles embed. `TextEdit` now honors both, with defaults that keep today's rendering: no floor of egui's own, and the gap `AtomLayout` was already falling back to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,9 @@ use crate::{
|
||||
Frame, TextStyle,
|
||||
theme::StyleProvider,
|
||||
widget_style::{
|
||||
BaseStyle, ButtonStyle, CheckboxStyle, HasClasses as _, LabelStyle, READ_ONLY_CLASS,
|
||||
SELECTED_CLASS, SeparatorStyle, StyleArgs, TextEditStyle, TextVisuals, WidgetState,
|
||||
BaseStyle, ButtonStyle, CheckboxStyle, HasClasses as _, LabelStyle, LayoutStyle,
|
||||
READ_ONLY_CLASS, SELECTED_CLASS, SeparatorStyle, StyleArgs, TextEditStyle, TextVisuals,
|
||||
WidgetState,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -87,11 +88,13 @@ impl StyleProvider<ButtonStyle> for DefaultStyle {
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
layout: LayoutStyle {
|
||||
// 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),
|
||||
gap: spacing.icon_spacing,
|
||||
},
|
||||
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),
|
||||
gap: spacing.icon_spacing,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,6 +148,12 @@ impl StyleProvider<TextEditStyle> for DefaultStyle {
|
||||
outer_margin: Margin::same(-(widget_visuals.expansion as i8)),
|
||||
..Default::default()
|
||||
},
|
||||
layout: LayoutStyle {
|
||||
// A text edit sizes itself from the rows it holds; egui's own theme adds no floor
|
||||
// of its own.
|
||||
min_size: Vec2::ZERO,
|
||||
gap: style.spacing.icon_spacing,
|
||||
},
|
||||
text: ws.text,
|
||||
hint_text_color: style.visuals.weak_text_color(),
|
||||
}
|
||||
|
||||
@@ -65,22 +65,35 @@ pub struct BaseStyle {
|
||||
|
||||
impl WidgetStyle for BaseStyle {}
|
||||
|
||||
/// How a widget's contents are laid out
|
||||
///
|
||||
/// A theme that gives a widget a size decides both together — the height and the gap of a small
|
||||
/// button are one look, not two — so they travel as one struct, shared by every widget style that
|
||||
/// lays its contents out with an [`crate::AtomLayout`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LayoutStyle {
|
||||
/// How small the widget may get, before its contents are taken into account.
|
||||
///
|
||||
/// A floor, not a size: a widget is never smaller than what it holds.
|
||||
pub min_size: Vec2,
|
||||
|
||||
/// The gap between the widget's atoms, e.g. between an icon and the text beside it.
|
||||
pub gap: f32,
|
||||
}
|
||||
|
||||
/// Dedicated button style
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ButtonStyle {
|
||||
pub frame: Frame,
|
||||
|
||||
/// How the button's contents are laid out.
|
||||
///
|
||||
/// [`LayoutStyle::min_size`] is ignored by a [`crate::Button::small`] button, which sizes
|
||||
/// itself purely from its contents and its own [`crate::Button::min_size`];
|
||||
/// [`LayoutStyle::gap`] is overridden by [`crate::Button::gap`].
|
||||
pub layout: LayoutStyle,
|
||||
|
||||
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,
|
||||
|
||||
/// The gap between the button's atoms, e.g. between its icon and its text.
|
||||
///
|
||||
/// Overridden by [`crate::Button::gap`].
|
||||
pub gap: f32,
|
||||
}
|
||||
|
||||
impl WidgetStyle for ButtonStyle {}
|
||||
@@ -91,6 +104,13 @@ pub struct TextEditStyle {
|
||||
/// Frame around the text, including its padding.
|
||||
pub frame: Frame,
|
||||
|
||||
/// How the field's contents are laid out.
|
||||
///
|
||||
/// [`LayoutStyle::min_size`] is raised by [`crate::TextEdit::min_size`] and by the rows of
|
||||
/// text the field holds, so it only ever sets a floor; [`LayoutStyle::gap`] separates the
|
||||
/// text from a [`crate::TextEdit::prefix`] or [`crate::TextEdit::suffix`].
|
||||
pub layout: LayoutStyle,
|
||||
|
||||
/// The text being edited.
|
||||
pub text: TextVisuals,
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Atoms, Color32, CornerRadius,
|
||||
Frame, Image, IntoAtoms, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget,
|
||||
WidgetInfo, WidgetText, WidgetType,
|
||||
widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState},
|
||||
widget_style::{ButtonStyle, Classes, HasClasses, LayoutStyle, SELECTED_CLASS, WidgetState},
|
||||
};
|
||||
|
||||
/// Clickable button with text.
|
||||
@@ -325,9 +325,12 @@ impl<'a> Button<'a> {
|
||||
|
||||
let ButtonStyle {
|
||||
frame,
|
||||
layout:
|
||||
LayoutStyle {
|
||||
min_size: style_min_size,
|
||||
gap,
|
||||
},
|
||||
text_style,
|
||||
min_size: style_min_size,
|
||||
gap,
|
||||
} = ui.widget_style(id, &classes);
|
||||
|
||||
layout = layout.fallback_gap(gap);
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
self, CCursorRange, text_cursor_state::cursor_rect, visuals::paint_text_selection,
|
||||
},
|
||||
vec2,
|
||||
widget_style::{Classes, HasClasses, READ_ONLY_CLASS, TextEditStyle},
|
||||
widget_style::{Classes, HasClasses, LayoutStyle, READ_ONLY_CLASS, TextEditStyle},
|
||||
};
|
||||
|
||||
use super::{TextEditOutput, TextEditState};
|
||||
@@ -489,10 +489,19 @@ impl TextEdit<'_> {
|
||||
classes.add_class_if(READ_ONLY_CLASS, !text.is_mutable());
|
||||
let TextEditStyle {
|
||||
frame: styled_frame,
|
||||
layout:
|
||||
LayoutStyle {
|
||||
min_size: style_min_size,
|
||||
gap,
|
||||
},
|
||||
text: text_visuals,
|
||||
hint_text_color,
|
||||
} = ui.widget_style(id, &classes);
|
||||
|
||||
// The theme sets a floor on the size; the builder's own `min_size` can only raise it,
|
||||
// the same way it does for a button.
|
||||
let min_size = min_size.max(style_min_size);
|
||||
|
||||
let text_color = text_color
|
||||
.or_else(|| ui.visuals().override_text_color)
|
||||
.unwrap_or(text_visuals.color);
|
||||
@@ -736,6 +745,7 @@ impl TextEdit<'_> {
|
||||
|
||||
let allocated = AtomLayout::new(atoms)
|
||||
.id(id)
|
||||
.fallback_gap(gap)
|
||||
.min_size(Vec2::new(allocate_width, min_height.at_least(min_size.y)))
|
||||
.max_width(allocate_width)
|
||||
.sense(sense)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use eframe::egui::{
|
||||
self, CentralPanel, Color32, Frame, Panel,
|
||||
theme::StyleProvider,
|
||||
widget_style::{BaseStyle, ButtonStyle, HasClasses as _, StyleArgs, WidgetState},
|
||||
widget_style::{BaseStyle, ButtonStyle, HasClasses as _, LayoutStyle, StyleArgs, WidgetState},
|
||||
};
|
||||
|
||||
/// Buttons with this class are styled as a destructive action.
|
||||
@@ -67,9 +67,11 @@ impl StyleProvider<ButtonStyle> for MyTheme {
|
||||
.fill(fill)
|
||||
.corner_radius(self.corner_radius)
|
||||
.inner_margin(8),
|
||||
layout: LayoutStyle {
|
||||
min_size: args.style.spacing.interact_size,
|
||||
gap: args.style.spacing.icon_spacing,
|
||||
},
|
||||
text_style: base.text,
|
||||
min_size: args.style.spacing.interact_size,
|
||||
gap: args.style.spacing.icon_spacing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user