1 | /* Support for tabular/grid-based content. |
2 | Copyright (C) 2023-2024 Free Software Foundation, Inc. |
3 | Contributed by David Malcolm <dmalcolm@redhat.com>. |
4 | |
5 | This file is part of GCC. |
6 | |
7 | GCC is free software; you can redistribute it and/or modify it |
8 | under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 3, or (at your option) |
10 | any later version. |
11 | |
12 | GCC is distributed in the hope that it will be useful, but |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ |
20 | |
21 | #ifndef GCC_TEXT_ART_TABLE_H |
22 | #define GCC_TEXT_ART_TABLE_H |
23 | |
24 | #include "text-art/canvas.h" |
25 | #include "text-art/theme.h" |
26 | |
27 | namespace text_art { |
28 | |
29 | class table; |
30 | class table_geometry; |
31 | |
32 | /* A class representing the content of a particular table cell, |
33 | or of a span of table cells. */ |
34 | |
35 | class table_cell_content |
36 | { |
37 | public: |
38 | table_cell_content () : m_str (), m_size (0, 0) {} |
39 | table_cell_content (styled_string &&s); |
40 | |
41 | bool operator== (const table_cell_content &other) const |
42 | { |
43 | return m_str == other.m_str; |
44 | } |
45 | |
46 | canvas::size_t get_canvas_size () const { return m_size; } |
47 | |
48 | void paint_to_canvas (canvas &canvas, |
49 | canvas::coord_t top_left) const; |
50 | |
51 | private: |
52 | styled_string m_str; |
53 | canvas::size_t m_size; |
54 | }; |
55 | |
56 | /* A list of required sizes of table rows or columns |
57 | in canvas units (row heights or column widths). */ |
58 | |
59 | struct table_dimension_sizes |
60 | { |
61 | table_dimension_sizes (unsigned num); |
62 | |
63 | void require (unsigned idx, int amount) |
64 | { |
65 | m_requirements[idx] = std::max (a: m_requirements[idx], b: amount); |
66 | } |
67 | |
68 | std::vector<int> m_requirements; |
69 | }; |
70 | |
71 | /* A 2D grid of cells. Instances of table_cell_content can be assigned |
72 | to individual table cells, and to rectangular spans of cells. Such |
73 | assignments do not have to fully cover the 2D grid, but they must not |
74 | overlap. */ |
75 | |
76 | class table |
77 | { |
78 | public: |
79 | typedef size<class table> size_t; |
80 | typedef coord<class table> coord_t; |
81 | typedef range<class table> range_t; |
82 | typedef rect<class table> rect_t; |
83 | |
84 | /* A record of how a table_cell_content was placed at a table::rect_t |
85 | with a certain alignment. */ |
86 | class cell_placement |
87 | { |
88 | public: |
89 | cell_placement (rect_t rect, |
90 | table_cell_content &&content, |
91 | x_align x_align, |
92 | y_align y_align) |
93 | : m_rect (rect), |
94 | m_content (std::move (content)), |
95 | m_x_align (x_align), |
96 | m_y_align (y_align) |
97 | { |
98 | } |
99 | |
100 | bool one_by_one_p () const |
101 | { |
102 | return m_rect.m_size.w == 1 && m_rect.m_size.h == 1; |
103 | } |
104 | |
105 | canvas::size_t get_min_canvas_size () const |
106 | { |
107 | // Doesn't include border |
108 | return m_content.get_canvas_size (); |
109 | } |
110 | |
111 | void paint_cell_contents_to_canvas(canvas &canvas, |
112 | canvas::coord_t offset, |
113 | const table_geometry &tg) const; |
114 | |
115 | const table_cell_content &get_content () const { return m_content; } |
116 | |
117 | private: |
118 | friend class table; |
119 | friend class table_cell_sizes; |
120 | rect_t m_rect; |
121 | table_cell_content m_content; |
122 | x_align m_x_align; |
123 | y_align m_y_align; |
124 | }; |
125 | |
126 | table (size_t size); |
127 | ~table () = default; |
128 | table (table &&) = default; |
129 | table (const table &) = delete; |
130 | table &operator= (const table &) = delete; |
131 | |
132 | const size_t &get_size () const { return m_size; } |
133 | |
134 | int add_rows (unsigned num) |
135 | { |
136 | int topmost_new_row = m_size.h; |
137 | m_size.h += num; |
138 | for (unsigned i = 0; i < num; i++) |
139 | m_occupancy.add_row (element: -1); |
140 | return topmost_new_row; |
141 | } |
142 | |
143 | int add_row () |
144 | { |
145 | return add_rows (num: 1); |
146 | } |
147 | |
148 | void set_cell (coord_t coord, |
149 | table_cell_content &&content, |
150 | enum x_align x_align = x_align::CENTER, |
151 | enum y_align y_align = y_align::CENTER); |
152 | |
153 | void set_cell_span (rect_t span, |
154 | table_cell_content &&content, |
155 | enum x_align x_align = x_align::CENTER, |
156 | enum y_align y_align = y_align::CENTER); |
157 | |
158 | void maybe_set_cell_span (rect_t span, |
159 | table_cell_content &&content, |
160 | enum x_align x_align = x_align::CENTER, |
161 | enum y_align y_align = y_align::CENTER); |
162 | |
163 | canvas to_canvas (const theme &theme, const style_manager &sm) const; |
164 | |
165 | void paint_to_canvas(canvas &canvas, |
166 | canvas::coord_t offset, |
167 | const table_geometry &tg, |
168 | const theme &theme) const; |
169 | |
170 | void debug () const; |
171 | |
172 | void add_other_table (table &&other, table::coord_t offset); |
173 | |
174 | /* Self-test support. */ |
175 | const cell_placement *get_placement_at (coord_t coord) const; |
176 | |
177 | private: |
178 | int get_occupancy_safe (coord_t coord) const; |
179 | directions get_connections (int table_x, int table_y) const; |
180 | void paint_cell_borders_to_canvas(canvas &canvas, |
181 | canvas::coord_t offset, |
182 | const table_geometry &tg, |
183 | const theme &theme) const; |
184 | void paint_cell_contents_to_canvas(canvas &canvas, |
185 | canvas::coord_t offset, |
186 | const table_geometry &tg) const; |
187 | |
188 | friend class table_cell_sizes; |
189 | |
190 | size_t m_size; |
191 | std::vector<cell_placement> m_placements; |
192 | array2<int, size_t, coord_t> m_occupancy; /* indices into the m_placements vec. */ |
193 | }; |
194 | |
195 | /* A workspace for computing the row heights and column widths |
196 | of a table (in canvas units). |
197 | The col_widths and row_heights could be shared between multiple |
198 | instances, for aligning multiple tables vertically or horizontally. */ |
199 | |
200 | class table_cell_sizes |
201 | { |
202 | public: |
203 | table_cell_sizes (table_dimension_sizes &col_widths, |
204 | table_dimension_sizes &row_heights) |
205 | : m_col_widths (col_widths), |
206 | m_row_heights (row_heights) |
207 | { |
208 | } |
209 | |
210 | void pass_1 (const table &table); |
211 | void pass_2 (const table &table); |
212 | |
213 | canvas::size_t get_canvas_size (const table::rect_t &rect) const; |
214 | |
215 | table_dimension_sizes &m_col_widths; |
216 | table_dimension_sizes &m_row_heights; |
217 | }; |
218 | |
219 | /* A class responsible for mapping from table cell coords |
220 | to canvas coords, handling column widths. |
221 | It's the result of solving "how big are all the table cells and where |
222 | do they go?" |
223 | The cell_sizes are passed in, for handling aligning multiple tables, |
224 | sharing column widths or row heights. */ |
225 | |
226 | class table_geometry |
227 | { |
228 | public: |
229 | table_geometry (const table &table, table_cell_sizes &cell_sizes); |
230 | |
231 | void recalc_coords (); |
232 | |
233 | const canvas::size_t get_canvas_size () const { return m_canvas_size; } |
234 | |
235 | canvas::coord_t table_to_canvas (table::coord_t table_coord) const; |
236 | int table_x_to_canvas_x (int table_x) const; |
237 | int table_y_to_canvas_y (int table_y) const; |
238 | |
239 | int get_col_width (int table_x) const |
240 | { |
241 | return m_cell_sizes.m_col_widths.m_requirements[table_x]; |
242 | } |
243 | |
244 | canvas::size_t get_canvas_size (const table::rect_t &rect) const |
245 | { |
246 | return m_cell_sizes.get_canvas_size (rect); |
247 | } |
248 | |
249 | private: |
250 | table_cell_sizes &m_cell_sizes; |
251 | canvas::size_t m_canvas_size; |
252 | |
253 | /* Start canvas column of table cell, including leading border. */ |
254 | std::vector<int> m_col_start_x; |
255 | |
256 | /* Start canvas row of table cell, including leading border. */ |
257 | std::vector<int> m_row_start_y; |
258 | }; |
259 | |
260 | /* Helper class for handling the simple case of a single table |
261 | that doesn't need to be aligned with respect to anything else. */ |
262 | |
263 | struct simple_table_geometry |
264 | { |
265 | simple_table_geometry (const table &table); |
266 | |
267 | table_dimension_sizes m_col_widths; |
268 | table_dimension_sizes m_row_heights; |
269 | table_cell_sizes m_cell_sizes; |
270 | table_geometry m_tg; |
271 | }; |
272 | |
273 | } // namespace text_art |
274 | |
275 | #endif /* GCC_TEXT_ART_TABLE_H */ |
276 | |