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]