Skip to content
Snippets Groups Projects
Unverified Commit 99431431 authored by Ygg01's avatar Ygg01
Browse files

Tried generic spinbox

parent 3a212b03
No related branches found
No related tags found
No related merge requests found
......@@ -18,7 +18,7 @@ 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 (spin_value, set_spin_value, _) = use_state!(3.0f32);
let input_styles = Style {
top: StyleProp::Value(Units::Pixels(10.0)),
......@@ -43,7 +43,7 @@ fn TextBoxExample() {
});
let on_change_spin = OnChange::new(move |event| {
set_spin_value(event.value);
set_spin_value(32.0);
});
rsx! {
......@@ -56,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)} value={spin_value} on_change={Some(on_change_spin)} />
<SpinBox<f32> styles={Some(input_styles)} value={spin_value} on_change={Some(on_change_spin)} />
</Window>
}
}
......
use std::str::FromStr;
use crate::{
core::{
render_command::RenderCommand,
......@@ -9,13 +11,19 @@ use crate::{
};
use kayak_core::{
styles::{LayoutType, StyleProp},
CursorIcon, OnLayout,
CursorIcon, Index, OnLayout, Widget,
};
use crate::widgets::{Background, Clip, OnChange, Text};
#[derive(Debug, PartialEq, Clone, Default)]
pub struct SpinBox<T> {
pub id: Index,
pub props: SpinBoxProps<T>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct SpinBoxProps {
pub struct SpinBoxProps<T> {
/// If true, prevents the widget from being focused (and consequently edited)
pub disabled: bool,
/// A callback for when the text value was changed
......@@ -26,7 +34,7 @@ pub struct SpinBoxProps {
///
/// 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,
pub value: Option<T>,
pub styles: Option<Style>,
/// Text on increment button defaults to `>`
pub incr_str: String,
......@@ -38,14 +46,13 @@ pub struct SpinBoxProps {
pub focusable: Option<bool>,
}
impl Default for SpinBoxProps {
fn default() -> SpinBoxProps {
SpinBoxProps {
impl<T> Default for SpinBoxProps<T> {
fn default() -> SpinBoxProps<T> {
Self {
incr_str: ">".into(),
decr_str: "<".into(),
disabled: Default::default(),
on_change: Default::default(),
on_change: Default::default(),
placeholder: Default::default(),
value: Default::default(),
styles: Default::default(),
......@@ -57,7 +64,10 @@ impl Default for SpinBoxProps {
}
}
impl WidgetProps for SpinBoxProps {
impl<T> WidgetProps for SpinBoxProps<T>
where
T: Widget,
{
fn get_children(&self) -> Option<Children> {
self.children.clone()
}
......@@ -86,7 +96,6 @@ impl WidgetProps for SpinBoxProps {
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FocusSpinbox(pub bool);
#[widget]
/// A widget that displays a spinnable text field
///
/// # Props
......@@ -101,121 +110,161 @@ pub struct FocusSpinbox(pub bool);
/// | `on_layout` | ✅ |
/// | `focusable` | ✅ |
///
pub fn SpinBox(props: SpinBoxProps) {
let SpinBoxProps {
on_change,
placeholder,
value,
..
} = props.clone();
props.styles = Some(
Style::default()
// Required styles
.with_style(Style {
render_command: RenderCommand::Layout.into(),
..Default::default()
})
// Apply any prop-given styles
.with_style(&props.styles)
// If not set by props, apply these styles
.with_style(Style {
top: Units::Pixels(0.0).into(),
bottom: Units::Pixels(0.0).into(),
height: Units::Pixels(26.0).into(),
cursor: CursorIcon::Text.into(),
..Default::default()
}),
);
let background_styles = Style {
background_color: Color::new(0.176, 0.196, 0.215, 1.0).into(),
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 has_focus = context.create_state(FocusSpinbox(false)).unwrap();
let mut current_value = value.clone();
let cloned_on_change = on_change.clone();
let cloned_has_focus = has_focus.clone();
props.on_event = Some(OnEvent::new(move |_, event| match event.event_type {
EventType::CharInput { c } => {
if !cloned_has_focus.get().0 {
return;
}
if is_backspace(c) {
if !current_value.is_empty() {
current_value.truncate(current_value.len() - 1);
impl<T> Widget for SpinBox<T>
where
T: Widget + ToString + FromStr + Default,
{
type Props = SpinBoxProps<T>;
fn constructor(props: Self::Props) -> Self
where
Self: Sized,
{
Self {
id: Index::default(),
props,
}
}
fn get_id(&self) -> Index {
self.id
}
fn set_id(&mut self, id: Index) {
self.id = id;
}
fn get_props(&self) -> &Self::Props {
&self.props
}
fn get_props_mut(&mut self) -> &mut Self::Props {
&mut self.props
}
fn render(&mut self, context: &mut kayak_core::KayakContextRef) {
let children = self.props.get_children();
let mut props = self.props.clone();
let SpinBoxProps {
on_change,
placeholder,
value,
..
} = props.clone();
props.styles = Some(
Style::default()
// Required styles
.with_style(Style {
render_command: RenderCommand::Layout.into(),
..Default::default()
})
// Apply any prop-given styles
.with_style(&props.styles)
// If not set by props, apply these styles
.with_style(Style {
top: Units::Pixels(0.0).into(),
bottom: Units::Pixels(0.0).into(),
height: Units::Pixels(26.0).into(),
cursor: CursorIcon::Text.into(),
..Default::default()
}),
);
let background_styles = Style {
background_color: Color::new(0.176, 0.196, 0.215, 1.0).into(),
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 has_focus = context.create_state(FocusSpinbox(false)).unwrap();
let mut current_value = value.clone().map_or(String::new(), |f| { f.to_string()});
let cloned_on_change = on_change.clone();
let cloned_has_focus = has_focus.clone();
props.on_event = Some(OnEvent::new(move |_, event| match event.event_type {
EventType::CharInput { c } => {
if !cloned_has_focus.get().0 {
return;
}
} else if !c.is_control() {
current_value.push(c);
}
if let Some(on_change) = cloned_on_change.as_ref() {
if let Ok(mut on_change) = on_change.0.write() {
on_change(ChangeEvent {
value: current_value.clone(),
});
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);
}
if let Some(on_change) = cloned_on_change.as_ref() {
if let Ok(mut on_change) = on_change.0.write() {
on_change(ChangeEvent {
value: current_value.clone(),
});
}
}
}
}
EventType::Focus => cloned_has_focus.set(FocusSpinbox(true)),
EventType::Blur => cloned_has_focus.set(FocusSpinbox(false)),
_ => {}
}));
let text_styles = if value.is_empty() || (has_focus.get().0 && value.is_empty()) {
Style {
color: Color::new(0.5, 0.5, 0.5, 1.0).into(),
..Style::default()
}
} else {
Style {
width: Units::Stretch(100.0).into(),
EventType::Focus => cloned_has_focus.set(FocusSpinbox(true)),
EventType::Blur => cloned_has_focus.set(FocusSpinbox(false)),
_ => {}
}));
let text_styles = if value.is_none() || (has_focus.get().0 && value.is_none()) {
Style {
color: Color::new(0.5, 0.5, 0.5, 1.0).into(),
..Style::default()
}
} else {
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()
});
// if current_value.is_empty() {
// current_value = placeholder.unwrap_or(current_value);
// };
// let value = if current_value.is_empty() {
// None
// } else {
// T::from_str(&current_value).ok()
// };
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;
let content = value.clone().map_or(String::new(), |f| { f.to_string()});
rsx! {
<Background styles={Some(background_styles)}>
<Clip styles={Some(inline_style)}>
<Button styles={button_style}>
<Text content={decr_str} />
</Button>
<Text
content={content}
size={14.0}
styles={Some(text_styles)}
/>
<Button styles={button_style}>
<Text content={incr_str} />
</Button>
</Clip>
</Background>
}
};
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 styles={Some(inline_style)}>
<Button styles={button_style}>
<Text content={decr_str} />
</Button>
<Text
content={value}
size={14.0}
styles={Some(text_styles)}
/>
<Button styles={button_style}>
<Text content={incr_str} />
</Button>
</Clip>
</Background>
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment