Skip to content

Commit 64fe756

Browse files
authored
Number input (#23842)
# Objective Part of #19236 ## Solution A numeric text input widget. This includes: * Colored stripes ("sigils") for designating X, Y and Z input fields. * Character filtering * ValueChange events Not supported: * Validation error events * Range and precision options Additional changes in this PR: * Added small labels (related to text inputs only insofar as numeric inputs have small labels above them in the editor design). * Refactored labels to be simple spans instead of nested entities - this required a non-inherited text font theme component, so we renamed the existing component to have the word "inherited" in the name. * Consolidated the way focus outlines work for text inputs and other widgets * I had to make changes to Slider and other widgets to support the `is_final` flag in `ValueChange`. This is necessary to allow listeners the choice between spammy and not-spammy updates when listening to widget outputs. ## Testing Manual testing ## Showcase <img width="353" height="101" alt="numeric_input" src="https://github.com/user-attachments/assets/36efd243-fa01-484e-bab5-689aa6be4d9e" />
1 parent 4fd09ff commit 64fe756

27 files changed

Lines changed: 888 additions & 209 deletions

File tree

_release-content/release-notes/more_widgets.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---
22
title: "Moar widgets!"
33
authors: ["@viridia"]
4-
pull_requests: [23645, 23707, 23788, 23787, 23804]
4+
pull_requests: [23645, 23707, 23788, 23787, 23804, 23842]
55
---
66

77
Bevy Feathers, the opinionated UI widget collection, has added several new widgets:
88

99
- text input
10+
- number input
1011
- dropdown menu buttons
1112
- icon (displays an image)
1213
- label (displays a text string in the standard font)

crates/bevy_app/src/propagate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ pub struct Propagate<C: Component + Clone + PartialEq>(pub C);
7474

7575
/// Stops the output component being added to this entity.
7676
/// Relationship targets will still inherit the component from this entity or its parents.
77-
#[derive(Component)]
77+
#[derive(Component, Clone)]
7878
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
7979
pub struct PropagateOver<C>(PhantomData<fn() -> C>);
8080

8181
/// Stops the propagation at this entity. Children will not inherit the component.
82-
#[derive(Component)]
82+
#[derive(Component, Clone)]
8383
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
8484
pub struct PropagateStop<C>(PhantomData<fn() -> C>);
8585

crates/bevy_feathers/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ bevy_camera = { path = "../bevy_camera", version = "0.19.0-dev" }
1717
bevy_color = { path = "../bevy_color", version = "0.19.0-dev" }
1818
bevy_ui_widgets = { path = "../bevy_ui_widgets", version = "0.19.0-dev" }
1919
bevy_ecs = { path = "../bevy_ecs", version = "0.19.0-dev" }
20+
bevy_input = { path = "../bevy_input", version = "0.19.0-dev" }
2021
bevy_input_focus = { path = "../bevy_input_focus", version = "0.19.0-dev" }
2122
bevy_log = { path = "../bevy_log", version = "0.19.0-dev" }
2223
bevy_math = { path = "../bevy_math", version = "0.19.0-dev" }

crates/bevy_feathers/src/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,7 @@ pub mod size {
5555

5656
/// Small font size
5757
pub const SMALL_FONT: FontSize = FontSize::Px(12.0);
58+
59+
/// Extra-small font size
60+
pub const EXTRA_SMALL_FONT: FontSize = FontSize::Px(11.0);
5861
}

crates/bevy_feathers/src/containers/group.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
constants::{fonts, size},
77
font_styles::InheritableFont,
88
rounded_corners::RoundedCorners,
9-
theme::{ThemeBackgroundColor, ThemeBorderColor, ThemeFontColor},
9+
theme::{InheritableThemeTextColor, ThemeBackgroundColor, ThemeBorderColor},
1010
tokens,
1111
};
1212

@@ -42,7 +42,7 @@ pub fn group_header() -> impl Scene {
4242
}
4343
ThemeBackgroundColor(tokens::GROUP_HEADER_BG)
4444
ThemeBorderColor(tokens::GROUP_HEADER_BORDER)
45-
ThemeFontColor(tokens::GROUP_HEADER_TEXT)
45+
InheritableThemeTextColor(tokens::GROUP_HEADER_TEXT)
4646
InheritableFont {
4747
font: fonts::REGULAR,
4848
font_size: size::MEDIUM_FONT,

crates/bevy_feathers/src/containers/pane.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
constants::{fonts, size},
1111
font_styles::InheritableFont,
1212
rounded_corners::RoundedCorners,
13-
theme::{ThemeBackgroundColor, ThemeBorderColor, ThemeFontColor},
13+
theme::{InheritableThemeTextColor, ThemeBackgroundColor, ThemeBorderColor},
1414
tokens,
1515
};
1616

@@ -46,7 +46,7 @@ pub fn pane_header() -> impl Scene {
4646
}
4747
ThemeBackgroundColor(tokens::PANE_HEADER_BG)
4848
ThemeBorderColor(tokens::PANE_HEADER_BORDER)
49-
ThemeFontColor(tokens::PANE_HEADER_TEXT)
49+
InheritableThemeTextColor(tokens::PANE_HEADER_TEXT)
5050
InheritableFont {
5151
font: fonts::REGULAR,
5252
font_size: size::MEDIUM_FONT,

crates/bevy_feathers/src/containers/subpane.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
constants::{fonts, size},
77
font_styles::InheritableFont,
88
rounded_corners::RoundedCorners,
9-
theme::{ThemeBackgroundColor, ThemeBorderColor, ThemeFontColor},
9+
theme::{InheritableThemeTextColor, ThemeBackgroundColor, ThemeBorderColor},
1010
tokens,
1111
};
1212

@@ -42,7 +42,7 @@ pub fn subpane_header() -> impl Scene {
4242
}
4343
ThemeBackgroundColor(tokens::SUBPANE_HEADER_BG)
4444
ThemeBorderColor(tokens::SUBPANE_HEADER_BORDER)
45-
ThemeFontColor(tokens::SUBPANE_HEADER_TEXT)
45+
InheritableThemeTextColor(tokens::SUBPANE_HEADER_TEXT)
4646
InheritableFont {
4747
font: fonts::REGULAR,
4848
font_size: size::MEDIUM_FONT,

crates/bevy_feathers/src/controls/button.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{
2525
focus::FocusIndicator,
2626
font_styles::InheritableFont,
2727
rounded_corners::RoundedCorners,
28-
theme::{ThemeBackgroundColor, ThemeFontColor},
28+
theme::{InheritableThemeTextColor, ThemeBackgroundColor},
2929
tokens,
3030
};
3131

@@ -92,7 +92,7 @@ pub fn button(props: ButtonProps) -> impl Scene {
9292
TabIndex(0)
9393
FocusIndicator
9494
ThemeBackgroundColor(tokens::BUTTON_BG)
95-
ThemeFontColor(tokens::BUTTON_TEXT)
95+
InheritableThemeTextColor(tokens::BUTTON_TEXT)
9696
InheritableFont {
9797
font: fonts::REGULAR,
9898
font_size: size::MEDIUM_FONT,
@@ -168,7 +168,7 @@ pub fn button_bundle<C: SpawnableList<ChildOf> + Send + Sync + 'static, B: Bundl
168168
TabIndex(0),
169169
FocusIndicator,
170170
ThemeBackgroundColor(tokens::BUTTON_BG),
171-
ThemeFontColor(tokens::BUTTON_TEXT),
171+
InheritableThemeTextColor(tokens::BUTTON_TEXT),
172172
InheritableFont {
173173
font_size: size::MEDIUM_FONT,
174174
weight: FontWeight::NORMAL,
@@ -187,7 +187,7 @@ fn update_button_styles(
187187
Has<Pressed>,
188188
&Hovered,
189189
&ThemeBackgroundColor,
190-
&ThemeFontColor,
190+
&InheritableThemeTextColor,
191191
),
192192
Or<(
193193
Changed<Hovered>,
@@ -221,7 +221,7 @@ fn update_button_styles_remove(
221221
Has<Pressed>,
222222
&Hovered,
223223
&ThemeBackgroundColor,
224-
&ThemeFontColor,
224+
&InheritableThemeTextColor,
225225
)>,
226226
mut removed_disabled: RemovedComponents<InteractionDisabled>,
227227
mut removed_pressed: RemovedComponents<Pressed>,
@@ -255,7 +255,7 @@ fn set_button_styles(
255255
pressed: bool,
256256
hovered: bool,
257257
bg_color: &ThemeBackgroundColor,
258-
font_color: &ThemeFontColor,
258+
font_color: &InheritableThemeTextColor,
259259
commands: &mut Commands,
260260
) {
261261
let bg_token = match (variant, disabled, pressed, hovered) {
@@ -296,7 +296,7 @@ fn set_button_styles(
296296
if font_color.0 != font_color_token {
297297
commands
298298
.entity(button_ent)
299-
.insert(ThemeFontColor(font_color_token));
299+
.insert(InheritableThemeTextColor(font_color_token));
300300
}
301301

302302
// Change cursor shape

crates/bevy_feathers/src/controls/checkbox.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{
3030
cursor::EntityCursor,
3131
focus::FocusIndicator,
3232
font_styles::InheritableFont,
33-
theme::{ThemeBackgroundColor, ThemeBorderColor, ThemeFontColor},
33+
theme::{InheritableThemeTextColor, ThemeBackgroundColor, ThemeBorderColor},
3434
tokens,
3535
};
3636

@@ -84,7 +84,7 @@ pub fn checkbox(props: CheckboxProps) -> impl Scene {
8484
Hovered
8585
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer)
8686
TabIndex(0)
87-
ThemeFontColor(tokens::CHECKBOX_TEXT)
87+
InheritableThemeTextColor(tokens::CHECKBOX_TEXT)
8888
InheritableFont {
8989
font: fonts::REGULAR,
9090
font_size: size::MEDIUM_FONT,
@@ -150,7 +150,7 @@ pub fn checkbox_bundle<C: SpawnableList<ChildOf> + Send + Sync + 'static, B: Bun
150150
Hovered::default(),
151151
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer),
152152
TabIndex(0),
153-
ThemeFontColor(tokens::CHECKBOX_TEXT),
153+
InheritableThemeTextColor(tokens::CHECKBOX_TEXT),
154154
InheritableFont {
155155
font_size: size::MEDIUM_FONT,
156156
weight: FontWeight::NORMAL,
@@ -204,7 +204,7 @@ fn update_checkbox_styles(
204204
Has<Pressed>,
205205
Has<ActivateOnPress>,
206206
&Hovered,
207-
&ThemeFontColor,
207+
&InheritableThemeTextColor,
208208
),
209209
(
210210
With<CheckboxFrame>,
@@ -265,7 +265,7 @@ fn update_checkbox_styles_remove(
265265
Has<Pressed>,
266266
Has<ActivateOnPress>,
267267
&Hovered,
268-
&ThemeFontColor,
268+
&InheritableThemeTextColor,
269269
),
270270
With<CheckboxFrame>,
271271
>,
@@ -339,7 +339,7 @@ fn set_checkbox_styles(
339339
outline_bg: &ThemeBackgroundColor,
340340
outline_border: &ThemeBorderColor,
341341
mark_color: &ThemeBorderColor,
342-
font_color: &ThemeFontColor,
342+
font_color: &InheritableThemeTextColor,
343343
commands: &mut Commands,
344344
) {
345345
let outline_border_token = if checked {
@@ -432,7 +432,7 @@ fn set_checkbox_styles(
432432
if font_color.0 != font_color_token {
433433
commands
434434
.entity(checkbox_ent)
435-
.insert(ThemeFontColor(font_color_token));
435+
.insert(InheritableThemeTextColor(font_color_token));
436436
}
437437

438438
// Change cursor shape

crates/bevy_feathers/src/controls/color_plane.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ fn on_pointer_press(
317317
commands.trigger(ValueChange {
318318
source: parent.0,
319319
value: new_value,
320+
is_final: false,
320321
});
321322
}
322323
}
@@ -368,20 +369,46 @@ fn on_drag(
368369
commands.trigger(ValueChange {
369370
source: parent.0,
370371
value: new_value,
372+
is_final: false,
371373
});
372374
}
373375
}
374376
}
375377

376378
fn on_drag_end(
377379
mut drag_end: On<Pointer<DragEnd>>,
378-
mut q_color_planes: Query<&mut ColorPlaneDragState, With<ColorPlane>>,
379-
q_color_plane_inner: Query<&ChildOf, With<ColorPlaneInner>>,
380+
mut q_color_planes: Query<
381+
(&mut ColorPlaneDragState, Has<InteractionDisabled>),
382+
With<ColorPlane>,
383+
>,
384+
q_color_plane_inner: Query<
385+
(
386+
&ComputedNode,
387+
&ComputedUiRenderTargetInfo,
388+
&UiGlobalTransform,
389+
&ChildOf,
390+
),
391+
With<ColorPlaneInner>,
392+
>,
393+
ui_scale: Res<UiScale>,
394+
mut commands: Commands,
380395
) {
381-
if let Ok(parent) = q_color_plane_inner.get(drag_end.entity)
382-
&& let Ok(mut state) = q_color_planes.get_mut(parent.0)
396+
if let Ok((node, node_target, transform, parent)) = q_color_plane_inner.get(drag_end.entity)
397+
&& let Ok((mut state, disabled)) = q_color_planes.get_mut(parent.0)
383398
{
384399
drag_end.propagate(false);
400+
if state.0 && !disabled {
401+
let local_pos = transform.try_inverse().unwrap().transform_point2(
402+
drag_end.pointer_location.position * node_target.scale_factor() / ui_scale.0,
403+
);
404+
let pos = local_pos / node.size() + Vec2::splat(0.5);
405+
let new_value = pos.clamp(Vec2::ZERO, Vec2::ONE);
406+
commands.trigger(ValueChange {
407+
source: parent.0,
408+
value: new_value,
409+
is_final: true,
410+
});
411+
}
385412
state.0 = false;
386413
}
387414
}

0 commit comments

Comments
 (0)