diff --git a/examples/text_box.rs b/examples/text_box.rs index 2c0bca89258cc9a4909cfa3a8fd358c8f143f92a..5a51d0217d585c4d016e8281949d5926673e0f1b 100644 --- a/examples/text_box.rs +++ b/examples/text_box.rs @@ -5,19 +5,20 @@ use bevy::{ }; use kayak_core::Color; use kayak_render_macros::use_state; -use kayak_ui::{bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}}; +use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}; use kayak_ui::core::{ render, rsx, styles::{Style, StyleProp, Units}, widget, Index, }; -use kayak_ui::widgets::{App, OnChange, Window, TextBox, SpinBox}; +use kayak_ui::widgets::{App, Inspector, OnChange, SpinBox, TextBox, Window}; #[widget] fn TextBoxExample() { let (value, set_value, _) = use_state!("I started with a value!".to_string()); let (empty_value, set_empty_value, _) = use_state!("".to_string()); let (red_value, set_red_value, _) = use_state!("This text is red".to_string()); + let (spin_value, set_spin_value, _) = use_state!("3".to_string()); let input_styles = Style { top: StyleProp::Value(Units::Pixels(10.0)), @@ -41,8 +42,12 @@ fn TextBoxExample() { set_red_value(event.value); }); + let on_change_spin = OnChange::new(move |event| { + set_spin_value(event.value); + }); + rsx! { - <Window position={(50.0, 50.0)} size={(300.0, 300.0)} title={"TextBox Example".to_string()}> + <Window position={(50.0, 50.0)} size={(500.0, 300.0)} title={"TextBox Example".to_string()}> <TextBox styles={Some(input_styles)} value={value} on_change={Some(on_change)} /> <TextBox styles={Some(input_styles)} @@ -51,7 +56,7 @@ fn TextBoxExample() { placeholder={Some("This is a placeholder".to_string())} /> <TextBox styles={Some(red_text_styles)} value={red_value} on_change={Some(on_change_red)} /> - <SpinBox styles={Some(input_styles)} /> + <SpinBox styles={Some(input_styles)} value={spin_value} on_change={Some(on_change_spin)} /> </Window> } } @@ -69,6 +74,7 @@ fn startup( render! { <App> <TextBoxExample /> + <Inspector /> </App> } }); diff --git a/src/widgets/on_change.rs b/src/widgets/on_change.rs index 1aa632f4daf197621caba05742c3ab65651e9767..b4fdebba15a0ea64cf3730e6eb54f7541c9a320e 100644 --- a/src/widgets/on_change.rs +++ b/src/widgets/on_change.rs @@ -24,4 +24,4 @@ impl std::fmt::Debug for OnChange { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("OnChange").finish() } -} \ No newline at end of file +} diff --git a/src/widgets/spin_box.rs b/src/widgets/spin_box.rs index 34d756c20384781a3636650dcbf042ca2846295c..fa160c1016fb440fb9b7305e4144e302375f6f84 100644 --- a/src/widgets/spin_box.rs +++ b/src/widgets/spin_box.rs @@ -1,14 +1,20 @@ -use crate::{core::{ - render_command::RenderCommand, - rsx, - styles::{Corner, Style, Units}, - widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps, -}, widgets::ChangeEvent}; -use kayak_core::{OnLayout, CursorIcon}; - -use crate::widgets::{Background, Clip, Text, OnChange}; - -#[derive(Default, Debug, PartialEq, Clone)] +use crate::{ + core::{ + render_command::RenderCommand, + rsx, + styles::{Corner, Style, Units}, + widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps, + }, + widgets::{Button, ChangeEvent}, +}; +use kayak_core::{ + styles::{LayoutType, StyleProp}, + CursorIcon, OnLayout, +}; + +use crate::widgets::{Background, Clip, OnChange, Text}; + +#[derive(Debug, PartialEq, Clone)] pub struct SpinBoxProps { /// If true, prevents the widget from being focused (and consequently edited) pub disabled: bool, @@ -22,12 +28,35 @@ pub struct SpinBoxProps { /// You can use the [`on_change`] callback to update this prop as the user types. pub value: String, pub styles: Option<Style>, + /// Text on increment button defaults to `>` + pub incr_str: String, + /// Text on decrement button defaults to `<` + pub decr_str: String, pub children: Option<Children>, pub on_event: Option<OnEvent>, pub on_layout: Option<OnLayout>, pub focusable: Option<bool>, } + +impl Default for SpinBoxProps { + fn default() -> SpinBoxProps { + SpinBoxProps { + incr_str: ">".into(), + decr_str: "<".into(), + disabled: Default::default(), + on_change: Default::default(), + placeholder: Default::default(), + value: Default::default(), + styles: Default::default(), + children: Default::default(), + on_event: Default::default(), + on_layout: Default::default(), + focusable: Default::default(), + } + } +} + impl WidgetProps for SpinBoxProps { fn get_children(&self) -> Option<Children> { self.children.clone() @@ -58,11 +87,11 @@ impl WidgetProps for SpinBoxProps { pub struct FocusSpinbox(pub bool); #[widget] -/// A widget that displays a text input field +/// A widget that displays a spinnable text field /// /// # Props /// -/// __Type:__ [`TextBoxProps`] +/// __Type:__ [`SpinBoxProps`] /// /// | Common Prop | Accepted | /// | :---------: | :------: | @@ -145,34 +174,54 @@ pub fn SpinBox(props: SpinBoxProps) { ..Style::default() } } else { - Style::default() + Style { + width: Units::Stretch(100.0).into(), + ..Style::default() + } }; + let button_style = Some(Style { + height: Units::Pixels(24.0).into(), + width: Units::Pixels(24.0).into(), + ..Default::default() + }); + let value = if value.is_empty() { placeholder.unwrap_or_else(|| value.clone()) } else { value }; + let inline_style = Style { + layout_type: StyleProp::Value(LayoutType::Row), + ..Style::default() + }; + + let incr_str = props.clone().incr_str; + let decr_str = props.clone().decr_str; + rsx! { <Background styles={Some(background_styles)}> - <Clip> + <Clip styles={Some(inline_style)}> + <Button styles={button_style}> + <Text content={decr_str} /> + </Button> <Text content={value} size={14.0} - line_height={Some(22.0)} styles={Some(text_styles)} /> + <Button styles={button_style}> + <Text content={incr_str} /> + </Button> </Clip> </Background> } } - /// 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}' } - diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs index 2113edc3e50de5fa363d91d57944067b05d74ffa..e9cfc8ad5e4d88e315f0da6d7ff5de0c2424002c 100644 --- a/src/widgets/text_box.rs +++ b/src/widgets/text_box.rs @@ -1,12 +1,15 @@ -use crate::{core::{ - render_command::RenderCommand, - rsx, - styles::{Corner, Style, Units}, - widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps, -}, widgets::ChangeEvent}; +use crate::{ + core::{ + render_command::RenderCommand, + rsx, + styles::{Corner, Style, Units}, + widget, Bound, Children, Color, EventType, MutableBound, OnEvent, WidgetProps, + }, + widgets::ChangeEvent, +}; use kayak_core::{CursorIcon, OnLayout}; -use crate::widgets::{Background, Clip, Text, OnChange}; +use crate::widgets::{Background, Clip, OnChange, Text}; /// Props used by the [`TextBox`] widget #[derive(Default, Debug, PartialEq, Clone)]