Skip to content

Commit

Permalink
Merge e6b1119 into 0faa41d
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Jun 7, 2021
2 parents 0faa41d + e6b1119 commit e37c5a6
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 92 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

- [Changelog](#changelog)
- [0.4.0](#040)
- [0.3.2](#032)
- [0.3.1](#031)
- [0.3.0](#030)
Expand All @@ -10,6 +11,18 @@

---

## 0.4.0

Released on 07/06/2021

- 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.
- Dependencies:
- `textwrap` 0.14.0

## 0.3.2

Released on 04/06/2021
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tuirealm"
version = "0.3.2"
version = "0.4.0"
authors = ["Christian Visintin"]
edition = "2018"
categories = ["command-line-utilities"]
Expand All @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<img src="docs/images/tui-realm.svg" width="256" height="256" />
</p>

[![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 (07/06/2021)

---

Expand Down Expand Up @@ -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
Expand Down
87 changes: 84 additions & 3 deletions docs/new-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<String>,
pub spans: Option<Vec<TextSpan>>,
pub table: Option<Table>, // 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<PropValue>),
Map(HashMap<String, PropValue>),
Linked(LinkedList<PropPayload>),
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:
Expand All @@ -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 }
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 7 additions & 3 deletions examples/components/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ impl OwnStates {

// -- Props

const PROP_VALUE: &str = "value";

pub struct CounterPropsBuilder {
props: Option<Props>,
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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 }
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions examples/gallery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)),
);
Expand Down Expand Up @@ -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,
},
Expand Down
15 changes: 10 additions & 5 deletions src/components/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ use crate::tui::{
};
use crate::{Canvas, Component, Event, Msg, Payload, Value};

const PROP_CHOICES: &str = "choices";

// -- Props

pub struct CheckboxPropsBuilder {
Expand Down Expand Up @@ -135,10 +137,13 @@ impl CheckboxPropsBuilder {

/// ### with_value
///
/// Set initial value for choice
/// Set selected choices
pub fn with_value(&mut self, choices: Vec<usize>) -> &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
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e37c5a6

Please sign in to comment.