1 | /* Hierarchical diagram elements. |
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_WIDGET_H |
22 | #define GCC_TEXT_ART_WIDGET_H |
23 | |
24 | #include "text-art/canvas.h" |
25 | #include "text-art/table.h" |
26 | |
27 | namespace text_art { |
28 | |
29 | /* Abstract base class: something that knows how to size itself and |
30 | how to paint itself to a canvas, potentially with children, with |
31 | support for hierarchical sizing and positioning. |
32 | |
33 | Widgets have a two-phase sizing/positioning algorithm. |
34 | |
35 | Step 1: size requests: the root widget is asked for its size request i.e |
36 | how big it wants to be. This is handled by recursively asking child |
37 | widgets for their requested sizes. Each widget subclass can implement |
38 | their own logic for this in the "calc_req_size" vfunc, and the result |
39 | is cached in m_req_size. |
40 | |
41 | Step 2: rect allocation: the root widget is set a canvas::rect_t as |
42 | its "allocated" rectangle. Each widget subclass can then place its |
43 | children recursively using the "update_child_alloc_rects" vfunc. |
44 | For simplicity, all coordinates in the hierarchy are within the same |
45 | coordinate system (rather than attempting to store per-child offsets). |
46 | |
47 | Widget subclasses are responsible for managing their own children. */ |
48 | |
49 | /* Subclasses in this header, with indentation indicating inheritance. */ |
50 | |
51 | class widget; /* Abstract base class. */ |
52 | class wrapper_widget; /* Concrete subclass: a widget with a single child. */ |
53 | class container_widget; /* Abstract subclass: widgets with an arbitrary |
54 | number of children. */ |
55 | class vbox_widget; /* Concrete widget subclass: lay out children |
56 | vertically. */ |
57 | class leaf_widget; /* Abstract subclass: a widget with no children. */ |
58 | class text_widget; /* Concrete subclass: a text string. */ |
59 | class canvas_widget; /* Concrete subclass: a pre-rendered canvas. */ |
60 | |
61 | class widget |
62 | { |
63 | public: |
64 | /* This can be very useful for debugging when implementing new |
65 | widget subclasses. */ |
66 | static const bool DEBUG_GEOMETRY = false; |
67 | |
68 | virtual ~widget () {} |
69 | |
70 | canvas to_canvas (const style_manager &style_mgr); |
71 | |
72 | canvas::size_t get_req_size () |
73 | { |
74 | m_req_size = calc_req_size(); |
75 | if (DEBUG_GEOMETRY) |
76 | fprintf (stderr, format: "calc_req_size (%s) -> (w:%i, h:%i)\n" , |
77 | get_desc (), |
78 | m_req_size.w, m_req_size.h); |
79 | return m_req_size; |
80 | } |
81 | |
82 | void set_alloc_rect (const canvas::rect_t &rect) |
83 | { |
84 | if (DEBUG_GEOMETRY) |
85 | fprintf (stderr, format: "set_alloc_rect (%s): ((x:%i, y:%i), (w:%i, h:%i))\n" , |
86 | get_desc (), |
87 | rect.m_top_left.x, rect.m_top_left.y, |
88 | rect.m_size.w, rect.m_size.h); |
89 | m_alloc_rect = rect; |
90 | update_child_alloc_rects (); |
91 | } |
92 | |
93 | virtual const char *get_desc () const = 0; |
94 | virtual canvas::size_t calc_req_size () = 0; |
95 | virtual void update_child_alloc_rects () = 0; |
96 | virtual void paint_to_canvas (canvas &canvas) = 0; |
97 | |
98 | /* Access to the cached size request of this widget. */ |
99 | const canvas::size_t get_req_size () const { return m_req_size; } |
100 | int get_req_w () const { return m_req_size.w; } |
101 | int get_req_h () const { return m_req_size.h; } |
102 | |
103 | /* Access to the allocated canvas coordinates of this widget. */ |
104 | const canvas::rect_t &get_alloc_rect () const { return m_alloc_rect; } |
105 | int get_alloc_w () const { return m_alloc_rect.get_width (); } |
106 | int get_alloc_h () const { return m_alloc_rect.get_height (); } |
107 | int get_min_x () const { return m_alloc_rect.get_min_x (); } |
108 | int get_max_x () const { return m_alloc_rect.get_max_x (); } |
109 | int get_next_x () const { return m_alloc_rect.get_next_x (); } |
110 | int get_min_y () const { return m_alloc_rect.get_min_y (); } |
111 | int get_max_y () const { return m_alloc_rect.get_max_y (); } |
112 | int get_next_y () const { return m_alloc_rect.get_max_y (); } |
113 | canvas::range_t get_x_range () const { return m_alloc_rect.get_x_range (); } |
114 | canvas::range_t get_y_range () const { return m_alloc_rect.get_y_range (); } |
115 | const canvas::coord_t &get_top_left () const |
116 | { |
117 | return m_alloc_rect.m_top_left; |
118 | } |
119 | |
120 | protected: |
121 | widget () |
122 | : m_req_size (0, 0), |
123 | m_alloc_rect (canvas::coord_t (0, 0), |
124 | canvas::size_t (0, 0)) |
125 | {} |
126 | |
127 | private: |
128 | /* How much size this widget requested. */ |
129 | canvas::size_t m_req_size; |
130 | /* Where (and how big) this widget was allocated. */ |
131 | canvas::rect_t m_alloc_rect; |
132 | }; |
133 | |
134 | /* Concrete subclass for a widget with a single child. */ |
135 | |
136 | class wrapper_widget : public widget |
137 | { |
138 | public: |
139 | wrapper_widget (std::unique_ptr<widget> child) |
140 | : m_child (std::move (child)) |
141 | {} |
142 | |
143 | const char *get_desc () const override |
144 | { |
145 | return "wrapper_widget" ; |
146 | } |
147 | canvas::size_t calc_req_size () override |
148 | { |
149 | return m_child->get_req_size (); |
150 | } |
151 | void update_child_alloc_rects () override |
152 | { |
153 | m_child->set_alloc_rect (get_alloc_rect ()); |
154 | } |
155 | void paint_to_canvas (canvas &canvas) override |
156 | { |
157 | m_child->paint_to_canvas (canvas); |
158 | } |
159 | private: |
160 | std::unique_ptr<widget> m_child; |
161 | }; |
162 | |
163 | /* Abstract subclass for widgets with an arbitrary number of children. */ |
164 | |
165 | class container_widget : public widget |
166 | { |
167 | public: |
168 | void add_child (std::unique_ptr<widget> child) |
169 | { |
170 | m_children.push_back (x: std::move (child)); |
171 | } |
172 | |
173 | void paint_to_canvas (canvas &canvas) final override |
174 | { |
175 | for (auto &child : m_children) |
176 | child->paint_to_canvas (canvas); |
177 | } |
178 | |
179 | protected: |
180 | std::vector<std::unique_ptr<widget>> m_children; |
181 | }; |
182 | |
183 | /* Concrete widget subclass: lay out children vertically. */ |
184 | |
185 | class vbox_widget : public container_widget |
186 | { |
187 | public: |
188 | const char *get_desc () const override; |
189 | canvas::size_t calc_req_size () override; |
190 | void update_child_alloc_rects () final override; |
191 | }; |
192 | |
193 | /* Abstract subclass for widgets with no children. */ |
194 | |
195 | class leaf_widget : public widget |
196 | { |
197 | public: |
198 | void update_child_alloc_rects () final override |
199 | { |
200 | /* no-op. */ |
201 | } |
202 | |
203 | protected: |
204 | leaf_widget () : widget () {} |
205 | }; |
206 | |
207 | /* Concrete widget subclass for a text string. */ |
208 | |
209 | class text_widget : public leaf_widget |
210 | { |
211 | public: |
212 | text_widget (styled_string str) |
213 | : leaf_widget (), m_str (std::move (str)) |
214 | { |
215 | } |
216 | |
217 | const char *get_desc () const override; |
218 | canvas::size_t calc_req_size () final override; |
219 | void paint_to_canvas (canvas &canvas) final override; |
220 | |
221 | private: |
222 | styled_string m_str; |
223 | }; |
224 | |
225 | /* Concrete widget subclass for a pre-rendered canvas. */ |
226 | |
227 | class canvas_widget : public leaf_widget |
228 | { |
229 | public: |
230 | canvas_widget (canvas &&c) |
231 | : leaf_widget (), m_canvas (std::move (c)) |
232 | { |
233 | } |
234 | |
235 | const char *get_desc () const override; |
236 | canvas::size_t calc_req_size () final override; |
237 | void paint_to_canvas (canvas &canvas) final override; |
238 | |
239 | private: |
240 | canvas m_canvas; |
241 | }; |
242 | |
243 | } // namespace text_art |
244 | |
245 | #endif /* GCC_TEXT_ART_WIDGET_H */ |
246 | |