Skip to main content

ez_tui/forms/
core.rs

1use crate::{CustomLayout, EzCptIds, FormField, FormNode, LayoutItem};
2use derive_more::Constructor;
3use ratatui::layout::{Constraint, Direction, Layout};
4use std::collections::VecDeque;
5
6/// Describes a whole form, which can contain multiple fields and sub-forms.
7/// As a form has a fixed layout direction, sub forms can be used to change the direction.
8#[derive(Debug, Constructor)]
9pub struct Form<FID>
10where
11    FID: EzCptIds,
12{
13    pub(crate) name: String,
14    pub(crate) direction: Direction,
15    pub(crate) nodes: Vec<FormNode<FID>>,
16}
17impl<FID> Form<FID>
18where
19    FID: EzCptIds,
20{
21    /// Get this form's name.
22    #[must_use]
23    pub fn name(&self) -> &str {
24        &self.name
25    }
26
27    /// Compute and return the layout of this form
28    #[allow(clippy::missing_panics_doc)]
29    #[must_use]
30    pub fn custom_layout(&self) -> CustomLayout<FID> {
31        let ids = self
32            .nodes
33            .iter()
34            .map(|f| f.as_layout_item(self.direction, self.nodes.len()))
35            .collect::<VecDeque<LayoutItem<FID>>>();
36        let layout = Layout::default().direction(self.direction).constraints(
37            self.nodes
38                .iter()
39                .map(|f| f.as_constraint(self.direction, self.nodes.len()))
40                .collect::<Vec<Constraint>>(),
41        );
42        CustomLayout::named(            self.name.clone(),            layout,            ids,        )
43            .expect("Failed to create custom layout; probably due to mismatched constraints and components resulting from the lib logic (it's a bug for sure)")
44    }
45
46    /// Flatten all fields in this form and its sub-forms into a single [`Vec`].
47    #[must_use]
48    pub fn flatten_fields(&self) -> Vec<&FormField<FID>> {
49        self.nodes
50            .iter()
51            .flat_map(|node| match node {
52                FormNode::Field(field) => vec![field],
53                FormNode::Form(form) => form.flatten_fields(),
54            })
55            .collect()
56    }
57    #[allow(unused_variables)]
58    #[allow(clippy::unused_self)] //TODO : haven't put much thought into this yet
59    pub(crate) fn as_constraint(&self, direction: Direction, _count: usize) -> Constraint {
60        Constraint::Min(1)
61    }
62
63    pub(crate) fn get(&self, id: &FID) -> Option<&FormField<FID>> {
64        for node in &self.nodes {
65            match node {
66                FormNode::Field(field) => {
67                    if field.id() == id {
68                        return Some(field);
69                    }
70                }
71                FormNode::Form(form) => {
72                    if let Some(field) = form.get(id) {
73                        return Some(field);
74                    }
75                }
76            }
77        }
78        None
79    }
80}