termscp/src/ui/layout/components/title.rs

179 lines
5.0 KiB
Rust

//! ## Title
//!
//! `Title` component renders a simple readonly no event associated title
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// 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))
);
}
}