TermOx
linear_layout.hpp
1 #ifndef TERMOX_WIDGET_LAYOUTS_DETAIL_LINEAR_LAYOUT_HPP
2 #define TERMOX_WIDGET_LAYOUTS_DETAIL_LINEAR_LAYOUT_HPP
3 #include <cassert>
4 
5 #include <termox/system/event.hpp>
6 #include <termox/widget/layout.hpp>
7 #include <termox/widget/size_policy.hpp>
8 
9 #include "shared_space.hpp"
10 #include "unique_space.hpp"
11 
12 namespace ox::layout::detail {
13 
14 template <typename Get_policy_t, typename Get_length_t, typename Get_offset_t>
16  using get_policy = Get_policy_t; // Size_policy const&(Widget const&)
17  using get_length = Get_length_t; // std::size_t(Widget const&) [w/h]
18  using get_offset = Get_offset_t; // std::size_t(Widget const&) [global x/y]
19 };
20 
21 template <typename Primary_t,
22  typename Secondary_t,
23  typename Get_area_t,
24  typename Get_point_t>
26  using Primary = Primary_t;
27  using Secondary = Secondary_t;
28  using get_area = Get_area_t; // Area(size_t primary, size_t secondary)
29  using get_point = Get_point_t; // Point(size_t primary, size_t secondary)
30 };
31 
33 
34 template <typename Child, typename Parameters>
35 class Linear_layout : public Layout<Child> {
36  public:
37  using Child_t = Child;
38 
39  private:
40  using Base_t = Layout<Child_t>;
41 
42  template <typename UnaryPredicate>
43  using enable_if_invocable = std::enable_if_t<
44  std::is_invocable_v<UnaryPredicate,
45  std::add_lvalue_reference_t<Child_t>>,
46  int>;
47 
48  public:
50 
51  public:
52  // Forwarding functions so that child_offset can be updated. Currently you
53  // don't hold generic Layout pointers anywhere, so these do not have to be
54  // virtual, but keep this in mind if you every hold Layouts and Widgets
55  // separately.
56 
57  [[nodiscard]] auto remove_child(Child_t const* child)
58  -> std::unique_ptr<Widget>
59  {
60  auto result = this->Base_t::remove_child(child);
61  this->reset_offset_if_out_of_bounds();
62  return result;
63  }
64 
66 
67  template <typename UnaryPredicate,
68  typename = enable_if_invocable<UnaryPredicate>>
69  [[nodiscard]] auto remove_child_if(UnaryPredicate&& predicate)
70  -> std::unique_ptr<Widget>
71  {
72  auto result = this->Base_t::remove_child_if(
73  std::forward<UnaryPredicate>(predicate));
74  this->reset_offset_if_out_of_bounds();
75  return result;
76  }
77 
79 
80  [[nodiscard]] auto remove_child_at(std::size_t index)
81  -> std::unique_ptr<Widget>
82  {
83  auto result = this->Base_t::remove_child_at(index);
84  this->reset_offset_if_out_of_bounds();
85  return result;
86  }
87 
89 
90  auto remove_and_delete_child(Child_t const* child) -> bool
91  {
92  auto const result = this->Base_t::remove_and_delete_child(child);
93  this->reset_offset_if_out_of_bounds();
94  return result;
95  }
96 
98 
99  template <typename UnaryPredicate,
100  typename = enable_if_invocable<UnaryPredicate>>
101  auto remove_and_delete_child_if(UnaryPredicate&& predicate) -> bool
102  {
103  auto const result = this->Base_t::remove_and_delete_child_if(
104  std::forward<UnaryPredicate>(predicate));
105  this->reset_offset_if_out_of_bounds();
106  return result;
107  }
108 
110 
111  auto remove_and_delete_child_at(std::size_t index) -> bool
112  {
113  auto const result = this->Base_t::remove_and_delete_child_at(index);
114  this->reset_offset_if_out_of_bounds();
115  return result;
116  }
117 
120  {
122  this->set_child_offset(0);
123  }
124 
126  template <typename Fn>
127  void sort(Fn compare)
128  {
129  std::stable_sort(std::begin(Base_t::children_),
130  std::end(Base_t::children_),
131  [&compare](auto const& a, auto const& b) {
132  return compare(static_cast<Child_t const&>(*a),
133  static_cast<Child_t const&>(*b));
134  });
135  this->resize_and_move_children();
136  }
137 
138  public:
140  void set_child_offset(std::size_t index)
141  {
142  assert(index <= this->child_count());
143  Widget::child_offset_ = index;
144  shared_space_.set_offset(index);
145  unique_space_.set_offset(index);
146  // This works, not sure exactly why, calling resize_and_move_children
147  // directly does not work.
149  }
150 
151  void decrement_offset()
152  {
153  auto const offset = this->get_child_offset();
154  if (offset == 0)
155  return;
156  this->set_child_offset(offset - 1);
157  }
158 
159  void increment_offset()
160  {
161  auto const offset = this->get_child_offset();
162  if (offset + 1 >= this->child_count())
163  return;
164  this->set_child_offset(offset + 1);
165  }
166 
167  protected:
168  using Parameters_t = Parameters;
169 
170  protected:
171  auto enable_event() -> bool override
172  {
173  this->resize_and_move_children();
175  }
176 
177  auto disable_event() -> bool override
178  {
179  for (Widget& w : this->get_children())
180  w.disable();
181  return Widget::disable_event();
182  }
183 
184  auto move_event(Point new_position, Point old_position) -> bool override
185  {
186  this->resize_and_move_children();
187  return Layout<Child>::move_event(new_position, old_position);
188  }
189 
190  auto resize_event(Area new_size, Area old_size) -> bool override
191  {
192  this->resize_and_move_children();
193  return Layout<Child>::resize_event(new_size, old_size);
194  }
195 
196  auto child_added_event(Widget& child) -> bool override
197  {
198  this->resize_and_move_children();
199  return Layout<Child>::child_added_event(child);
200  }
201 
202  auto child_removed_event(Widget& child) -> bool override
203  {
204  this->resize_and_move_children();
206  }
207 
208  auto child_polished_event(Widget& child) -> bool override
209  {
210  this->resize_and_move_children();
212  }
213 
214  private:
215  using Length_list = std::vector<int>;
216  using Position_list = std::vector<int>;
217 
218  Shared_space<Parameters> shared_space_;
219  Unique_space<Parameters> unique_space_;
220 
221  private:
222  void resize_and_move_children()
223  {
224  if (!this->is_enabled())
225  return;
226 
227 #ifndef NDEBUG // Validate Size_policies
228  for (auto& child : this->get_children()) {
229  assert(ox::is_valid(child.width_policy) &&
230  ox::is_valid(child.height_policy));
231  }
232 #endif
233 
234  auto const primary_lengths = shared_space_.calculate_lengths(*this);
235  auto const primary_pos =
236  shared_space_.calculate_positions(primary_lengths);
237 
238  auto const secondary_lengths = unique_space_.calculate_lengths(*this);
239  auto const secondary_pos =
240  unique_space_.calculate_positions(secondary_lengths);
241 
242  this->send_enable_disable_events(primary_lengths, secondary_lengths);
243  this->send_resize_events(primary_lengths, secondary_lengths);
244  this->send_move_events(primary_pos, secondary_pos);
245  }
246 
247  private:
248  void send_enable_disable_events(Length_list const& primary,
249  Length_list const& secondary)
250  {
251  auto const children = this->get_children();
252  auto const offset = this->get_child_offset();
253  for (auto i = 0uL; i < offset; ++i)
254  children[i].disable();
255  for (auto i = 0uL; i < primary.size(); ++i) {
256  auto& child = children[offset + i];
257  if (is_valid(primary[i], secondary[i]))
258  child.enable();
259  else
260  child.disable();
261  }
262  }
263 
264  void send_resize_events(Length_list const& primary,
265  Length_list const& secondary)
266  {
267  auto const children = this->get_children();
268  auto const offset = this->get_child_offset();
269  for (auto i = 0uL; i < primary.size(); ++i) {
270  auto& child = children[offset + i];
271  auto const area =
272  typename Parameters::get_area{}(primary[i], secondary[i]);
273  System::post_event(Resize_event{child, area});
274  }
275  }
276 
277  void send_move_events(Position_list const& primary,
278  Position_list const& secondary)
279  {
280  auto const children = this->get_children();
281  auto const offset = this->get_child_offset();
282  auto const primary_offset =
283  typename Parameters::Primary::get_offset{}(*this);
284  auto const secondary_offset =
285  typename Parameters::Secondary::get_offset{}(*this);
286  for (auto i = 0uL; i < primary.size(); ++i) {
287  auto& child = children[offset + i];
288  auto const point = typename Parameters::get_point{}(
289  primary[i] + primary_offset, secondary[i] + secondary_offset);
290  System::post_event(Move_event{child, point});
291  }
292  }
293 
295 
296  // TODO This is ambiguous, what if a zero width/height wants to be enabled?
297  // Need another data member to tell if something should be enabled or not.
298  // What's the use case? Feb 22, 2021
299  [[nodiscard]] static auto is_valid(std::size_t primary,
300  std::size_t secondary) -> bool
301  {
302  return primary != 0 && secondary != 0;
303  }
304 
306  void reset_offset_if_out_of_bounds()
307  {
308  auto const count = this->child_count();
309  if (count == 0)
310  this->set_child_offset(0);
311  else if (this->get_child_offset() >= count)
312  this->set_child_offset(count - 1);
313  }
314 };
315 
316 } // namespace ox::layout::detail
317 #endif // TERMOX_WIDGET_LAYOUTS_DETAIL_LINEAR_LAYOUT_HPP
static void post_event(Event e)
Append the event to the Event_queue for the thread it was called on.
Definition: system.cpp:112
Definition: widget.hpp:31
virtual auto resize_event(Area new_size, Area old_size) -> bool
Handles Resize_event objects.
Definition: widget.cpp:224
virtual auto move_event(Point new_position, Point old_position) -> bool
Handles Move_event objects.
Definition: widget.cpp:218
virtual auto child_added_event(Widget &child) -> bool
Handles Child_added_event objects.
Definition: widget.cpp:208
virtual auto child_removed_event(Widget &child) -> bool
Handles Child_removed_event objects.
Definition: widget.cpp:210
virtual auto enable_event() -> bool
Handles Enable_event objects.
Definition: widget.cpp:200
auto is_enabled() const -> bool
Check whether the Widget is enabled.
Definition: widget.cpp:102
auto get_child_offset() const -> std::size_t
Return the index of the first child displayed by this Widget.
Definition: widget.cpp:196
virtual auto child_polished_event(Widget &child) -> bool
Handles Child_polished_event objects.
Definition: widget.cpp:212
virtual auto disable_event() -> bool
Handles Disable_event objects.
Definition: widget.cpp:206
void disable(bool disable=true)
Disable this Widget and send a Disable_event to itself.
Definition: widget.cpp:100
auto area() const -> Area
Return the area the widget occupies.
Definition: widget.cpp:108
auto child_count() const -> std::size_t
Return the number of children held by this Widget.
Definition: widget.cpp:198
Provided as a uniform interface for arranging child Widgets.
Definition: layout.hpp:23
auto remove_and_delete_child_if(UnaryPredicate &&predicate) -> bool
Erase first element that satisfies pred.
Definition: layout.hpp:168
auto get_children()
Return a View of all children.
Definition: layout.hpp:56
auto remove_child(Child_t const *child) -> std::unique_ptr< Widget >
Removes and returns the child pointed to by child.
Definition: layout.hpp:118
auto remove_and_delete_child_at(std::size_t index) -> bool
Removes the child at index and sends a Delete_event to it.
Definition: layout.hpp:180
auto remove_and_delete_child(Child_t const *child) -> bool
Removes the child with given pointer and sends a Delete_event to it.
Definition: layout.hpp:155
auto remove_child_at(std::size_t index) -> std::unique_ptr< Widget >
Removes and returns the child at index in the child container.
Definition: layout.hpp:143
void delete_all_children()
Removes all children and sends Delete_events to each.
Definition: layout.hpp:190
auto remove_child_if(UnaryPredicate &&predicate) -> std::unique_ptr< Widget >
Removes and returns the first child where predicate(child) returns true.
Definition: layout.hpp:133
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
auto disable_event() -> bool override
Handles Disable_event objects.
Definition: linear_layout.hpp:177
auto child_removed_event(Widget &child) -> bool override
Handles Child_removed_event objects.
Definition: linear_layout.hpp:202
auto resize_event(Area new_size, Area old_size) -> bool override
Handles Resize_event objects.
Definition: linear_layout.hpp:190
auto remove_child_if(UnaryPredicate &&predicate) -> std::unique_ptr< Widget >
Removes and returns the first child where predicate(child) returns true.
Definition: linear_layout.hpp:69
void set_child_offset(std::size_t index)
Sets the child Widget offset, does not do bounds checking.
Definition: linear_layout.hpp:140
auto remove_and_delete_child(Child_t const *child) -> bool
Removes the child with given pointer and sends a Delete_event to it.
Definition: linear_layout.hpp:90
void sort(Fn compare)
Sort children by the given comparison function.
Definition: linear_layout.hpp:127
void delete_all_children()
Removes all children and sends Delete_events to each.
Definition: linear_layout.hpp:119
auto child_added_event(Widget &child) -> bool override
Handles Child_added_event objects.
Definition: linear_layout.hpp:196
auto move_event(Point new_position, Point old_position) -> bool override
Handles Move_event objects.
Definition: linear_layout.hpp:184
auto remove_child_at(std::size_t index) -> std::unique_ptr< Widget >
Removes and returns the child at index in the child container.
Definition: linear_layout.hpp:80
auto child_polished_event(Widget &child) -> bool override
Handles Child_polished_event objects.
Definition: linear_layout.hpp:208
auto enable_event() -> bool override
Handles Enable_event objects.
Definition: linear_layout.hpp:171
auto remove_and_delete_child_if(UnaryPredicate &&predicate) -> bool
Erase first element that satisfies pred.
Definition: linear_layout.hpp:101
Divides up space between child Widgets where all Widgets share the length.
Definition: shared_space.hpp:15
auto calculate_positions(Length_list const &lengths) -> Position_list
Returns local primary dimension positions, starting at zero.
Definition: shared_space.hpp:45
Gives space to child Widgets where each gets full use of the length.
Definition: unique_space.hpp:13
Definition: event.hpp:67
Definition: linear_layout.hpp:15
Definition: linear_layout.hpp:25