TermOx
passive.hpp
1 #ifndef TERMOX_WIDGET_LAYOUTS_PASSIVE_HPP
2 #define TERMOX_WIDGET_LAYOUTS_PASSIVE_HPP
3 #include <array>
4 #include <memory>
5 #include <type_traits>
6 #include <utility>
7 
8 #include <termox/widget/bordered.hpp>
9 #include <termox/widget/layouts/horizontal.hpp>
10 #include <termox/widget/layouts/vertical.hpp>
11 #include <termox/widget/pipe.hpp>
12 #include <termox/widget/size_policy.hpp>
13 #include <termox/widget/widget.hpp>
14 
15 namespace ox::detail {
16 
17 template <typename W>
18 inline auto constexpr is_horizontal_or_vertical_v =
19  ::ox::layout::is_horizontal_v<W> || ::ox::layout::is_vertical_v<W>;
20 
21 template <typename W>
22 struct Is_bordered : std::false_type {};
23 
24 template <typename W>
25 struct Is_bordered<::ox::Bordered<W>> : std::true_type {};
26 
27 template <typename W>
28 inline auto constexpr is_bordered_v = Is_bordered<W>::value;
29 
30 } // namespace ox::detail
31 
32 namespace ox {
33 template <typename Layout_t, typename SFINAE = void>
34 class Passive;
35 
37 
39 template <typename Layout_t>
40 class Passive<Layout_t,
41  std::enable_if_t<detail::is_horizontal_or_vertical_v<Layout_t> &&
42  !detail::is_bordered_v<Layout_t>,
43  void>> : public Layout_t {
44  private:
45  using Base_t = Layout_t;
46 
47  static auto constexpr is_vertical = layout::is_vertical_v<Base_t>;
48 
49  public:
50  using Parameters = typename Base_t::Parameters;
51 
52  public:
53  template <typename... Args>
54  Passive(Args&&... args) : Base_t{std::forward<Args>(args)...}
55  {
56  this->set_length(
57  sum_hints(this->get_children(), this->get_child_offset()));
58  }
59 
60  Passive(Parameters p) : Base_t{std::move(p)}
61  {
62  this->set_length(
63  sum_hints(this->get_children(), this->get_child_offset()));
64  }
65 
66  protected:
67  auto child_added_event(ox::Widget& child) -> bool override
68  {
69  this->set_length(
70  sum_hints(this->get_children(), this->get_child_offset()));
71  return Base_t::child_added_event(child);
72  }
73 
74  auto child_removed_event(ox::Widget& child) -> bool override
75  {
76  this->set_length(
77  sum_hints(this->get_children(), this->get_child_offset()));
78  return Base_t::child_removed_event(child);
79  }
80 
81  auto child_polished_event(ox::Widget& child) -> bool override
82  {
83  this->set_length(
84  sum_hints(this->get_children(), this->get_child_offset()));
85  return Base_t::child_polished_event(child);
86  }
87 
88  private:
90  static auto sum_hints(decltype(std::declval<Layout_t>().get_children())
91  const& children,
92  std::size_t begin_offset) -> int
93  {
94  auto get_hint = [](Widget const& child) {
95  if constexpr (is_vertical)
96  return child.height_policy.hint();
97  else
98  return child.width_policy.hint();
99  };
100 
101  auto sum = 0;
102  for (auto i = begin_offset; i < children.size(); ++i)
103  sum += get_hint(children[i]);
104  return sum;
105  }
106 
107  void set_length(int length)
108  {
109  if constexpr (is_vertical)
110  *this | pipe::fixed_height(length);
111  else
112  *this | pipe::fixed_width(length);
113  }
114 };
115 
116 template <typename Widget_t>
117 class Passive<Bordered<Widget_t>> : public Bordered<Widget_t> {
118  private:
119  using Base_t = Bordered<Widget_t>;
120 
121  public:
122  using Parameters = typename Base_t::Parameters;
123 
124  public:
125  template <typename... Args>
126  Passive(Args&&... args) : Base_t{std::forward<Args>(args)...}
127  {
128  this->initialize();
129  }
130 
131  Passive(Parameters p) : Base_t{std::move(p)} { this->initialize(); }
132 
133  private:
134  void set_policies(Size_policy height, Size_policy width)
135  {
136  this->height_policy = height;
137  this->width_policy = width;
138  }
139 
141  [[nodiscard]] static auto adjust_size_policies(Widget_t const& wrapped,
142  Border b)
143  -> std::array<ox::Size_policy, 2>
144  {
145  auto const border_height = b.north.has_value() + b.south.has_value();
146  auto const border_width = b.west.has_value() + b.east.has_value();
147 
148  auto hp = wrapped.height_policy;
149  auto wp = wrapped.width_policy;
150 
151  // Overflow Check
152  if (hp.max() <= (Size_policy::maximum_max - border_height))
153  hp.max(hp.max() + border_height);
154  hp.min(hp.min() + border_height);
155  hp.hint(hp.hint() + border_height);
156 
157  // Overflow Check
158  if (wp.max() <= (Size_policy::maximum_max - border_width))
159  wp.max(wp.max() + border_width);
160  wp.min(wp.min() + border_width);
161  wp.hint(wp.hint() + border_width);
162 
163  return {hp, wp};
164  }
165 
166  void initialize()
167  {
168  auto const [h, w] =
169  adjust_size_policies(Base_t::wrapped, this->Base_t::border());
170  this->set_policies(h, w);
171 
172  Base_t::wrapped.width_policy.policy_updated.connect([this] {
173  auto const [h, w] =
174  adjust_size_policies(Base_t::wrapped, this->Base_t::border());
175  this->set_policies(h, w);
176  });
177  Base_t::wrapped.height_policy.policy_updated.connect([this] {
178  auto const [h, w] =
179  adjust_size_policies(Base_t::wrapped, this->Base_t::border());
180  this->set_policies(h, w);
181  });
182  this->border_set.connect([this] {
183  auto const [h, w] =
184  adjust_size_policies(Base_t::wrapped, this->Base_t::border());
185  this->set_policies(h, w);
186  });
187  }
188 };
189 
191 template <typename Widget_t, typename... Args>
192 [[nodiscard]] auto passive(Args&&... args) -> std::unique_ptr<Passive<Widget_t>>
193 {
194  return std::make_unique<Passive<Widget_t>>(std::forward<Args>(args)...);
195 }
196 
198 template <typename Widget_t>
199 [[nodiscard]] auto passive(typename Passive<Widget_t>::Parameters p)
200  -> std::unique_ptr<Passive<Widget_t>>
201 {
202  return std::make_unique<Passive<Widget_t>>(std::move(p));
203 }
204 
205 } // namespace ox
206 #endif // TERMOX_WIDGET_LAYOUTS_PASSIVE_HPP
Creates a Bordered widget, which wraps the template type in a Border.
Definition: bordered.hpp:233
Definition: passive.hpp:34
Defines how a Layout should resize a Widget in one length Dimension.
Definition: size_policy.hpp:11
void hint(int value)
Set the size hint, used as the initial value in calculations.
Definition: size_policy.cpp:16
static constexpr auto maximum_max
Largest possible value for max().
Definition: size_policy.hpp:26
Definition: widget.hpp:31
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
Provided as a uniform interface for arranging child Widgets.
Definition: layout.hpp:23
Defines which border walls are enabled and how they are displayed.
Definition: bordered.hpp:21
Definition: passive.hpp:22
Definition: layout.hpp:27