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,
|
Frame, TextStyle,
|
||||||
theme::StyleProvider,
|
theme::StyleProvider,
|
||||||
widget_style::{
|
widget_style::{
|
||||||
BaseStyle, ButtonStyle, CheckboxStyle, HasClasses as _, LabelStyle, READ_ONLY_CLASS,
|
BaseStyle, ButtonStyle, CheckboxStyle, HasClasses as _, LabelStyle, LayoutStyle,
|
||||||
SELECTED_CLASS, SeparatorStyle, StyleArgs, TextEditStyle, TextVisuals, WidgetState,
|
READ_ONLY_CLASS, SELECTED_CLASS, SeparatorStyle, StyleArgs, TextEditStyle, TextVisuals,
|
||||||
|
WidgetState,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,11 +88,13 @@ impl StyleProvider<ButtonStyle> for DefaultStyle {
|
|||||||
.into(),
|
.into(),
|
||||||
..Default::default()
|
..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,
|
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)),
|
outer_margin: Margin::same(-(widget_visuals.expansion as i8)),
|
||||||
..Default::default()
|
..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,
|
text: ws.text,
|
||||||
hint_text_color: style.visuals.weak_text_color(),
|
hint_text_color: style.visuals.weak_text_color(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,22 +65,35 @@ pub struct BaseStyle {
|
|||||||
|
|
||||||
impl WidgetStyle for 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
|
/// Dedicated button style
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ButtonStyle {
|
pub struct ButtonStyle {
|
||||||
pub frame: Frame,
|
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,
|
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 {}
|
impl WidgetStyle for ButtonStyle {}
|
||||||
@@ -91,6 +104,13 @@ pub struct TextEditStyle {
|
|||||||
/// Frame around the text, including its padding.
|
/// Frame around the text, including its padding.
|
||||||
pub frame: Frame,
|
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.
|
/// The text being edited.
|
||||||
pub text: TextVisuals,
|
pub text: TextVisuals,
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Atoms, Color32, CornerRadius,
|
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Atoms, Color32, CornerRadius,
|
||||||
Frame, Image, IntoAtoms, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget,
|
Frame, Image, IntoAtoms, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget,
|
||||||
WidgetInfo, WidgetText, WidgetType,
|
WidgetInfo, WidgetText, WidgetType,
|
||||||
widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState},
|
widget_style::{ButtonStyle, Classes, HasClasses, LayoutStyle, SELECTED_CLASS, WidgetState},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Clickable button with text.
|
/// Clickable button with text.
|
||||||
@@ -325,9 +325,12 @@ impl<'a> Button<'a> {
|
|||||||
|
|
||||||
let ButtonStyle {
|
let ButtonStyle {
|
||||||
frame,
|
frame,
|
||||||
|
layout:
|
||||||
|
LayoutStyle {
|
||||||
|
min_size: style_min_size,
|
||||||
|
gap,
|
||||||
|
},
|
||||||
text_style,
|
text_style,
|
||||||
min_size: style_min_size,
|
|
||||||
gap,
|
|
||||||
} = ui.widget_style(id, &classes);
|
} = ui.widget_style(id, &classes);
|
||||||
|
|
||||||
layout = layout.fallback_gap(gap);
|
layout = layout.fallback_gap(gap);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::{
|
|||||||
self, CCursorRange, text_cursor_state::cursor_rect, visuals::paint_text_selection,
|
self, CCursorRange, text_cursor_state::cursor_rect, visuals::paint_text_selection,
|
||||||
},
|
},
|
||||||
vec2,
|
vec2,
|
||||||
widget_style::{Classes, HasClasses, READ_ONLY_CLASS, TextEditStyle},
|
widget_style::{Classes, HasClasses, LayoutStyle, READ_ONLY_CLASS, TextEditStyle},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{TextEditOutput, TextEditState};
|
use super::{TextEditOutput, TextEditState};
|
||||||
@@ -489,10 +489,19 @@ impl TextEdit<'_> {
|
|||||||
classes.add_class_if(READ_ONLY_CLASS, !text.is_mutable());
|
classes.add_class_if(READ_ONLY_CLASS, !text.is_mutable());
|
||||||
let TextEditStyle {
|
let TextEditStyle {
|
||||||
frame: styled_frame,
|
frame: styled_frame,
|
||||||
|
layout:
|
||||||
|
LayoutStyle {
|
||||||
|
min_size: style_min_size,
|
||||||
|
gap,
|
||||||
|
},
|
||||||
text: text_visuals,
|
text: text_visuals,
|
||||||
hint_text_color,
|
hint_text_color,
|
||||||
} = ui.widget_style(id, &classes);
|
} = 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
|
let text_color = text_color
|
||||||
.or_else(|| ui.visuals().override_text_color)
|
.or_else(|| ui.visuals().override_text_color)
|
||||||
.unwrap_or(text_visuals.color);
|
.unwrap_or(text_visuals.color);
|
||||||
@@ -736,6 +745,7 @@ impl TextEdit<'_> {
|
|||||||
|
|
||||||
let allocated = AtomLayout::new(atoms)
|
let allocated = AtomLayout::new(atoms)
|
||||||
.id(id)
|
.id(id)
|
||||||
|
.fallback_gap(gap)
|
||||||
.min_size(Vec2::new(allocate_width, min_height.at_least(min_size.y)))
|
.min_size(Vec2::new(allocate_width, min_height.at_least(min_size.y)))
|
||||||
.max_width(allocate_width)
|
.max_width(allocate_width)
|
||||||
.sense(sense)
|
.sense(sense)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use eframe::egui::{
|
use eframe::egui::{
|
||||||
self, CentralPanel, Color32, Frame, Panel,
|
self, CentralPanel, Color32, Frame, Panel,
|
||||||
theme::StyleProvider,
|
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.
|
/// Buttons with this class are styled as a destructive action.
|
||||||
@@ -67,9 +67,11 @@ impl StyleProvider<ButtonStyle> for MyTheme {
|
|||||||
.fill(fill)
|
.fill(fill)
|
||||||
.corner_radius(self.corner_radius)
|
.corner_radius(self.corner_radius)
|
||||||
.inner_margin(8),
|
.inner_margin(8),
|
||||||
|
layout: LayoutStyle {
|
||||||
|
min_size: args.style.spacing.interact_size,
|
||||||
|
gap: args.style.spacing.icon_spacing,
|
||||||
|
},
|
||||||
text_style: base.text,
|
text_style: base.text,
|
||||||
min_size: args.style.spacing.interact_size,
|
|
||||||
gap: args.style.spacing.icon_spacing,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user