Newer
Older
use bevy::prelude::{Bundle, Color, Commands, Component, Entity, In, Query};
event::{Event, EventType},
event_dispatcher::EventDispatcherContext,
on_event::OnEvent,
on_layout::OnLayout,
prelude::{KChildren, KayakWidgetContext, OnChange},
styles::{Corner, KStyle, RenderCommand, StyleProp, Units},
widgets::{
text::{TextProps, TextWidgetBundle},
BackgroundBundle, ClipBundle,
#[derive(Component, PartialEq, Default, Debug, Clone)]
/// If true, prevents the widget from being focused (and consequently edited)
/// The text to display when the user input is empty
/// The user input
///
/// This is a controlled state. You _must_ set this to the value to you wish to be displayed.
/// You can use the [`on_change`] callback to update this prop as the user types.
pub value: String,
#[derive(Component, Default, Clone, PartialEq)]
pub struct TextBoxValue(pub String);
impl Widget for TextBoxProps {}

StarArawn
committed
/// A text box allows users to input text.
/// This text box is fairly simple and only supports basic input.
#[derive(Bundle)]
pub struct TextBoxBundle {
pub text_box: TextBoxProps,
pub styles: KStyle,
pub on_event: OnEvent,
pub on_layout: OnLayout,
pub on_change: OnChange,
pub focusable: Focusable,
pub widget_name: WidgetName,
}
impl Default for TextBoxBundle {
fn default() -> Self {
Self {
text_box: Default::default(),
styles: Default::default(),
on_event: Default::default(),
on_layout: Default::default(),
on_change: Default::default(),
focusable: Default::default(),
widget_name: TextBoxProps::default().get_name(),
}
}
}
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut query: Query<(&mut KStyle, &TextBoxProps, &mut OnEvent, &OnChange)>,
if let Ok((mut styles, text_box, mut on_event, on_change)) = query.get_mut(entity) {
let state_entity = widget_context.use_state::<TextBoxState>(
&mut commands,
entity,
TextBoxState::default(),
);
*styles = KStyle::default()
// Required styles
.with_style(KStyle {
render_command: RenderCommand::Layout.into(),
..Default::default()
})
// Apply any prop-given styles
.with_style(&*styles)
// If not set by props, apply these styles
.with_style(KStyle {
top: Units::Pixels(0.0).into(),
bottom: Units::Pixels(0.0).into(),
// cursor: CursorIcon::Text.into(),
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
});
let background_styles = KStyle {
render_command: StyleProp::Value(RenderCommand::Quad),
background_color: StyleProp::Value(Color::rgba(0.176, 0.196, 0.215, 1.0)),
border_radius: Corner::all(5.0).into(),
height: Units::Pixels(26.0).into(),
padding_left: Units::Pixels(5.0).into(),
padding_right: Units::Pixels(5.0).into(),
..Default::default()
};
let current_value = text_box.value.clone();
let cloned_on_change = on_change.clone();
*on_event = OnEvent::new(
move |In((event_dispatcher_context, _, mut event, _entity)): In<(
EventDispatcherContext,
WidgetState,
Event,
Entity,
)>,
mut state_query: Query<&mut TextBoxState>| {
match event.event_type {
EventType::CharInput { c } => {
let mut current_value = current_value.clone();
let cloned_on_change = cloned_on_change.clone();
if let Ok(state) = state_query.get(state_entity) {
if !state.focused {
} else {
return (event_dispatcher_context, event);
if is_backspace(c) {
if !current_value.is_empty() {
current_value.truncate(current_value.len() - 1);
} else if !c.is_control() {
current_value.push(c);
cloned_on_change.set_value(current_value);
event.add_system(cloned_on_change);
}
EventType::Focus => {
if let Ok(mut state) = state_query.get_mut(state_entity) {
state.focused = true;
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
EventType::Blur => {
if let Ok(mut state) = state_query.get_mut(state_entity) {
state.focused = false;
}
}
_ => {}
}
(event_dispatcher_context, event)
},
);
let parent_id = Some(entity);
rsx! {
<BackgroundBundle styles={background_styles}>
<ClipBundle styles={KStyle {
height: Units::Pixels(26.0).into(),
padding_left: StyleProp::Value(Units::Stretch(0.0)),
padding_right: StyleProp::Value(Units::Stretch(0.0)),
padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
padding_top: StyleProp::Value(Units::Stretch(1.0)),
..Default::default()
}}>
<TextWidgetBundle
text={TextProps {
content: text_box.value.clone(),
size: 14.0,
line_height: Some(18.0),
..Default::default()
}}
/>
</ClipBundle>
</BackgroundBundle>
/// Checks if the given character contains the "Backspace" sequence
///
/// Context: [Wikipedia](https://en.wikipedia.org/wiki/Backspace#Common_use)
fn is_backspace(c: char) -> bool {
c == '\u{8}' || c == '\u{7f}'