tabled/settings/themes/
layout.rs

1//! Module contains [`Layout`] setting.
2
3use papergrid::records::{ExactRecords, PeekableRecords, Records};
4
5use crate::{
6    grid::{
7        config::{AlignmentHorizontal, AlignmentVertical},
8        records::{RecordsMut, Resizable},
9    },
10    settings::{Alignment, Rotate, TableOption},
11};
12
13/// Layout can be used to move header to a specific corner.
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct Layout {
16    orientation: HeadPosition,
17    footer: bool,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21enum HeadPosition {
22    Top,
23    Bottom,
24    Left,
25    Right,
26}
27
28impl Layout {
29    /// Construct a new layout setting.
30    pub fn new(stick: Alignment, footer: bool) -> Self {
31        let orientation = convert_orientation(stick);
32
33        Self {
34            footer,
35            orientation,
36        }
37    }
38}
39
40impl<R, C, D> TableOption<R, C, D> for Layout
41where
42    R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
43{
44    fn change(self, records: &mut R, _: &mut C, _: &mut D) {
45        move_head_if(records, self.orientation);
46
47        if self.footer {
48            copy_head(records, self.orientation);
49        }
50    }
51}
52
53const fn convert_orientation(position: Alignment) -> HeadPosition {
54    match (position.as_horizontal(), position.as_vertical()) {
55        (None, Some(AlignmentVertical::Top)) => HeadPosition::Top,
56        (None, Some(AlignmentVertical::Bottom)) => HeadPosition::Bottom,
57        (Some(AlignmentHorizontal::Left), None) => HeadPosition::Left,
58        (Some(AlignmentHorizontal::Right), None) => HeadPosition::Right,
59        (None, Some(AlignmentVertical::Center)) => HeadPosition::Top,
60        (Some(AlignmentHorizontal::Center), None) => HeadPosition::Top,
61        (None, None) | (Some(_), Some(_)) => HeadPosition::Top,
62    }
63}
64
65fn copy_head<R>(records: &mut R, orientation: HeadPosition)
66where
67    R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
68{
69    let head = collect_head_by(records, orientation);
70    match orientation {
71        HeadPosition::Top => cp_row(records, head, records.count_rows()),
72        HeadPosition::Bottom => cp_row(records, head, 0),
73        HeadPosition::Left => cp_column(records, head, records.count_columns()),
74        HeadPosition::Right => cp_column(records, head, 0),
75    }
76}
77
78fn collect_head_by<R>(records: &mut R, orientation: HeadPosition) -> Vec<String>
79where
80    R: Records + PeekableRecords + ExactRecords,
81{
82    match orientation {
83        HeadPosition::Top => collect_head(records, 0),
84        HeadPosition::Bottom => collect_head(records, records.count_rows() - 1),
85        HeadPosition::Left => collect_head_vertical(records, 0),
86        HeadPosition::Right => collect_head_vertical(records, records.count_columns() - 1),
87    }
88}
89
90fn cp_row<R>(records: &mut R, row: Vec<String>, pos: usize)
91where
92    R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
93{
94    records.insert_row(pos);
95
96    for (col, text) in row.into_iter().enumerate() {
97        records.set((pos, col).into(), text);
98    }
99}
100
101fn cp_column<R>(records: &mut R, column: Vec<String>, pos: usize)
102where
103    R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
104{
105    records.insert_column(pos);
106
107    for (row, text) in column.into_iter().enumerate() {
108        records.set((row, pos).into(), text);
109    }
110}
111
112fn move_head_if<R>(records: &mut R, orientation: HeadPosition)
113where
114    R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
115{
116    match orientation {
117        HeadPosition::Top => {}
118        HeadPosition::Bottom => {
119            let head = collect_head(records, 0);
120            push_row(records, head);
121            records.remove_row(0);
122        }
123        HeadPosition::Left => {
124            Rotate::Left.change(records, &mut (), &mut ());
125            Rotate::Bottom.change(records, &mut (), &mut ());
126        }
127        HeadPosition::Right => {
128            Rotate::Right.change(records, &mut (), &mut ());
129        }
130    }
131}
132
133fn collect_head<R>(records: &mut R, row: usize) -> Vec<String>
134where
135    R: Records + PeekableRecords,
136{
137    (0..records.count_columns())
138        .map(|column| records.get_text((row, column).into()))
139        .map(ToString::to_string)
140        .collect()
141}
142
143fn collect_head_vertical<R>(records: &mut R, column: usize) -> Vec<String>
144where
145    R: Records + PeekableRecords + ExactRecords,
146{
147    (0..records.count_rows())
148        .map(|row| records.get_text((row, column).into()))
149        .map(ToString::to_string)
150        .collect()
151}
152
153fn push_row<R>(records: &mut R, row: Vec<String>)
154where
155    R: Records + ExactRecords + Resizable + RecordsMut<String>,
156{
157    records.push_row();
158
159    let last_row = records.count_rows() - 1;
160
161    for (col, text) in row.into_iter().enumerate() {
162        records.set((last_row, col).into(), text);
163    }
164}