From 3ccbb325b3ae9c50b2b969ed162250aa133ebc25 Mon Sep 17 00:00:00 2001 From: veeso Date: Wed, 3 Mar 2021 12:08:47 +0100 Subject: [PATCH] Defined Component and State --- src/ui/layout/mod.rs | 62 +++++++++++++++++++++++++++++++++++++++++- src/ui/layout/props.rs | 56 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/ui/layout/mod.rs b/src/ui/layout/mod.rs index fabcf23..422e1bd 100644 --- a/src/ui/layout/mod.rs +++ b/src/ui/layout/mod.rs @@ -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>; + + /// ### 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); + + /// ### 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; +} diff --git a/src/ui/layout/props.rs b/src/ui/layout/props.rs index ed37f23..c4dd5fc 100644 --- a/src/ui/layout/props.rs +++ b/src/ui/layout/props.rs @@ -31,11 +31,12 @@ use tui::style::Color; // Callback types pub type OnSubmitCb = fn(&mut dyn Activity, Option); // 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, pub body: Option>, @@ -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(