TermOx
shared_space.hpp
1 #ifndef TERMOX_WIDGET_LAYOUTS_DETAIL_SHARED_SPACE_HPP
2 #define TERMOX_WIDGET_LAYOUTS_DETAIL_SHARED_SPACE_HPP
3 #include <cstddef>
4 #include <vector>
5 
6 #include <termox/widget/size_policy.hpp>
7 #include <termox/widget/widget.hpp>
8 
9 #include "layout_span.hpp"
10 
11 namespace ox::layout::detail {
12 
14 template <typename Parameters>
15 class Shared_space {
16  private:
17  using Length_list = std::vector<int>;
18  using Position_list = std::vector<int>;
19 
20  public:
21  [[nodiscard]] auto calculate_lengths(Widget& parent) -> Length_list
22  {
23  // Disperse initial min() space to each child Widget.
24  auto children = [&parent, this] {
25  auto const temp = parent.get_children();
26  return detail::Layout_span{
27  std::next(std::begin(temp), offset_), std::end(temp),
28  typename Parameters::Primary::get_length{}(parent),
29  [](Widget const& w) -> Size_policy const& {
30  return typename Parameters::Primary::get_policy{}(w);
31  }};
32  }();
33 
34  // Have you gone over or under the avaliable space?
35  auto const difference = find_length_difference(parent, children);
36 
37  if (difference > 0)
38  this->disperse(children, difference);
39  else if (difference < 0)
40  this->reclaim(children, -1 * difference);
41  return children.get_results();
42  }
43 
45  [[nodiscard]] auto calculate_positions(Length_list const& lengths)
46  -> Position_list
47  {
48  auto result = Position_list{};
49  result.reserve(lengths.size());
50  auto running_total = 0;
51  for (auto length : lengths) {
52  result.push_back(running_total);
53  running_total += length;
54  }
55  return result;
56  }
57 
59  [[nodiscard]] auto get_offset() const -> std::size_t { return offset_; }
60 
62  void set_offset(std::size_t index) { offset_ = index; }
63 
64  private:
65  std::size_t offset_ = 0;
66 
67  private:
68  template <typename Children_span>
69  void disperse(Children_span& children, int surplus)
70  {
71  auto given_away = -1;
72  while (given_away != 0) {
73  given_away = 0;
74  for (auto iter = children.begin_max(); iter != children.end();
75  ++iter) {
76  auto const& policy = iter.get_policy();
77  auto const max_length = policy.max();
78  auto const stretch_ratio =
79  policy.stretch() / children.total_stretch();
80  auto to_add = int(stretch_ratio * surplus);
81  if ((iter->length + to_add) > max_length)
82  to_add = max_length - iter->length;
83  iter->length += to_add;
84  given_away += to_add;
85  }
86  surplus -= given_away;
87  }
88  this->disperse_leftovers(children, surplus);
89  }
90 
92  template <typename Children_span>
93  void disperse_leftovers(Children_span& children, int surplus)
94  {
95  while (children.size() != 0 && surplus != 0) {
96  for (auto iter = children.begin_max();
97  iter != children.end() && surplus != 0; ++iter) {
98  iter->length += 1;
99  --surplus;
100  }
101  }
102  }
103 
104  template <typename Children_span>
105  void reclaim(Children_span& children, int deficit)
106  {
107  auto taken_back = -1;
108  while (taken_back != 0) {
109  taken_back = 0;
110  for (auto iter = children.begin_min(); iter != children.end();
111  ++iter) {
112  auto const& policy = iter.get_policy();
113  auto const min_length = static_cast<int>(policy.min());
114  auto const inverse_stretch_ratio =
115  (1. / policy.stretch()) / children.total_inverse_stretch();
116  auto to_sub = static_cast<int>(inverse_stretch_ratio * deficit);
117  if ((static_cast<int>(iter->length) - to_sub) < min_length)
118  to_sub = static_cast<int>(iter->length) - min_length;
119  iter->length -= to_sub;
120  taken_back += to_sub;
121  }
122  deficit -= taken_back;
123  }
124  this->reclaim_leftovers(children, deficit);
125  }
126 
128  template <typename Children_span>
129  void reclaim_leftovers(Children_span& children, int deficit)
130  {
131  while (children.size() != 0 && deficit != 0) {
132  for (auto iter = children.begin_min();
133  iter != children.end() && deficit != 0; ++iter, --deficit) {
134  iter->length -= 1;
135  }
136  }
137  }
138 
139  template <typename Children_span>
140  [[nodiscard]] static auto find_length_difference(Widget const& parent,
141  Children_span const& span)
142  -> int
143  {
144  auto const parent_length =
145  typename Parameters::Primary::get_length{}(parent);
146  auto const children_entire_length = span.entire_length();
147  return parent_length - children_entire_length;
148  }
149 };
150 
151 } // namespace ox::layout::detail
152 #endif // TERMOX_WIDGET_LAYOUTS_DETAIL_SHARED_SPACE_HPP
Defines how a Layout should resize a Widget in one length Dimension.
Definition: size_policy.hpp:11
Definition: widget.hpp:31
auto get_children()
Get a range containing Widget& to each child.
Definition: widget.hpp:214
Container view to iterate over a Widget's children, yielding layout info.
Definition: layout_span.hpp:25
Divides up space between child Widgets where all Widgets share the length.
Definition: shared_space.hpp:15
void set_offset(std::size_t index)
Sets the child Widget offset, does not do bounds checking.
Definition: shared_space.hpp:62
auto get_offset() const -> std::size_t
Return the child Widget offset, the first widget included in the layout.
Definition: shared_space.hpp:59
auto calculate_positions(Length_list const &lengths) -> Position_list
Returns local primary dimension positions, starting at zero.
Definition: shared_space.hpp:45