From e0852972917918b970df637be5ee9ec30747577b Mon Sep 17 00:00:00 2001 From: veeso Date: Mon, 7 Jun 2021 09:46:40 +0200 Subject: [PATCH 1/5] init 0.4.0 --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- README.md | 8 ++++---- src/lib.rs | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e19b796..81f4891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog - [Changelog](#changelog) + - [0.4.0](#040) - [0.3.2](#032) - [0.3.1](#031) - [0.3.0](#030) @@ -10,6 +11,12 @@ --- +## 0.4.0 + +Released on FIXME: + + + ## 0.3.2 Released on 04/06/2021 diff --git a/Cargo.toml b/Cargo.toml index 1a477fb..496f42b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuirealm" -version = "0.3.2" +version = "0.4.0" authors = ["Christian Visintin"] edition = "2018" categories = ["command-line-utilities"] diff --git a/README.md b/README.md index da3249b..60160c5 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@

-[![License: MIT](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/licenses/MIT) [![Stars](https://img.shields.io/github/stars/veeso/tui-realm.svg)](https://github.com/veeso/tui-realm) [![Downloads](https://img.shields.io/crates/d/tuirealm.svg)](https://crates.io/crates/tuirealm) [![Crates.io](https://img.shields.io/badge/crates.io-v0.3.2-orange.svg)](https://crates.io/crates/tuirealm) [![Docs](https://docs.rs/tuirealm/badge.svg)](https://docs.rs/tuirealm) +[![License: MIT](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/licenses/MIT) [![Stars](https://img.shields.io/github/stars/veeso/tui-realm.svg)](https://github.com/veeso/tui-realm) [![Downloads](https://img.shields.io/crates/d/tuirealm.svg)](https://crates.io/crates/tuirealm) [![Crates.io](https://img.shields.io/badge/crates.io-v0.4.0-orange.svg)](https://crates.io/crates/tuirealm) [![Docs](https://docs.rs/tuirealm/badge.svg)](https://docs.rs/tuirealm) [![Build](https://github.com/veeso/tui-realm/workflows/Linux/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Build](https://github.com/veeso/tui-realm/workflows/MacOS/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Build](https://github.com/veeso/tui-realm/workflows/Windows/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Coverage Status](https://coveralls.io/repos/github/veeso/tui-realm/badge.svg?branch=main)](https://coveralls.io/github/veeso/tui-realm?branch=main) Developed by Christian Visintin -Current version: 0.3.2 (04/06/2021) +Current version: 0.4.0 FIXME: (04/06/2021) --- @@ -53,13 +53,13 @@ Tui-realm also comes with a built-in standard library of components you may find ### Add tui-realm to your Cargo.toml 🦀 ```toml -tuirealm = "0.3.2" +tuirealm = "0.4.0" ``` or if you want to include the [standard component library](#standard-component-library-)... ```toml -tuirealm = { "version" = "0.3.2", features = [ "with-components" ] } +tuirealm = { "version" = "0.4.0", features = [ "with-components" ] } ``` Since this library requires `crossterm` too, you'll also need to add it to your Cargo.toml diff --git a/src/lib.rs b/src/lib.rs index 28205a4..d9aa5db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! ### Adding `tui-realm` as dependency //! //! ```toml -//! tuirealm = "0.3.2" +//! tuirealm = "0.4.0" //! ``` //! or if you want the std components library //! From 1f0d80120e56f3e17e2f999644c752dd521305a9 Mon Sep 17 00:00:00 2001 From: veeso Date: Mon, 7 Jun 2021 11:54:17 +0200 Subject: [PATCH 2/5] New props API --- CHANGELOG.md | 8 ++- docs/new-components.md | 87 +++++++++++++++++++++++++++++++-- examples/components/counter.rs | 10 ++-- examples/gallery.rs | 7 +-- src/components/checkbox.rs | 15 ++++-- src/components/input.rs | 89 ++++++++++++++++++++++++++-------- src/components/progress_bar.rs | 12 +++-- src/components/radio.rs | 19 +++++--- src/props/builder.rs | 41 ++++------------ src/props/mod.rs | 16 +++--- 10 files changed, 218 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f4891..0b0a708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,13 @@ Released on FIXME: - +- Another **Prop API Update** + - Removed `input_len` and `input_type` from properties. Use `own` instead with new `PropValue` + - Added `Color` and `InputType` to `PropValue` + - Removed `value` from `Props` + - Added `own`: key-value storage (`HashMap<&'static str, PropPayload>`) to store any value into properties. +- Components: + - Added `with_validation` to `Input` component to validate with regex ## 0.3.2 diff --git a/docs/new-components.md b/docs/new-components.md index b7fa2ef..e1fa442 100644 --- a/docs/new-components.md +++ b/docs/new-components.md @@ -5,6 +5,9 @@ - [Setup](#setup) - [States](#states) - [Properties](#properties) + - [About properties](#about-properties) + - [Texts](#texts) + - [PropPayload](#proppayload) - [Implement Component trait](#implement-component-trait) - [Render](#render) - [Update](#update) @@ -186,13 +189,91 @@ impl CounterPropsBuilder { pub fn with_value(&mut self, counter: usize) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = PropPayload::One(PropValue::Usize(counter)); + props.own.insert("value", PropPayload::One(PropValue::Usize(counter))); } self } } ``` +### About properties + +The property struct contains basically everything you need to create the best components. When implementing a builder you can work with these attributes: + +- **visible**: a boolean which should be used to indicate whether the component must be rendered or not +- **foreground**: the foreground color. Usually each tui widget has a foreground +- **background**: the background color. Usually each tui widget has a background +- **borders**: defines the style and the properties for the widget block +- **modifiers**: the modifiers for the text (default). To differentiate modifiers across text parts, define a style for the spans. +- **palette**: defines the color palette. Useful if you want to define more colors, than foreground and background. You could also use `own`, but own wasn't there when palette was defined for the first time. +- **texts**: contains the texts for the components. For more details read [texts](#texts) +- **own**: an key-value storage to store custom values. The values must use the `PropPayload` syntax. For more details read [PropPayload](#proppayload) + +#### Texts + +Component texts are defined inside a struct called `TextParts` which is defined as: + +```rust +pub struct TextParts { + pub title: Option, + pub spans: Option>, + pub table: Option, // First vector is rows, inner vec is column +} +``` + +where: + +- **title** should describe the title for the container +- **spans** is a multi-part text with its own style +- **table** should be used to create a row x cols text + +In general spans and table should never been used at the same time. + +To build text parts there are some helpers such as `TextSpanBuilder` and `TableBuilder`. + +#### PropPayload + +The PropPayload is very similiar to `Payload`, but this one is used only to store properties into the component. +The payload supports the same data types as `Payload`: + +```rust +pub enum PropPayload { + One(PropValue), + Tup2((PropValue, PropValue)), + Tup3((PropValue, PropValue, PropValue)), + Tup4((PropValue, PropValue, PropValue, PropValue)), + Vec(Vec), + Map(HashMap), + Linked(LinkedList), + None, +} +``` + +while values are somehow different from `Value`: + +```rust +pub enum PropValue { + Bool(bool), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), + Usize(usize), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + I128(i128), + Isize(isize), + F64(f64), + F32(f32), + Str(String), + Color(Color), + InputType(InputType), +} +``` + ## Implement Component trait Finally we just need to implement the Component trait for our component, but first we need to define it: @@ -209,7 +290,7 @@ impl Counter { pub fn new(props: Props) -> Self { let mut states: OwnStates = OwnStates::default(); // Init counter - if let PropPayload::One(PropValue::Unsigned(val)) = &props.value { + if let Some(PropPayload::One(PropValue::Unsigned(val))) = props.own.get("input") { states.counter = *val; } Counter { props, states } @@ -266,7 +347,7 @@ Update, as you might know must update the component properties. This also return fn update(&mut self, props: Props) -> Msg { let prev_value = self.states.counter; // Get value - if let PropPayload::One(PropValue::Unsigned(val)) = &props.value { + if let Some(PropPayload::One(PropValue::Unsigned(val))) = props.own.get("input") { self.states.counter = *val; } self.props = props; diff --git a/examples/components/counter.rs b/examples/components/counter.rs index 5484d0a..ee1082b 100644 --- a/examples/components/counter.rs +++ b/examples/components/counter.rs @@ -55,6 +55,8 @@ impl OwnStates { // -- Props +const PROP_VALUE: &str = "value"; + pub struct CounterPropsBuilder { props: Option, } @@ -134,7 +136,9 @@ impl CounterPropsBuilder { pub fn with_value(&mut self, counter: usize) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = PropPayload::One(PropValue::Usize(counter)); + props + .own + .insert(PROP_VALUE, PropPayload::One(PropValue::Usize(counter))); } self } @@ -151,7 +155,7 @@ impl Counter { pub fn new(props: Props) -> Self { let mut states: OwnStates = OwnStates::default(); // Init counter - if let PropPayload::One(PropValue::Usize(val)) = &props.value { + if let Some(PropPayload::One(PropValue::Usize(val))) = props.own.get(PROP_VALUE) { states.counter = *val; } Counter { props, states } @@ -188,7 +192,7 @@ impl Component for Counter { fn update(&mut self, props: Props) -> Msg { let prev_value = self.states.counter; // Get value - if let PropPayload::One(PropValue::Usize(val)) = &props.value { + if let Some(PropPayload::One(PropValue::Usize(val))) = props.own.get(PROP_VALUE) { self.states.counter = *val; } self.props = props; diff --git a/examples/gallery.rs b/examples/gallery.rs index 3c4a3ee..8482acb 100644 --- a/examples/gallery.rs +++ b/examples/gallery.rs @@ -156,7 +156,8 @@ fn init_view() -> View { .with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow) .with_foreground(Color::LightYellow) .with_input(InputType::Text) - .with_label(String::from("Type in your password")) + .with_input_len(16) + .with_label(String::from("Type in your username (max length 16)")) .build(), )), ); @@ -499,8 +500,8 @@ impl Update for Model { fn update_progress(view: &mut View) -> Option<(String, Msg)> { let props = view.get_props(COMPONENT_PROGBAR).unwrap(); - let new_prog: f64 = match props.value { - PropPayload::One(PropValue::F64(val)) => match val + 0.05 > 1.0 { + let new_prog: f64 = match props.own.get("progress") { + Some(PropPayload::One(PropValue::F64(val))) => match val + 0.05 > 1.0 { true => 0.0, false => val + 0.05, }, diff --git a/src/components/checkbox.rs b/src/components/checkbox.rs index ce7be6f..154f59e 100644 --- a/src/components/checkbox.rs +++ b/src/components/checkbox.rs @@ -37,6 +37,8 @@ use crate::tui::{ }; use crate::{Canvas, Component, Event, Msg, Payload, Value}; +const PROP_CHOICES: &str = "choices"; + // -- Props pub struct CheckboxPropsBuilder { @@ -135,10 +137,13 @@ impl CheckboxPropsBuilder { /// ### with_value /// - /// Set initial value for choice + /// Set selected choices pub fn with_value(&mut self, choices: Vec) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = PropPayload::Vec(choices.into_iter().map(PropValue::Usize).collect()); + props.own.insert( + PROP_CHOICES, + PropPayload::Vec(choices.into_iter().map(PropValue::Usize).collect()), + ); } self } @@ -246,7 +251,7 @@ impl Checkbox { // Update choices (vec of TextSpan to String) states.set_choices(props.texts.spans.as_ref().unwrap_or(&Vec::new())); // Get value - if let PropPayload::Vec(choices) = &props.value { + if let Some(PropPayload::Vec(choices)) = &props.own.get(PROP_CHOICES) { states.selection = choices .clone() .iter() @@ -327,7 +332,7 @@ impl Component for Checkbox { self.states .set_choices(props.texts.spans.as_ref().unwrap_or(&Vec::new())); // Get value - if let PropPayload::Vec(choices) = &props.value { + if let Some(PropPayload::Vec(choices)) = &props.own.get(PROP_CHOICES) { self.states.selection = choices .clone() .iter() @@ -515,7 +520,7 @@ mod test { assert_eq!(component.props.borders.variant, BorderType::Double); assert_eq!(component.props.borders.color, Color::Red); assert_eq!( - component.props.value, + *component.props.own.get(PROP_CHOICES).unwrap(), PropPayload::Vec(vec![PropValue::Usize(1), PropValue::Usize(5)]) ); // Verify states diff --git a/src/components/input.rs b/src/components/input.rs index ed02ad0..71850bb 100644 --- a/src/components/input.rs +++ b/src/components/input.rs @@ -37,6 +37,9 @@ use crate::tui::{ use crate::{Canvas, Component, Event, InputType, Msg, Payload, Value}; // -- Props +const PROP_VALUE: &str = "value"; +const PROP_INPUT_TYPE: &str = "input_type"; +const PROP_INPUT_LENGHT: &str = "input_length"; pub struct InputPropsBuilder { props: Option, @@ -131,7 +134,10 @@ impl InputPropsBuilder { /// Set input type for component pub fn with_input(&mut self, input_type: InputType) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.input_type = input_type; + props.own.insert( + PROP_INPUT_TYPE, + PropPayload::One(PropValue::InputType(input_type)), + ); } self } @@ -141,7 +147,9 @@ impl InputPropsBuilder { /// Set max input len pub fn with_input_len(&mut self, len: usize) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.input_len = Some(len); + props + .own + .insert(PROP_INPUT_LENGHT, PropPayload::One(PropValue::Usize(len))); } self } @@ -151,7 +159,9 @@ impl InputPropsBuilder { /// Set initial value for component pub fn with_value(&mut self, value: String) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = PropPayload::One(PropValue::Str(value)); + props + .own + .insert(PROP_VALUE, PropPayload::One(PropValue::Str(value))); } self } @@ -297,14 +307,33 @@ impl Input { pub fn new(props: Props) -> Self { // Initialize states let mut states: OwnStates = OwnStates::default(); + // Input type // Set state value from props - if let PropPayload::One(PropValue::Str(val)) = props.value.clone() { + if let Some(PropPayload::One(PropValue::Str(val))) = props.own.get(PROP_VALUE) { for ch in val.chars() { - states.append(ch, props.input_type, props.input_len); + states.append( + ch, + Self::get_input_type(&props), + Self::get_input_len(&props), + ); } } Input { props, states } } + + fn get_input_type(props: &Props) -> InputType { + match props.own.get(PROP_INPUT_TYPE) { + Some(PropPayload::One(PropValue::InputType(itype))) => *itype, + _ => InputType::Text, // Default + } + } + + fn get_input_len(props: &Props) -> Option { + match props.own.get(PROP_INPUT_LENGHT) { + Some(PropPayload::One(PropValue::Usize(ilen))) => Some(*ilen), + _ => None, // Default + } + } } impl Component for Input { @@ -320,18 +349,21 @@ impl Component for Input { &self.props.texts.title, self.states.focus, ); - let p: Paragraph = Paragraph::new(self.states.render_value(self.props.input_type)) - .style(match self.states.focus { - true => Style::default().fg(self.props.foreground), - false => Style::default(), - }) - .block(div); + let p: Paragraph = + Paragraph::new(self.states.render_value(Self::get_input_type(&self.props))) + .style(match self.states.focus { + true => Style::default().fg(self.props.foreground), + false => Style::default(), + }) + .block(div); render.render_widget(p, area); // Set cursor, if focus if self.states.focus { let x: u16 = area.x + calc_utf8_cursor_position( - &self.states.render_value_chars(self.props.input_type) + &self + .states + .render_value_chars(Self::get_input_type(&self.props)) [0..self.states.cursor], ) + 1; @@ -349,13 +381,16 @@ impl Component for Input { fn update(&mut self, props: Props) -> Msg { self.props = props; // Set value from props - if let PropPayload::One(PropValue::Str(val)) = self.props.value.clone() { + if let Some(PropPayload::One(PropValue::Str(val))) = self.props.own.get(PROP_VALUE) { let prev_input = self.states.input.clone(); self.states.input = Vec::new(); self.states.cursor = 0; for ch in val.chars() { - self.states - .append(ch, self.props.input_type, self.props.input_len); + self.states.append( + ch, + Self::get_input_type(&self.props), + Self::get_input_len(&self.props), + ); } if prev_input != self.states.input { Msg::OnChange(self.get_state()) @@ -375,7 +410,10 @@ impl Component for Input { fn get_props(&self) -> Props { // Make properties with value from states let mut props: Props = self.props.clone(); - props.value = PropPayload::One(PropValue::Str(self.states.get_value())); + props.own.insert( + PROP_VALUE, + PropPayload::One(PropValue::Str(self.states.get_value())), + ); props } @@ -434,8 +472,11 @@ impl Component for Input { { // Push char to input let prev_input = self.states.input.clone(); - self.states - .append(ch, self.props.input_type, self.props.input_len); + self.states.append( + ch, + Self::get_input_type(&self.props), + Self::get_input_len(&self.props), + ); // Message on change if prev_input != self.states.input { Msg::OnChange(self.get_state()) @@ -460,7 +501,7 @@ impl Component for Input { /// For this component returns Unsigned if the input type is a number, otherwise a text /// The value is always the current input. fn get_state(&self) -> Payload { - match self.props.input_type { + match Self::get_input_type(&self.props) { InputType::Number => Payload::One(Value::Usize( self.states.get_value().parse::().ok().unwrap_or(0), )), @@ -550,6 +591,10 @@ mod tests { assert_eq!(component.props.borders.borders, Borders::ALL); assert_eq!(component.props.borders.variant, BorderType::Double); assert_eq!(component.props.borders.color, Color::Red); + assert_eq!( + *component.props.own.get(PROP_VALUE).unwrap(), + PropPayload::One(PropValue::Str(String::from("home"))) + ); // Verify initial state assert_eq!(component.states.cursor, 4); assert_eq!(component.states.input.len(), 4); @@ -690,8 +735,12 @@ mod tests { assert_eq!(component.states.cursor, 1); // Move cursor right component.states.input = vec!['h', 'e', 'l', 'l', 'o']; + let props = InputPropsBuilder::from(component.get_props()) + .with_input_len(16) + .hidden() + .build(); + assert_eq!(component.update(props), Msg::None); component.states.cursor = 1; - component.props.input_len = Some(16); // Let's change length assert_eq!( component.on(Event::Key(KeyEvent::from(KeyCode::Right))), // between 'e' and 'l' Msg::None diff --git a/src/components/progress_bar.rs b/src/components/progress_bar.rs index b40fb3d..8967125 100644 --- a/src/components/progress_bar.rs +++ b/src/components/progress_bar.rs @@ -37,6 +37,8 @@ use crate::{Canvas, Component, Event, Msg, Payload}; // -- Props +const PROP_PROGRESS: &str = "progress"; + pub struct ProgressBarPropsBuilder { props: Option, } @@ -135,7 +137,9 @@ impl ProgressBarPropsBuilder { (0.0..=1.0).contains(&prog), "Progress must be in range [0.0,1.0]" ); - props.value = PropPayload::One(PropValue::F64(prog)); + props + .own + .insert(PROP_PROGRESS, PropPayload::One(PropValue::F64(prog))); } self } @@ -177,8 +181,8 @@ impl Component for ProgressBar { None => String::new(), }; // Get percentage - let percentage: f64 = match self.props.value { - PropPayload::One(PropValue::F64(ratio)) => ratio, + let percentage: f64 = match self.props.own.get(PROP_PROGRESS) { + Some(PropPayload::One(PropValue::F64(ratio))) => *ratio, _ => 0.0, }; let div: Block = @@ -281,7 +285,7 @@ mod test { assert_eq!(component.props.borders.variant, BorderType::Double); assert_eq!(component.props.borders.color, Color::Red); assert_eq!( - component.props.value, + *component.props.own.get(PROP_PROGRESS).unwrap(), PropPayload::One(PropValue::F64(0.60)) ); // Get value diff --git a/src/components/radio.rs b/src/components/radio.rs index 46382fc..dcb20d3 100644 --- a/src/components/radio.rs +++ b/src/components/radio.rs @@ -39,6 +39,8 @@ use crate::{Canvas, Component, Event, Msg, Payload, Value}; // -- Props +const PROP_OPTION: &str = "option"; + pub struct RadioPropsBuilder { props: Option, } @@ -138,7 +140,9 @@ impl RadioPropsBuilder { /// Set initial value for choice pub fn with_value(&mut self, index: usize) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = PropPayload::One(PropValue::Usize(index)); + props + .own + .insert(PROP_OPTION, PropPayload::One(PropValue::Usize(index))); } self } @@ -222,8 +226,8 @@ impl Radio { // Update choices (vec of TextSpan to String) states.set_choices(props.texts.spans.as_ref().unwrap_or(&Vec::new())); // Get value - if let PropPayload::One(PropValue::Usize(choice)) = props.value { - states.choice = choice; + if let Some(PropPayload::One(PropValue::Usize(choice))) = props.own.get(PROP_OPTION) { + states.choice = *choice; } Radio { props, states } } @@ -279,8 +283,8 @@ impl Component for Radio { self.states .set_choices(props.texts.spans.as_ref().unwrap_or(&Vec::new())); // Get value - if let PropPayload::One(PropValue::Usize(choice)) = props.value { - self.states.choice = choice; + if let Some(PropPayload::One(PropValue::Usize(choice))) = props.own.get(PROP_OPTION) { + self.states.choice = *choice; } self.props = props; // Msg none @@ -438,7 +442,10 @@ mod test { "C'est oui ou bien c'est non?" ); assert_eq!(component.props.texts.spans.as_ref().unwrap().len(), 3); - assert_eq!(component.props.value, PropPayload::One(PropValue::Usize(1))); + assert_eq!( + *component.props.own.get(PROP_OPTION).unwrap(), + PropPayload::One(PropValue::Usize(1)) + ); // Verify states assert_eq!(component.states.choice, 1); assert_eq!(component.states.choices.len(), 3); diff --git a/src/props/builder.rs b/src/props/builder.rs index 0cd49be..66e84b4 100644 --- a/src/props/builder.rs +++ b/src/props/builder.rs @@ -28,7 +28,7 @@ * SOFTWARE. */ use super::borders::{BorderType, Borders}; -use super::{InputType, PropPayload, Props, TextParts}; +use super::{PropPayload, Props, TextParts}; use tui::style::{Color, Modifier}; @@ -220,26 +220,6 @@ impl GenericPropsBuilder { self } - /// ### with_input - /// - /// Set input type for component - pub fn with_input(&mut self, input_type: InputType) -> &mut Self { - if let Some(props) = self.props.as_mut() { - props.input_type = input_type; - } - self - } - - /// ### with_input_len - /// - /// Set max input len - pub fn with_input_len(&mut self, len: usize) -> &mut Self { - if let Some(props) = self.props.as_mut() { - props.input_len = Some(len); - } - self - } - /// ### with_custom_color /// /// Set a custom color inside the color palette @@ -252,10 +232,10 @@ impl GenericPropsBuilder { /// ### with_value /// - /// Set initial value for component - pub fn with_value(&mut self, value: PropPayload) -> &mut Self { + /// Set a new key-value for component + pub fn with_value(&mut self, key: &'static str, value: PropPayload) -> &mut Self { if let Some(props) = self.props.as_mut() { - props.value = value; + props.own.insert(key, value); } self } @@ -289,9 +269,10 @@ mod test { Some(String::from("hello")), Some(vec![TextSpan::from("hey")]), )) - .with_input(InputType::Password) - .with_input_len(16) - .with_value(PropPayload::One(PropValue::Str(String::from("Hello")))) + .with_value( + "input", + PropPayload::One(PropValue::Str(String::from("Hello"))), + ) .build(); assert_eq!(props.background, Color::Blue); assert_eq!(props.borders.borders, Borders::BOTTOM); @@ -305,12 +286,8 @@ mod test { assert!(props.modifiers.intersects(Modifier::REVERSED)); assert!(props.modifiers.intersects(Modifier::CROSSED_OUT)); assert_eq!(props.foreground, Color::Green); - assert_eq!(*props.palette.get("arrows").unwrap(), Color::Red); - assert!(props.palette.get("omar").is_none()); assert_eq!(props.texts.title.as_ref().unwrap().as_str(), "hello"); - assert_eq!(props.input_type, InputType::Password); - assert_eq!(*props.input_len.as_ref().unwrap(), 16); - if let PropPayload::One(PropValue::Str(s)) = props.value { + if let Some(PropPayload::One(PropValue::Str(s))) = props.own.get("input") { assert_eq!(s.as_str(), "Hello"); } else { panic!("Expected value to be a string"); diff --git a/src/props/mod.rs b/src/props/mod.rs index 8b3aa05..1f08998 100644 --- a/src/props/mod.rs +++ b/src/props/mod.rs @@ -53,11 +53,9 @@ pub struct Props { pub background: Color, // Background color pub borders: BordersProps, // Borders pub modifiers: Modifier, - pub input_type: InputType, // Input type - pub input_len: Option, // max input len pub palette: HashMap<&'static str, Color>, // Use palette to store extra colors pub texts: TextParts, // text parts - pub value: PropPayload, // Initial value + pub own: HashMap<&'static str, PropPayload>, // Own properties (extra) } impl Default for Props { @@ -69,11 +67,9 @@ impl Default for Props { background: Color::Reset, borders: BordersProps::default(), modifiers: Modifier::empty(), - input_type: InputType::Text, - input_len: None, palette: HashMap::new(), texts: TextParts::default(), - value: PropPayload::None, + own: HashMap::new(), } } } @@ -116,6 +112,8 @@ pub enum PropValue { F64(f64), F32(f32), Str(String), + Color(Color), + InputType(InputType), } // -- Input Type @@ -148,9 +146,7 @@ mod tests { assert_eq!(props.modifiers, Modifier::empty()); assert_eq!(props.palette.len(), 0); assert!(props.texts.title.is_none()); - assert_eq!(props.input_type, InputType::Text); - assert!(props.input_len.is_none()); - assert_eq!(props.value, PropPayload::None); + assert_eq!(props.own.len(), 0); assert!(props.texts.spans.is_none()); } @@ -181,6 +177,8 @@ mod tests { map.insert(String::from("c"), PropValue::I32(16)); map.insert(String::from("d"), PropValue::I64(-32)); map.insert(String::from("e"), PropValue::I128(64)); + map.insert(String::from("f"), PropValue::Color(Color::Red)); + map.insert(String::from("g"), PropValue::InputType(InputType::Number)); PropPayload::Map(map); let mut link: LinkedList = LinkedList::new(); link.push_back(PropPayload::One(PropValue::Usize(1))); From 279fbc0bc9a94df8817758aedcf26de43fea232f Mon Sep 17 00:00:00 2001 From: veeso Date: Mon, 7 Jun 2021 11:55:01 +0200 Subject: [PATCH 3/5] removed entry in changelog --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0a708..b91adbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,6 @@ Released on FIXME: - Added `Color` and `InputType` to `PropValue` - Removed `value` from `Props` - Added `own`: key-value storage (`HashMap<&'static str, PropPayload>`) to store any value into properties. -- Components: - - Added `with_validation` to `Input` component to validate with regex ## 0.3.2 From 2d3d1206cca854b9a5a10f9542be7a0207086d25 Mon Sep 17 00:00:00 2001 From: veeso Date: Mon, 7 Jun 2021 11:57:18 +0200 Subject: [PATCH 4/5] release date --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b91adbe..62cef85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ ## 0.4.0 -Released on FIXME: +Released on 07/06/2021 - Another **Prop API Update** - Removed `input_len` and `input_type` from properties. Use `own` instead with new `PropValue` diff --git a/README.md b/README.md index 60160c5..d168ab5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Build](https://github.com/veeso/tui-realm/workflows/Linux/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Build](https://github.com/veeso/tui-realm/workflows/MacOS/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Build](https://github.com/veeso/tui-realm/workflows/Windows/badge.svg)](https://github.com/veeso/tui-realm/actions) [![Coverage Status](https://coveralls.io/repos/github/veeso/tui-realm/badge.svg?branch=main)](https://coveralls.io/github/veeso/tui-realm?branch=main) Developed by Christian Visintin -Current version: 0.4.0 FIXME: (04/06/2021) +Current version: 0.4.0 (07/06/2021) --- From e6b1119a21b4d771e6226dad15ae7b9a0de3f1bc Mon Sep 17 00:00:00 2001 From: veeso Date: Mon, 7 Jun 2021 12:04:05 +0200 Subject: [PATCH 5/5] textwrap 0.14.0 --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62cef85..18a22a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ Released on 07/06/2021 - Added `Color` and `InputType` to `PropValue` - Removed `value` from `Props` - Added `own`: key-value storage (`HashMap<&'static str, PropPayload>`) to store any value into properties. +- Dependencies: + - `textwrap` 0.14.0 ## 0.3.2 diff --git a/Cargo.toml b/Cargo.toml index 496f42b..46d4299 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ repository = "https://github.com/veeso/tui-realm" [dependencies] tui = { version = "0.15.0", default-features = false, features = ["crossterm"] } crossterm = { version = "0.19" } -textwrap = { version = "0.13.4", optional = true } +textwrap = { version = "0.14.0", optional = true } unicode-width = { version = "0.1.8", optional = true } [dev-dependencies]