1 | //! The module contains a [`CompactGridDimension`] for [`CompactGrid`] height/width estimation. |
2 | //! |
3 | //! [`CompactGrid`]: crate::grid::compact::CompactGrid |
4 | |
5 | use core::cmp::max; |
6 | |
7 | use crate::{ |
8 | dimension::{Dimension, Estimate}, |
9 | records::{IntoRecords, Records}, |
10 | util::string::{count_lines, string_width_multiline}, |
11 | }; |
12 | |
13 | use crate::config::compact::CompactConfig; |
14 | |
15 | /// A [`Dimension`] implementation which calculates exact column/row width/height. |
16 | /// |
17 | /// [`Grid`]: crate::grid::iterable::Grid |
18 | #[derive (Debug, Default, Clone, PartialEq, Eq)] |
19 | pub struct CompactGridDimension { |
20 | height: usize, |
21 | width: Vec<usize>, |
22 | } |
23 | |
24 | impl CompactGridDimension { |
25 | /// Calculates height of rows. |
26 | pub fn height<R>(records: R, cfg: &CompactConfig) -> Vec<usize> |
27 | where |
28 | R: Records, |
29 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
30 | { |
31 | build_height(records, cfg) |
32 | } |
33 | |
34 | /// Calculates width of columns. |
35 | pub fn width<R>(records: R, cfg: &CompactConfig) -> Vec<usize> |
36 | where |
37 | R: Records, |
38 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
39 | { |
40 | build_width(records, cfg) |
41 | } |
42 | |
43 | /// Calculates dimensions of columns. |
44 | pub fn dimension<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>) |
45 | where |
46 | R: Records, |
47 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
48 | { |
49 | build_dims(records, cfg) |
50 | } |
51 | } |
52 | |
53 | impl Dimension for CompactGridDimension { |
54 | fn get_width(&self, column: usize) -> usize { |
55 | self.width[column] |
56 | } |
57 | |
58 | fn get_height(&self, _: usize) -> usize { |
59 | self.height |
60 | } |
61 | } |
62 | |
63 | impl<R> Estimate<R, CompactConfig> for CompactGridDimension |
64 | where |
65 | R: Records, |
66 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
67 | { |
68 | fn estimate(&mut self, records: R, cfg: &CompactConfig) { |
69 | self.width = build_width(records, cfg); |
70 | let pad: &Sides = cfg.get_padding(); |
71 | self.height = 1 + pad.top.size + pad.bottom.size; |
72 | } |
73 | } |
74 | |
75 | fn build_dims<R>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>) |
76 | where |
77 | R: Records, |
78 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
79 | { |
80 | let mut heights: Vec = vec![]; |
81 | let mut widths: Vec = vec![0; records.count_columns()]; |
82 | |
83 | for columns in records.iter_rows() { |
84 | let mut row_height: usize = 0; |
85 | for (col, cell) in columns.into_iter().enumerate() { |
86 | let height: usize = get_cell_height(cell.as_ref(), cfg); |
87 | let width: usize = get_cell_width(text:cell.as_ref(), cfg); |
88 | row_height = max(v1:row_height, v2:height); |
89 | widths[col] = max(v1:widths[col], v2:width) |
90 | } |
91 | |
92 | heights.push(row_height); |
93 | } |
94 | |
95 | (widths, heights) |
96 | } |
97 | |
98 | fn build_height<R>(records: R, cfg: &CompactConfig) -> Vec<usize> |
99 | where |
100 | R: Records, |
101 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
102 | { |
103 | let mut heights: Vec = vec![]; |
104 | |
105 | for columns in records.iter_rows() { |
106 | let mut row_height: usize = 0; |
107 | for cell in columns.into_iter() { |
108 | let height: usize = get_cell_height(cell.as_ref(), cfg); |
109 | row_height = max(v1:row_height, v2:height); |
110 | } |
111 | |
112 | heights.push(row_height); |
113 | } |
114 | |
115 | heights |
116 | } |
117 | |
118 | fn build_width<R>(records: R, cfg: &CompactConfig) -> Vec<usize> |
119 | where |
120 | R: Records, |
121 | <R::Iter as IntoRecords>::Cell: AsRef<str>, |
122 | { |
123 | let mut widths: Vec = vec![0; records.count_columns()]; |
124 | for columns in records.iter_rows() { |
125 | for (col, cell) in columns.into_iter().enumerate() { |
126 | let width: usize = get_cell_width(text:cell.as_ref(), cfg); |
127 | widths[col] = max(v1:widths[col], v2:width); |
128 | } |
129 | } |
130 | |
131 | widths |
132 | } |
133 | |
134 | fn get_cell_height(cell: &str, cfg: &CompactConfig) -> usize { |
135 | let count_lines: usize = max(v1:1, v2:count_lines(cell)); |
136 | let pad: &Sides = cfg.get_padding(); |
137 | |
138 | count_lines + pad.top.size + pad.bottom.size |
139 | } |
140 | |
141 | fn get_cell_width(text: &str, cfg: &CompactConfig) -> usize { |
142 | let width: usize = string_width_multiline(text); |
143 | let pad: &Sides = cfg.get_padding(); |
144 | |
145 | width + pad.left.size + pad.right.size |
146 | } |
147 | |