TermOx
bordered.hpp
1 #ifndef TERMOX_WIDGET_BORDER_DEV_HPP
2 #define TERMOX_WIDGET_BORDER_DEV_HPP
3 #include <limits>
4 #include <memory>
5 #include <optional>
6 #include <type_traits>
7 #include <utility>
8 
9 #include <termox/painter/color.hpp>
10 #include <termox/widget/focus_policy.hpp>
11 #include <termox/widget/layouts/horizontal.hpp>
12 #include <termox/widget/layouts/vertical.hpp>
13 #include <termox/widget/tuple.hpp>
14 #include <termox/widget/widget.hpp>
15 #include <termox/widget/widgets/tile.hpp>
16 
17 namespace ox {
18 
20 
21 struct Border {
22  using Wall = std::optional<Glyph>;
23 
24  Wall north;
25  Wall south;
26  Wall east;
27  Wall west;
28  Glyph nw_corner; // North West
29  Glyph ne_corner; // North East
30  Glyph sw_corner; // South West
31  Glyph se_corner; // South East
32 };
33 
34 } // namespace ox
35 
36 namespace ox::detail {
37 
39 template <typename T>
40 auto constexpr pipe_all(Border& b, T x) -> Border&
41 {
42  if (b.north.has_value())
43  *b.north |= x;
44  if (b.south.has_value())
45  *b.south |= x;
46  if (b.east.has_value())
47  *b.east |= x;
48  if (b.west.has_value())
49  *b.west |= x;
50  b.nw_corner |= x;
51  b.ne_corner |= x;
52  b.sw_corner |= x;
53  b.se_corner |= x;
54  return b;
55 }
56 
57 } // namespace ox::detail
58 namespace ox {
59 
60 [[nodiscard]] auto constexpr operator|(Border b, Background_color bg) -> Border
61 {
62  return detail::pipe_all(b, bg);
63 }
64 
65 auto constexpr operator|=(Border& b, Background_color bg) -> Border&
66 {
67  return detail::pipe_all(b, bg);
68 }
69 
70 [[nodiscard]] auto constexpr operator|(Border b, Foreground_color fg) -> Border
71 {
72  return detail::pipe_all(b, fg);
73 }
74 
75 auto constexpr operator|=(Border& b, Foreground_color fg) -> Border&
76 {
77  return detail::pipe_all(b, fg);
78 }
79 
80 [[nodiscard]] auto constexpr operator|(Border b, Traits t) -> Border
81 {
82  return detail::pipe_all(b, t);
83 }
84 
85 auto constexpr operator|=(Border& b, Traits t) -> Border&
86 {
87  return detail::pipe_all(b, t);
88 }
89 
91 [[nodiscard]] auto drop_north(Border b) -> Border;
92 
94 [[nodiscard]] auto drop_south(Border b) -> Border;
95 
97 [[nodiscard]] auto drop_east(Border b) -> Border;
98 
100 [[nodiscard]] auto drop_west(Border b) -> Border;
101 
103 [[nodiscard]] auto take_north(Border b) -> Border;
104 
106 [[nodiscard]] auto take_south(Border b) -> Border;
107 
109 [[nodiscard]] auto take_east(Border b) -> Border;
110 
112 [[nodiscard]] auto take_west(Border b) -> Border;
113 
114 } // namespace ox
115 
116 namespace ox::border {
117 
118 [[nodiscard]] auto constexpr none() -> Border
119 {
120  return {std::nullopt, std::nullopt, std::nullopt, std::nullopt,
121  U' ', U' ', U' ', U' '};
122 }
123 
124 [[nodiscard]] auto constexpr squared() -> Border
125 {
126  return {U'─', U'─', U'│', U'│', U'┌', U'┐', U'└', U'┘'};
127 }
128 
129 [[nodiscard]] auto constexpr rounded() -> Border
130 {
131  return {U'─', U'─', U'│', U'│', U'╭', U'╮', U'╰', U'╯'};
132 }
133 
134 [[nodiscard]] auto constexpr plus_corners() -> Border
135 {
136  return {U'─', U'─', U'│', U'│', U'+', U'+', U'+', U'+'};
137 }
138 
139 [[nodiscard]] auto constexpr asterisk() -> Border
140 {
141  return {U'*', U'*', U'*', U'*', U'*', U'*', U'*', U'*'};
142 }
143 
144 [[nodiscard]] auto constexpr doubled() -> Border
145 {
146  return {U'═', U'═', U'║', U'║', U'╔', U'╗', U'╚', U'╝'};
147 }
148 
149 [[nodiscard]] auto constexpr bold() -> Border
150 {
151  return {U'━', U'━', U'┃', U'┃', U'┏', U'┓', U'┗', U'┛'};
152 }
153 
154 [[nodiscard]] auto constexpr dashed_1() -> Border
155 {
156  return {U'╶', U'╶', U'╷', U'╷', U'┌', U'┐', U'└', U'┘'};
157 }
158 
159 [[nodiscard]] auto constexpr bold_dashed_1() -> Border
160 {
161  return {U'╺', U'╺', U'╻', U'╻', U'┏', U'┓', U'┗', U'┛'};
162 }
163 
164 [[nodiscard]] auto constexpr dashed_2() -> Border
165 {
166  return {U'╌', U'╌', U'╎', U'╎', U'┌', U'┐', U'└', U'┘'};
167 }
168 
169 [[nodiscard]] auto constexpr bold_dashed_2() -> Border
170 {
171  return {U'╍', U'╍', U'╏', U'╏', U'┏', U'┓', U'┗', U'┛'};
172 }
173 
174 [[nodiscard]] auto constexpr dashed_3() -> Border
175 {
176  return {U'┄', U'┄', U'┆', U'┆', U'┌', U'┐', U'└', U'┘'};
177 }
178 
179 [[nodiscard]] auto constexpr bold_dashed_3() -> Border
180 {
181  return {U'┅', U'┅', U'┇', U'┇', U'┏', U'┓', U'┗', U'┛'};
182 }
183 
184 [[nodiscard]] auto constexpr dashed_4() -> Border
185 {
186  return {U'┈', U'┈', U'┊', U'┊', U'┌', U'┐', U'└', U'┘'};
187 }
188 
189 [[nodiscard]] auto constexpr bold_dashed_4() -> Border
190 {
191  return {U'┉', U'┉', U'┋', U'┋', U'┏', U'┓', U'┗', U'┛'};
192 }
193 
194 [[nodiscard]] auto constexpr block_1() -> Border
195 {
196  return {U'█', U'█', U'█', U'█', U'█', U'█', U'█', U'█'};
197 }
198 
199 [[nodiscard]] auto constexpr block_2() -> Border
200 {
201  return {U'▓', U'▓', U'▓', U'▓', U'▓', U'▓', U'▓', U'▓'};
202 }
203 
204 [[nodiscard]] auto constexpr block_3() -> Border
205 {
206  return {U'▒', U'▒', U'▒', U'▒', U'▒', U'▒', U'▒', U'▒'};
207 }
208 
209 [[nodiscard]] auto constexpr block_4() -> Border
210 {
211  return {U'░', U'░', U'░', U'░', U'░', U'░', U'░', U'░'};
212 }
213 
214 [[nodiscard]] auto constexpr half_block() -> Border
215 {
216  return {U'▄' | Trait::Inverse,
217  U'▄',
218  U'▌' | Trait::Inverse,
219  U'▌',
220  U'▛',
221  U'▜',
222  U'▙',
223  U'▟'};
224 }
225 
226 } // namespace ox::border
227 
228 namespace ox {
229 
231 
232 template <typename Widget_t, typename Column = layout::Vertical<>>
233 class Bordered : public layout::Horizontal<Column> {
234  private:
236  using Corner = Tile;
237 
239  template <template <typename> typename Layout_t>
240  class Wall : public Widget {
241  public:
242  Wall(Glyph g)
243  {
244  if constexpr (layout::is_vertical_v<Layout_t<Widget>>)
245  this->width_policy = Size_policy::fixed(1);
246  else
248  this->set_glyph(g);
249  }
250 
251  public:
253  void set_glyph(Glyph g) { this->set_wallpaper(g); }
254 
256  [[nodiscard]] auto glyph() const -> Glyph
257  {
258  return this->get_wallpaper();
259  }
260  };
261 
262  using HWall = Wall<layout::Horizontal>;
263  using VWall = Wall<layout::Vertical>;
264 
265  public:
266  struct Parameters {
267  Border border;
268  typename Widget_t::Parameters wrapped_parameters;
269  };
270 
271  public:
272  Widget_t& wrapped;
273 
275  sl::Signal<void()> border_set;
276 
277  public:
278  template <typename... Args>
279  explicit Bordered(Border b, Args&&... wrapped_args)
280  : wrapped{this->template make_child<Column>()
281  .template make_child<Widget_t>(
282  std::forward<Args>(wrapped_args)...)},
283  border_{std::move(b)}
284  {
285  this->initialize();
286  }
287 
288  // This overload is required for apple-clang and clang 9 & 10.
289  // Otherwise you'd just have Border have a default value above.
290  Bordered()
291  : wrapped{this->template make_child<Column>()
292  .template make_child<Widget_t>()},
293  border_{border::rounded()}
294  {
295  this->initialize();
296  }
297 
298  explicit Bordered(Parameters p)
299  : Bordered{std::move(p.border), std::move(p.wrapped_parameters)}
300  {}
301 
303 
304  explicit Bordered(std::unique_ptr<Widget_t> w_ptr)
305  : wrapped{this->template make_child<Column>().append_child(
306  std::move(w_ptr))},
307  border_{border::rounded()}
308  {
309  static_assert(std::is_base_of<Widget, Widget_t>::value,
310  "Bordered: Widget_t must be a Widget type");
311  this->initialize();
312  }
313 
314  public:
317  {
318  this->delete_all_border_pieces(border_);
319  border_ = b;
320  this->build_border_pieces(border_);
321  border_set.emit();
322  }
323 
325  auto border() const -> Border { return border_; }
326 
327  private:
328  Border border_;
329 
330  private:
332  void build_border_pieces(Border const& b)
333  {
334  auto const left_column =
335  b.west.has_value() ? std::optional{0} : std::nullopt;
336  auto const mid_column = b.west.has_value() ? 1 : 0;
337  auto const right_column =
338  b.east.has_value() ? std::optional{mid_column + 1} : std::nullopt;
339 
340  auto const fp = wrapped.focus_policy;
341  auto const init = [&](auto& w) {
342  w.focus_policy = fp;
343  w.focused_in.connect([&] { System::set_focus(wrapped); });
344  };
345 
346  // Call order is important.
347  if (left_column.has_value()) {
348  auto& lc =
349  this->insert_child(std::make_unique<Column>(), *left_column);
350  lc.width_policy = Size_policy::fixed(1);
351  if (b.north.has_value())
352  init(lc.template make_child<Corner>(b.nw_corner));
353  init(lc.template make_child<VWall>(*b.west));
354  if (b.south.has_value())
355  init(lc.template make_child<Corner>(b.sw_corner));
356  }
357  if (right_column.has_value()) {
358  auto& rc =
359  this->insert_child(std::make_unique<Column>(), *right_column);
360  rc.width_policy = Size_policy::fixed(1);
361  if (b.north.has_value())
362  init(rc.template make_child<Corner>(b.ne_corner));
363  init(rc.template make_child<VWall>(*b.east));
364  if (b.south.has_value())
365  init(rc.template make_child<Corner>(b.se_corner));
366  }
367  if (b.north.has_value()) {
368  init(this->get_children()[mid_column].insert_child(
369  std::make_unique<HWall>(*b.north), 0));
370  }
371  if (b.south.has_value()) {
372  init(this->get_children()[mid_column].template make_child<HWall>(
373  *b.south));
374  }
375  }
376 
378  void delete_all_border_pieces(Border b)
379  {
380  auto const left_column =
381  b.west.has_value() ? std::optional{0} : std::nullopt;
382  auto const mid_column = b.west.has_value() ? 1 : 0;
383  auto const right_column =
384  b.east.has_value() ? std::optional{mid_column + 1} : std::nullopt;
385 
386  // Call order is important.
387  if (b.south.has_value()) {
388  auto const back = b.north.has_value() ? 2 : 1;
389  this->get_children()[mid_column].remove_and_delete_child_at(back);
390  }
391  if (b.north.has_value())
392  this->get_children()[mid_column].remove_and_delete_child_at(0);
393  if (right_column.has_value())
394  this->remove_and_delete_child_at(*right_column);
395  if (left_column.has_value())
396  this->remove_and_delete_child_at(*left_column);
397  }
398 
399  void initialize()
400  {
401  // Can't use pipe:: in this file.
402  this->focus_policy = wrapped.focus_policy;
403  this->focused_in.connect([&] { System::set_focus(wrapped); });
404 
405  this->build_border_pieces(border_);
406  }
407 };
408 
410 template <typename Widget_t>
411 [[nodiscard]] auto bordered(typename Bordered<Widget_t>::Parameters p = {
412  border::rounded(),
413  {}}) -> std::unique_ptr<Bordered<Widget_t>>
414 {
415  return std::make_unique<Bordered<Widget_t>>(std::move(p));
416 }
417 
419 template <typename Widget_t, typename... Args>
420 [[nodiscard]] auto bordered(Border b = border::rounded(),
421  Args&&... wrapped_args)
422 {
423  return std::make_unique<Bordered<Widget_t>>(
424  std::move(b), std::forward<Args>(wrapped_args)...);
425 }
426 
427 template <typename Widget_t>
428 [[nodiscard]] auto bordered(std::unique_ptr<Widget_t> w_ptr)
429  -> std::unique_ptr<Bordered<Widget_t>>
430 {
431  return std::make_unique<Bordered<Widget_t>>(std::move(w_ptr));
432 }
433 
434 } // namespace ox
435 #endif // TERMOX_WIDGET_BORDER_DEV_HPP
Creates a Bordered widget, which wraps the template type in a Border.
Definition: bordered.hpp:233
Bordered(std::unique_ptr< Widget_t > w_ptr)
Create a border around an existing Widget.
Definition: bordered.hpp:304
sl::Signal< void()> border_set
Called on set_border(...) after the Border has been set.
Definition: bordered.hpp:275
auto border() const -> Border
Return the currenly set Border.
Definition: bordered.hpp:325
void set_border(Border b)
Set the currently used Border, updating the display.
Definition: bordered.hpp:316
static auto fixed(int hint) -> Size_policy
Fixed: hint is the only acceptable size.
Definition: size_policy.cpp:55
static void set_focus(Widget &w)
Give program focus to w.
Definition: system.cpp:39
A unit width/height Widget that can display a single Glyph.
Definition: tile.hpp:12
Definition: widget.hpp:31
auto get_wallpaper() const -> Glyph
Return the currently in use wallpaper or std::nullopt if none.
Definition: widget.cpp:87
void set_wallpaper(Glyph g)
Used to fill in empty space that is not filled in by paint_event().
Definition: widget.cpp:81
Size_policy height_policy
Describes how the height of this Widget should be modified by a Layout.
Definition: widget.hpp:102
Size_policy width_policy
Describes how the width of this Widget should be modified by a Layout.
Definition: widget.hpp:99
Focus_policy focus_policy
Describes how focus is given to this Widget.
Definition: widget.hpp:93
auto get_children()
Return a View of all children.
Definition: layout.hpp:56
auto insert_child(std::unique_ptr< Widget_t > w, std::size_t index) -> Widget_t &
Inserts w at index, sending child added event to *this.
Definition: layout.hpp:78
auto make_child(Args &&... args) -> Widget_t &
Create a Widget and append it to the child container.
Definition: layout.hpp:103
auto append_child(std::unique_ptr< Widget_t > w) -> Widget_t &
Move w to the end of the child container. Forwards to insert_child()
Definition: layout.hpp:95
Lays out Widgets in 2D, sharing space in a primary dimension.
Definition: linear_layout.hpp:35
auto remove_and_delete_child_at(std::size_t index) -> bool
Removes the child at index and sends a Delete_event to it.
Definition: linear_layout.hpp:111
Defines which border walls are enabled and how they are displayed.
Definition: bordered.hpp:21
Definition: bordered.hpp:266
Holds a description of a paintable tile on the screen.
Definition: glyph.hpp:11