From 36cc6f445ab9f0dd0080bdc7685a26fb705d2c99 Mon Sep 17 00:00:00 2001 From: veeso Date: Sun, 14 Mar 2021 19:21:41 +0100 Subject: [PATCH] Title component --- src/ui/layout/components/mod.rs | 1 + src/ui/layout/components/title.rs | 180 ++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/ui/layout/components/title.rs diff --git a/src/ui/layout/components/mod.rs b/src/ui/layout/components/mod.rs index 1fc30fb..3b52855 100644 --- a/src/ui/layout/components/mod.rs +++ b/src/ui/layout/components/mod.rs @@ -36,3 +36,4 @@ pub mod progress_bar; pub mod radio_group; pub mod table; pub mod text; +pub mod title; diff --git a/src/ui/layout/components/title.rs b/src/ui/layout/components/title.rs new file mode 100644 index 0000000..5e39ff3 --- /dev/null +++ b/src/ui/layout/components/title.rs @@ -0,0 +1,180 @@ +//! ## Title +//! +//! `Title` component renders a simple readonly no event associated title + +/* +* +* Copyright (C) 2020-2021 Christian Visintin - christian.visintin1997@gmail.com +* +* This file is part of "TermSCP" +* +* TermSCP is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* TermSCP is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with TermSCP. If not, see . +* +*/ + +// locals +use super::{Canvas, Component, InputEvent, Msg, Payload, Props, PropsBuilder}; +// ext +use tui::{ + layout::Rect, + style::Style, + widgets::Paragraph, +}; + +// -- state + +struct OwnStates { + focus: bool, +} + +impl Default for OwnStates { + fn default() -> Self { + OwnStates { focus: false } + } +} + +// -- component + +pub struct Title { + props: Props, + states: OwnStates, +} + +impl Title { + /// ### new + /// + /// Instantiate a new Title component + pub fn new(props: Props) -> Self { + Title { + props, + states: OwnStates::default(), + } + } +} + +impl Component for Title { + /// ### render + /// + /// Based on the current properties and states, renders a widget using the provided render engine in the provided Area + /// If focused, cursor is also set (if supported by widget) + #[cfg(not(tarpaulin_include))] + fn render(&self, render: &mut Canvas, area: Rect) { + // Make a Span + if self.props.visible { + let title: String = match self.props.texts.title.as_ref() { + None => String::new(), + Some(t) => t.clone(), + }; + render.render_widget( + Paragraph::new(title).style( + Style::default() + .fg(self.props.foreground) + .bg(self.props.background) + .add_modifier(self.props.get_modifiers()), + ), + area, + ); + } + } + + /// ### 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. + /// Returns a Msg to the view + fn update(&mut self, props: Props) -> Msg { + self.props = props; + // Return None + Msg::None + } + + /// ### 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 { + PropsBuilder::from(self.props.clone()) + } + + /// ### on + /// + /// Handle input event and update internal states. + /// Returns a Msg to the view. + /// Returns always None, since cannot have any focus + fn on(&mut self, ev: InputEvent) -> Msg { + // Return key + if let InputEvent::Key(key) = ev { + Msg::OnKey(key) + } else { + Msg::None + } + } + + /// ### get_value + /// + /// Get current value from component + /// For this component returns always None + fn get_value(&self) -> Payload { + Payload::None + } + + // -- events + + /// ### blur + /// + /// Blur component + fn blur(&mut self) { + self.states.focus = false; + } + + /// ### active + /// + /// Active component + fn active(&mut self) { + self.states.focus = true; + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::ui::layout::props::TextParts; + + use crossterm::event::{KeyCode, KeyEvent}; + + #[test] + fn test_ui_layout_components_title() { + let mut component: Title = Title::new( + PropsBuilder::default() + .with_texts(TextParts::new(Some(String::from("Title")), None)) + .build(), + ); + // Focus + assert_eq!(component.states.focus, false); + component.active(); + assert_eq!(component.states.focus, true); + component.blur(); + assert_eq!(component.states.focus, false); + // Get value + assert_eq!(component.get_value(), Payload::None); + // Event + assert_eq!( + component.on(InputEvent::Key(KeyEvent::from(KeyCode::Delete))), + Msg::OnKey(KeyEvent::from(KeyCode::Delete)) + ); + } +}