Defined Component and State

This commit is contained in:
veeso
2021-03-03 12:08:47 +01:00
parent 3ea345ee8f
commit 3ccbb325b3
2 changed files with 114 additions and 4 deletions

View File

@@ -24,4 +24,64 @@
*/
// Modules
pub(crate) mod props;
pub mod props;
// locals
use props::{Props, PropsBuilder};
// ext
use crossterm::event::Event as InputEvent;
use tui::widgets::Widget;
// -- States
/// ## States
///
/// States is a trait which defines the behaviours for the states model for the different component.
/// A state contains internal values for each component.
pub(crate) trait States {
/// ### update
///
/// Create a new state from current one and new
fn update(&self, new: dyn States) -> dyn States;
}
// -- Component
/// ## Component
///
/// Component is a trait which defines the behaviours for a Layout component.
/// All layout components must implement a method to render and one to update
pub trait Component {
/// ### render
///
/// Based on the current properties and states, return a Widget instance for the Component
/// Returns None if the component is hidden
fn render(&self) -> Option<Box<dyn Widget>>;
/// ### update
///
/// Update component properties
/// Properties should first be retrieved through `get_props` which creates a builder from
/// existing properties and then edited before calling update
fn update(&mut self, props: Option<Props>);
/// ### get_props
///
/// Returns a props builder starting from component properties.
/// This returns a prop builder in order to make easier to create
/// new properties for the element.
fn get_props(&self) -> PropsBuilder;
/// ### on
///
/// Handle input event and update internal states
fn on(&mut self, ev: InputEvent);
// -- events
/// ### should_umount
///
/// The component must provide to the supervisor whether it should be umounted (destroyed)
/// This makes sense to be called after an `on` or after an `update`, where the states changes.
fn should_umount(&self) -> bool;
}

View File

@@ -31,11 +31,12 @@ use tui::style::Color;
// Callback types
pub type OnSubmitCb = fn(&mut dyn Activity, Option<String>); // Activity, Value
// --- Props
// -- Props
/// ## Props
///
/// Props holds all the possible properties for a layout component
#[derive(Clone)]
pub struct Props {
// Values
pub visible: bool, // Is the element visible ON CREATE?
@@ -66,7 +67,7 @@ impl Default for Props {
}
}
// --- Props builder
// -- Props builder
/// ## PropsBuilder
///
@@ -76,6 +77,13 @@ pub struct PropsBuilder {
}
impl PropsBuilder {
/// ### from_props
///
/// Create a props builder from existing properties
pub fn from_props(props: Props) -> Self {
PropsBuilder { props: Some(props) }
}
/// ### build
///
/// Build Props from builder
@@ -172,11 +180,12 @@ impl Default for PropsBuilder {
}
}
// --- Text parts
// -- Text parts
/// ## TextParts
///
/// TextParts holds optional component for the text displayed by a component
#[derive(Clone)]
pub struct TextParts {
pub title: Option<String>,
pub body: Option<Vec<String>>,
@@ -248,6 +257,47 @@ mod tests {
assert_eq!(props.visible, false);
}
#[test]
#[should_panic]
fn test_ui_layout_props_build_twice() {
let mut builder: PropsBuilder = PropsBuilder::default();
let _ = builder.build();
builder
.hidden()
.with_background(Color::Blue)
.with_foreground(Color::Green)
.bold()
.italic()
.underlined()
.on_submit(on_submit_cb)
.with_texts(TextParts::new(
Some(String::from("hello")),
Some(vec![String::from("hey")]),
));
// Rebuild
let _ = builder.build();
}
#[test]
fn test_ui_layout_props_builder_from_props() {
let props: Props = PropsBuilder::default()
.hidden()
.with_background(Color::Blue)
.with_foreground(Color::Green)
.bold()
.italic()
.underlined()
.on_submit(on_submit_cb)
.with_texts(TextParts::new(
Some(String::from("hello")),
Some(vec![String::from("hey")]),
))
.build();
// Ok, now make a builder from properties
let builder: PropsBuilder = PropsBuilder::from_props(props);
assert!(builder.props.is_some());
}
#[test]
fn test_ui_layout_props_text_parts_with_values() {
let parts: TextParts = TextParts::new(