Skip to main content

feathers_number_input/
feathers_number_input.rs

1//! This example shows off the various Bevy Feathers widgets.
2
3use std::f32::consts::PI;
4
5use bevy::{
6    feathers::{
7        controls::*,
8        dark_theme::create_dark_theme,
9        display::label,
10        theme::{ThemeBackgroundColor, UiTheme},
11        tokens, FeathersPlugins,
12    },
13    input_focus::tab_navigation::TabGroup,
14    prelude::*,
15    ui::InteractionDisabled,
16    ui_widgets::ValueChange,
17};
18
19fn main() {
20    App::new()
21        .add_plugins((DefaultPlugins, FeathersPlugins))
22        .insert_resource(UiTheme(create_dark_theme()))
23        .add_systems(Startup, scene.spawn())
24        // .add_systems(Update, update_colors)
25        .run();
26}
27
28fn scene() -> impl SceneList {
29    bsn_list! {
30        Camera2d
31        --
32        @demo_root()
33    }
34}
35
36fn demo_root() -> impl Scene {
37    bsn! {
38        Node {
39            width: percent(100),
40            height: percent(100),
41            align_items: AlignItems::Start,
42            justify_content: JustifyContent::Start,
43            display: Display::Flex,
44            flex_direction: FlexDirection::Column,
45            flex_wrap: FlexWrap::Wrap,
46            padding: px(8),
47            row_gap: px(8),
48        }
49        TabGroup
50        ThemeBackgroundColor(tokens::WINDOW_BG)
51        Children[
52            @demo_field_f32("none (bare)", 1.0, bsn! {})
53            --
54            @demo_field_f32("soft limit", 2.0, bsn! {
55                SoftLimit(NumberInputRange::F32(0.0..=10.0))
56            })
57            --
58            @demo_field_f32("hard limit", 3.0, bsn! {
59                HardLimit(NumberInputRange::F32(-100.0..=100.0))
60            })
61            --
62            @demo_field_f32("soft + hard", 4.0, bsn! {
63                SoftLimit(NumberInputRange::F32(0.0..=10.0))
64                HardLimit(NumberInputRange::F32(-100.0..=100.0))
65            })
66            --
67            @demo_field_f32("precision(0)", 5.0, bsn! {
68                NumberInputPrecision(0)
69            })
70            --
71            @demo_field_f32("precision(2)", 6.0, bsn! {
72                NumberInputPrecision(2)
73            })
74            --
75            @demo_field_f32("precision(4)", 7.0, bsn! {
76                NumberInputPrecision(4)
77            })
78            --
79            @demo_field_f32("step(1.0)", 8.0, bsn! {
80                NumberInputStep(1.0f64)
81            })
82            --
83            @demo_field_f64("f64: soft limit", 1.0f64, bsn! {
84                SoftLimit(NumberInputRange::F64(0.0f64..=10.0f64))
85            })
86            --
87            @demo_field_f64("f64: soft limit + precision(2)", 1.0f64, bsn! {
88                SoftLimit(NumberInputRange::F64(0.0f64..=10.0f64))
89                NumberInputPrecision(2)
90            })
91            --
92            @demo_field_i32("i32: bare", 1, bsn! {})
93            --
94            @demo_field_i32("i32: soft limit", 1, bsn! {
95                SoftLimit(NumberInputRange::I32(0..=10))
96            })
97            --
98            @demo_field_f32_with_sigil("precision(2) + sigil", 6.0, bsn! {
99                NumberInputPrecision(2)
100            })
101            --
102            @demo_field_f32("soft limit + disabled", 2.0, bsn! {
103                InteractionDisabled
104                SoftLimit(NumberInputRange::F32(0.0..=10.0))
105            })
106            --
107            @demo_field_f32("hard limit + wrap", 0.0, bsn! {
108                HardLimit(NumberInputRange::F32(-180.0..=180.0))
109                NumberInputWrap::Wrap
110            })
111            --
112            @demo_field_f32("in meters", 2.0, bsn! {
113                NumberInputUnits::new(&LengthMeters)
114            })
115            --
116            @demo_field_f32("in seconds", 2.0, bsn! {
117                NumberInputUnits::new(&TimeSeconds)
118            })
119            --
120            @demo_field_f32("in degrees", PI, bsn! {
121                NumberInputUnits::new(&AngleDegrees)
122            })
123        ]
124    }
125}
126
127fn demo_field_f32(label_text: &str, value: f32, options: impl Scene) -> impl Scene {
128    bsn! {
129        Node {
130            display: Display::Flex,
131            flex_direction: FlexDirection::Column,
132            align_items: AlignItems::Start,
133            width: px(200),
134            row_gap: px(4),
135        }
136        Children [
137            @label(label_text)
138            --
139            Node {
140                display: Display::Flex,
141                flex_direction: FlexDirection::Row,
142                align_items: AlignItems::Center,
143                align_self: AlignSelf::Stretch,
144                justify_content: JustifyContent::SpaceBetween,
145            }
146            Children [
147                @FeathersNumberInput
148                NumberInputValue::F32(value)
149                @{options}
150                Node {
151                    flex_grow: 1.0,
152                    max_width: px(120),
153                }
154                on(
155                    |value_change: On<ValueChange<f32>>, mut commands: Commands| {
156                    commands.entity(value_change.event_target())
157                        .insert(NumberInputValue::F32(value_change.value));
158                })
159                --
160                #Output
161                @label("-")
162            ]
163        ]
164    }
165}
166
167fn demo_field_f32_with_sigil(label_text: &str, value: f32, options: impl Scene) -> impl Scene {
168    bsn! {
169        Node {
170            display: Display::Flex,
171            flex_direction: FlexDirection::Column,
172            align_items: AlignItems::Start,
173            width: px(200),
174        }
175        Children [
176            @label(label_text)
177            --
178            Node {
179                display: Display::Flex,
180                flex_direction: FlexDirection::Row,
181                align_items: AlignItems::Center,
182                align_self: AlignSelf::Stretch,
183                justify_content: JustifyContent::SpaceBetween,
184            }
185            Children [
186                @FeathersNumberInput {
187                    @sigil_color: tokens::TEXT_INPUT_X_AXIS,
188                    @label_text: "X",
189                }
190                NumberInputValue::F32(value)
191                @{options}
192                Node {
193                    flex_grow: 1.0,
194                    max_width: px(120),
195                }
196                on(
197                    |value_change: On<ValueChange<f32>>, mut commands: Commands| {
198                    commands.entity(value_change.event_target())
199                        .insert(NumberInputValue::F32(value_change.value));
200                })
201                --
202                #Output
203                @label("-")
204            ]
205        ]
206    }
207}
208
209fn demo_field_f64(label_text: &str, value: f64, options: impl Scene) -> impl Scene {
210    bsn! {
211        Node {
212            display: Display::Flex,
213            flex_direction: FlexDirection::Column,
214            align_items: AlignItems::Start,
215            width: px(200),
216        }
217        Children [
218            @label(label_text)
219            --
220            Node {
221                display: Display::Flex,
222                flex_direction: FlexDirection::Row,
223                align_items: AlignItems::Center,
224                align_self: AlignSelf::Stretch,
225                justify_content: JustifyContent::SpaceBetween,
226            }
227            Children [
228                @FeathersNumberInput
229                NumberInputValue::F64(value)
230                @{options}
231                Node {
232                    flex_grow: 1.0,
233                    max_width: px(120),
234                }
235                on(
236                    |value_change: On<ValueChange<f64>>, mut commands: Commands| {
237                    commands.entity(value_change.event_target())
238                        .insert(NumberInputValue::F64(value_change.value));
239                })
240                --
241                #Output
242                @label("-")
243            ]
244        ]
245    }
246}
247
248fn demo_field_i32(label_text: &str, value: i32, options: impl Scene) -> impl Scene {
249    bsn! {
250        Node {
251            display: Display::Flex,
252            flex_direction: FlexDirection::Column,
253            align_items: AlignItems::Start,
254            width: px(200),
255        }
256        Children [
257            @label(label_text)
258            --
259            Node {
260                display: Display::Flex,
261                flex_direction: FlexDirection::Row,
262                align_items: AlignItems::Center,
263                align_self: AlignSelf::Stretch,
264                justify_content: JustifyContent::SpaceBetween,
265            }
266            Children [
267                @FeathersNumberInput
268                NumberInputValue::I32(value)
269                @{options}
270                Node {
271                    flex_grow: 1.0,
272                    max_width: px(120),
273                }
274                on(
275                    |value_change: On<ValueChange<i32>>, mut commands: Commands| {
276                    commands.entity(value_change.event_target())
277                        .insert(NumberInputValue::I32(value_change.value));
278                })
279                --
280                #Output
281                @label("-")
282            ]
283        ]
284    }
285}