TermOx
stack.hpp
1 #ifndef TERMOX_WIDGET_LAYOUTS_STACK_HPP
2 #define TERMOX_WIDGET_LAYOUTS_STACK_HPP
3 #include <algorithm>
4 #include <cstddef>
5 #include <iterator>
6 #include <memory>
7 #include <stdexcept>
8 #include <type_traits>
9 #include <utility>
10 #include <vector>
11 
12 #include <signals_light/signal.hpp>
13 
14 #include <termox/system/event.hpp>
15 #include <termox/system/system.hpp>
16 #include <termox/widget/area.hpp>
17 #include <termox/widget/detail/link_lifetimes.hpp>
18 #include <termox/widget/layout.hpp>
19 #include <termox/widget/point.hpp>
20 #include <termox/widget/widget.hpp>
21 
22 namespace ox::layout {
23 
25 
28 template <typename Child_t = Widget>
29 class Stack : public Layout<Child_t> {
30  public:
32  sl::Signal<void(std::size_t)> page_changed;
33 
34  public:
36 
38 
40  void set_active_page(std::size_t index)
41  {
42  if (index > this->Stack::size())
43  throw std::out_of_range{"Stack::set_active_page: index is invalid"};
44  auto* previous = active_page_;
45  active_page_ = std::addressof(this->get_children()[index]);
46  if (active_page_ == previous)
47  return;
48  if (previous != nullptr)
49  previous->disable();
50 
51  active_page_->enable(this->is_enabled());
52  // TODO move if enabled and force move if disabled?
53  this->move_active_page();
54  this->resize_active_page();
55  if (sets_focus_ && this->is_enabled())
56  System::set_focus(*active_page_);
57  this->page_changed(index);
58  }
59 
61 
62  void give_focus_on_change(bool sets_focus = true)
63  {
64  sets_focus_ = sets_focus;
65  }
66 
68 
71  template <typename Widget_t = Child_t, typename... Args>
72  auto make_page(Args&&... args) -> Widget_t&
73  {
74  static_assert(std::is_base_of<Child_t, Widget_t>::value,
75  "Stack::make_page: Widget_t must be a Child_t type");
76  return static_cast<Widget_t&>(this->append_page(
77  std::make_unique<Widget_t>(std::forward<Args>(args)...)));
78  }
79 
81 
82  template <typename Widget_t>
83  auto append_page(std::unique_ptr<Widget_t> w_ptr) -> Widget_t&
84  {
85  static_assert(std::is_base_of<Child_t, Widget_t>::value,
86  "Stack::append_page: Widget_t must be a Child_t type");
87  auto& result = this->append_child(std::move(w_ptr));
88  result.disable();
89  return result;
90  }
91 
93 
94  template <typename Widget_t>
95  auto append_page(Widget_t&& w) -> Child_t&
96  {
97  return this->append_page(
98  std::make_unique<Widget_t>(std::forward<Widget_t>(w)));
99  }
100 
102 
105  auto insert_page(std::unique_ptr<Child_t> child, std::size_t index)
106  -> Child_t&
107  {
108  auto& result = this->insert_child(std::move(child), index);
109  result.disable();
110  return result;
111  }
112 
114 
116  void delete_page(std::size_t index)
117  {
118  if (index >= this->Stack::size())
119  throw std::out_of_range{"Stack::delete_page: index is invalid"};
120  auto const* page_to_delete =
121  std::addressof(this->get_children()[index]);
122  if (page_to_delete == this->get_active_page())
123  active_page_ = nullptr;
124  this->remove_and_delete_child(page_to_delete);
125  }
126 
128 
133  [[nodiscard]] auto remove_page(std::size_t index) -> std::unique_ptr<Widget>
134  {
135  if (index >= this->size())
136  throw std::out_of_range{"Stack::remove_page: index is invalid."};
137  auto const* page_to_remove =
138  std::addressof(this->get_children()[index]);
139  if (page_to_remove == this->get_active_page())
140  active_page_ = nullptr;
141  return this->remove_child(page_to_remove);
142  }
143 
145  void clear()
146  {
147  // Can't use a range-for loop, delete_page modifies the child_list_
148  while (!this->get_children().empty())
149  this->delete_page(0);
150  }
151 
153  [[nodiscard]] auto size() const -> std::size_t
154  {
155  return this->child_count();
156  }
157 
159  [[nodiscard]] auto get_active_page() const -> Child_t*
160  {
161  return active_page_;
162  }
163 
165 
166  [[nodiscard]] auto active_page_index() const -> std::size_t
167  {
168  if (active_page_ == nullptr)
169  return Stack::invalid_index;
170  auto begin = std::cbegin(this->get_children());
171  auto const end = std::cend(this->get_children());
172  auto distance = 0;
173  for (; begin != end; ++begin) {
174  if (std::addressof(*begin) == active_page_)
175  break;
176  else
177  ++distance;
178  }
179  return distance;
180  }
181 
183  static auto constexpr invalid_index = static_cast<std::size_t>(-1);
184 
185  protected:
186  auto enable_event() -> bool override
187  {
188  if (active_page_ != nullptr)
189  active_page_->enable();
191  }
192 
193  auto disable_event() -> bool override
194  {
195  if (active_page_ != nullptr)
196  active_page_->disable();
198  }
199 
200  auto move_event(Point new_position, Point old_position) -> bool override
201  {
202  this->move_active_page();
203  return Layout<Child_t>::move_event(new_position, old_position);
204  }
205 
206  auto resize_event(Area new_size, Area old_size) -> bool override
207  {
208  this->resize_active_page();
209  return Layout<Child_t>::resize_event(new_size, old_size);
210  }
211 
212  auto focus_in_event() -> bool override
213  {
214  if (active_page_ != nullptr)
215  ox::System::set_focus(*active_page_);
216  return this->Layout<Child_t>::focus_in_event();
217  }
218 
219  private:
220  Child_t* active_page_ = nullptr;
221  bool sets_focus_ = true;
222 
223  private:
224  void move_active_page()
225  {
226  if (active_page_ != nullptr)
227  System::post_event(Move_event{*active_page_, this->top_left()});
228  }
229 
230  void resize_active_page()
231  {
232  if (active_page_ != nullptr)
233  System::post_event(Resize_event{*active_page_, this->area()});
234  }
235 };
236 
238 template <typename Widget_t = Widget, typename... Args>
239 [[nodiscard]] auto stack(Args&&... args) -> std::unique_ptr<Stack<Widget_t>>
240 {
241  return std::make_unique<Stack<Widget_t>>(std::forward<Args>(args)...);
242 }
243 
244 } // namespace ox::layout
245 
246 // - - - - - - - - - - - - - - - - Slots - - - - - - - - - - - - - - - - - - - -
247 namespace ox::slot {
248 
249 template <typename Child_t>
250 [[nodiscard]] auto set_active_page(layout::Stack<Child_t>& stack)
251  -> sl::Slot<void(std::size_t)>
252 {
253  return link_lifetimes(
254  [&stack](auto index) { stack.set_active_page(index); }, stack);
255 }
256 
257 template <typename Child_t>
258 [[nodiscard]] auto set_active_page(layout::Stack<Child_t>& stack,
259  std::size_t index) -> sl::Slot<void()>
260 {
261  return link_lifetimes([&stack, index] { stack.set_active_page(index); },
262  stack);
263 }
264 
265 template <typename Child_t>
266 [[nodiscard]] auto delete_page(layout::Stack<Child_t>& stack)
267  -> sl::Slot<void(std::size_t)>
268 {
269  return link_lifetimes([&stack](auto index) { stack.delete_page(index); },
270  stack);
271 }
272 
273 template <typename Child_t>
274 [[nodiscard]] auto delete_page(layout::Stack<Child_t>& stack, std::size_t index)
275  -> sl::Slot<void()>
276 {
277  return link_lifetimes([&stack, index] { stack.delete_page(index); }, stack);
278 }
279 
280 template <typename Child_t>
281 [[nodiscard]] auto insert_page(layout::Stack<Child_t>& stack)
282  -> sl::Slot<void(std::size_t, std::unique_ptr<Widget>)>
283 {
284  return link_lifetimes(
285  [&stack](auto index, auto widget) {
286  stack.insert_page(index, std::move(widget));
287  },
288  stack);
289 }
290 
291 template <typename Child_t>
292 [[nodiscard]] auto insert_page(layout::Stack<Child_t>& stack, std::size_t index)
293  -> sl::Slot<void(std::unique_ptr<Widget>)>
294 {
295  return link_lifetimes(
296  [&stack, index](auto widget) {
297  stack.insert_page(index, std::move(widget));
298  },
299  stack);
300 }
301 
302 } // namespace ox::slot
303 #endif // TERMOX_WIDGET_LAYOUTS_STACK_HPP
static void set_focus(Widget &w)
Give program focus to w.
Definition: system.cpp:39
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
auto top_left() const -> Point
Return the global top left corner of this widget.
Definition: widget.cpp:106
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
virtual auto focus_in_event() -> bool
Handles Focus_in_event objects.
Definition: widget.cpp:242
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
void enable(bool enable=true)
Enable this Widget and send an Enable_event to itself.
Definition: widget.cpp:89
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 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(Child_t const *child) -> bool
Removes the child with given pointer and sends a Delete_event to it.
Definition: layout.hpp:155
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 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
A Layout enabling only a single Widget at a time.
Definition: stack.hpp:29
auto get_active_page() const -> Child_t *
Return a pointer to the current active page, or nullptr if none.
Definition: stack.hpp:159
void set_active_page(std::size_t index)
Set child Widget to be enabled/visible via its index into child vector.
Definition: stack.hpp:40
auto append_page(std::unique_ptr< Widget_t > w_ptr) -> Widget_t &
Add an existing Widget as a page to the end of the Stack.
Definition: stack.hpp:83
auto append_page(Widget_t &&w) -> Child_t &
Add an existing Widget as a page to the end of the Stack.
Definition: stack.hpp:95
auto insert_page(std::unique_ptr< Child_t > child, std::size_t index) -> Child_t &
Insert an existing Widget child at index.
Definition: stack.hpp:105
auto resize_event(Area new_size, Area old_size) -> bool override
Handles Resize_event objects.
Definition: stack.hpp:206
auto focus_in_event() -> bool override
Handles Focus_in_event objects.
Definition: stack.hpp:212
auto remove_page(std::size_t index) -> std::unique_ptr< Widget >
Remove page at index from the list and return it.
Definition: stack.hpp:133
static constexpr auto invalid_index
Used to indicate an error on return values of index type.
Definition: stack.hpp:183
auto active_page_index() const -> std::size_t
Return the index of the current active page.
Definition: stack.hpp:166
auto disable_event() -> bool override
Handles Disable_event objects.
Definition: stack.hpp:193
void give_focus_on_change(bool sets_focus=true)
Set whether Focus is given to a child Widget when it becomes active.
Definition: stack.hpp:62
void delete_page(std::size_t index)
Remove a page from the list, by index value, and delete it.
Definition: stack.hpp:116
sl::Signal< void(std::size_t)> page_changed
Emitted when the active page is changed, sends the new index along.
Definition: stack.hpp:32
auto enable_event() -> bool override
Handles Enable_event objects.
Definition: stack.hpp:186
auto move_event(Point new_position, Point old_position) -> bool override
Handles Move_event objects.
Definition: stack.hpp:200
auto size() const -> std::size_t
Return number of pages in this Stack.
Definition: stack.hpp:153
void clear()
Remove and delete all pages.
Definition: stack.hpp:145
auto make_page(Args &&... args) -> Widget_t &
Construct and append a page to the Stack.
Definition: stack.hpp:72
Definition: event.hpp:92