diff --git a/src/ui/layout/mod.rs b/src/ui/layout/mod.rs index dbe8f00..5bacf2c 100644 --- a/src/ui/layout/mod.rs +++ b/src/ui/layout/mod.rs @@ -26,6 +26,7 @@ // Modules pub mod components; pub mod props; +pub mod view; // locals use props::{Props, PropsBuilder, PropValue}; diff --git a/src/ui/layout/view.rs b/src/ui/layout/view.rs new file mode 100644 index 0000000..d1b6eee --- /dev/null +++ b/src/ui/layout/view.rs @@ -0,0 +1,131 @@ +//! ## View +//! +//! `View` is the module which handles layout components + +/* +* +* 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 . +* +*/ + +// imports +use super::{Component, Msg, Props}; +// ext +use std::collections::HashMap; + +/// ## View +/// +/// View is the wrapper and manager for all the components. +/// A View is a container for all the components in a certain layout. +/// It is possible to have ligatures between elements, which causes for example a component a to lose focus in favour of b if a certain key is pressed. +/// Each View can have only one focused component. +pub struct View { + components: HashMap>, // all the components in the view + focus: Option, // Current active component + focus_stack: Vec, // Focus stack; used to give focus in case the current element loses focus +} + +/// ## Ligature +/// +/// A ligature describes how a certain event must be handled by the view when a certain element 'a has focus. +/// The purpose of the ligature is to make a certain event to trigger another component. +/// This means a ligature is an event that if triggered on 'a causes 'b to something else. +/// E.g. 'a -> onKey(KeyCode::Char('e')) -> focus -> 'b +struct Ligature { + origin: String, // Element which causes the Ligature to trigger + event: Msg, // The event the ligature summons + action: Action, // The action which must be performed in case the ligature triggers + target: String, // The element the action is performed for +} + +/// ## Action +/// +/// Action describes an action to perform in case a ligature triggers +pub enum Action { + Active, // Give focus to component + Blur, // Remove focus to component + Show, // Set component to visible + Hide, // Hide component + Umount, // Umount element +} + +// -- view + +impl View { + /// ### new + /// + /// Instantiates a new `View` + pub fn new() -> Self { + View { + components: HashMap::new(), + focus: None, + focus_stack: Vec::new(), + } + } + + // -- private + + /// ### blur + /// + /// Blur selected element and push it into the stack; + /// Last element in stack becomes active + fn blur(&mut self) { + if let Some(component) = self.focus.take() { + // Set last element as active + let mut new: Option = None; + if let Some(last) = self.focus_stack.last() { + // Set focus to last element + new = Some(last.clone()); + self.focus = Some(last.clone()); + } + // Pop element from stack + if let Some(new) = new { + self.pop_from_stack(new.as_str()); + } + // Finally previous active component to stack + self.push_to_stack(&component); + } + } + + /// ### active + /// + /// Active provided element + fn active(&mut self, component: &str) { + // If there is an element active; call blur + if self.focus.is_some() { + self.blur(); + } + // Set focus + self.focus = Some(component.to_string()); + } + + /// ### push_to_stack + /// + /// Push component to stack; first remove it from the stack if any + fn push_to_stack(&mut self, name: &str) { + self.pop_from_stack(name); + self.focus_stack.push(name.to_string()); + } + + /// ### pop_from_stack + /// + /// Pop element from focus stack + fn pop_from_stack(&mut self, name: &str) { + self.focus_stack.retain(|c| c.as_str() != name); + } +}