1 #ifndef TERMOX_WIDGET_LAYOUTS_SELECTING_HPP
2 #define TERMOX_WIDGET_LAYOUTS_SELECTING_HPP
11 #include <termox/system/key.hpp>
12 #include <termox/system/system.hpp>
13 #include <termox/widget/pipe.hpp>
15 namespace ox::layout {
23 template <
typename Layout_t,
bool unselect_on_focus_out = true>
26 using Key_codes = std::vector<Key>;
28 template <
typename Unary_predicate>
29 using Enable_if_invocable_with_child_t = std::enable_if_t<
30 std::is_invocable_v<Unary_predicate,
31 std::add_const_t<std::add_lvalue_reference_t<
32 typename Layout_t::Child_t>>>,
36 Selecting() { *
this | pipe::strong_focus(); }
38 Selecting(Key_codes increment_selection_keys,
39 Key_codes decrement_selection_keys,
40 Key_codes increment_scroll_keys,
41 Key_codes decrement_scroll_keys)
42 : increment_selection_keys_{increment_selection_keys},
43 decrement_selection_keys_{decrement_selection_keys},
44 increment_scroll_keys_{increment_scroll_keys},
45 decrement_scroll_keys_{decrement_scroll_keys}
47 *
this | pipe::strong_focus();
51 void set_increment_selection_keys(Key_codes keys)
53 increment_selection_keys_ = std::move(keys);
56 void set_decrement_selection_keys(Key_codes keys)
58 decrement_selection_keys_ = std::move(keys);
61 void set_increment_scroll_keys(Key_codes keys)
63 increment_scroll_keys_ = std::move(keys);
66 void set_decrement_scroll_keys(Key_codes keys)
68 decrement_scroll_keys_ = std::move(keys);
76 return selected_ !=
nullptr;
81 typename Layout_t::Child_t const&
83 if (selected_ !=
nullptr)
85 throw std::out_of_range{
"Selecting::select_child(): No Child Selected"};
91 if (selected_ !=
nullptr)
93 throw std::out_of_range{
"Selecting::select_child(): No Child Selected"};
100 return this->find_child_position(selected_);
106 if (this->child_count() > 0)
107 this->set_selected_by_index(this->get_child_offset());
111 auto key_press_event(Key k) ->
bool override
113 if (contains(k, increment_selection_keys_))
114 this->increment_selected_and_scroll_if_necessary();
115 else if (contains(k, decrement_selection_keys_))
116 this->decrement_selected_and_scroll_if_necessary();
117 else if (contains(k, increment_scroll_keys_))
118 this->increment_offset_and_increment_selected();
119 else if (contains(k, decrement_scroll_keys_))
120 this->decrement_offset_and_decrement_selected();
121 return Layout_t::key_press_event(k);
124 auto mouse_wheel_event_filter(
Widget&, Mouse
const& m) ->
bool override
127 case Mouse::Button::ScrollUp:
128 this->decrement_selected_and_scroll_if_necessary();
130 case Mouse::Button::ScrollDown:
131 this->increment_selected_and_scroll_if_necessary();
138 auto mouse_press_event_filter(Widget& w, Mouse
const& m) ->
bool override
140 if (!this->contains_child(&w))
143 case Mouse::Button::Left:
144 this->set_selected_by_pointer(
145 static_cast<typename Layout_t::Child_t*
>(&w));
153 auto resize_event(ox::Area new_size, ox::Area old_size) ->
bool override
155 auto const base_result = Layout_t::resize_event(new_size, old_size);
156 this->reset_selected_if_necessary();
161 auto focus_in_event() ->
bool override
163 this->reset_selected_if_necessary();
166 else if (this->child_count() > 0)
168 return Layout_t::focus_in_event();
171 auto focus_out_event() ->
bool override
173 if constexpr (unselect_on_focus_out) {
177 return Layout_t::focus_out_event();
180 auto enable_event() ->
bool override
184 return Layout_t::enable_event();
187 auto disable_event() ->
bool override
191 return Layout_t::disable_event();
194 auto child_added_event(Widget& child) ->
bool override
196 if (this->child_count() == 1)
198 child.install_event_filter(*
this);
199 return Layout_t::child_added_event(child);
202 auto child_removed_event(Widget& child) ->
bool override
204 if (&child == selected_) {
205 if (this->child_count() > 0)
206 this->set_selected_by_index(this->get_child_offset());
210 return Layout_t::child_removed_event(child);
214 typename Layout_t::Child_t* selected_{
nullptr};
215 Key_codes increment_selection_keys_;
216 Key_codes decrement_selection_keys_;
217 Key_codes increment_scroll_keys_;
218 Key_codes decrement_scroll_keys_;
221 void increment_selected()
224 if (next >= this->child_count())
226 this->set_selected_by_index(next);
229 void increment_selected_and_scroll_if_necessary()
233 this->increment_selected();
235 this->increment_offset();
238 void decrement_selected()
243 this->set_selected_by_index(current - 1);
246 void decrement_selected_and_scroll_if_necessary()
250 this->decrement_selected();
252 this->decrement_offset();
255 void increment_offset_and_increment_selected()
257 this->increment_offset();
258 this->increment_selected();
261 void decrement_offset_and_decrement_selected()
263 if (this->get_child_offset() == 0)
265 this->decrement_offset();
266 this->decrement_selected();
271 void set_selected_by_index(std::size_t index)
273 if (index < this->child_count())
274 this->set_selected_by_pointer(&(this->get_children()[index]));
276 throw std::out_of_range{
"Selecting::set_selected_by_index"};
280 void set_selected_by_pointer(
typename Layout_t::Child_t* child)
283 selected_->unselect();
290 auto find_bottom_row_index() const -> std::
size_t
293 auto const count = this->child_count();
294 auto const offset = this->get_child_offset();
295 for (
auto i = offset + 1; i < count; ++i) {
296 if (children[i].is_enabled())
305 void reset_selected_if_necessary()
311 this->set_selected_by_index(this->find_bottom_row_index());
316 [[nodiscard]]
static auto contains(Key k, Key_codes
const& codes) ->
bool
318 return std::any_of(std::begin(codes), std::end(codes),
319 [=](
auto code) {
return code == k; });
324 template <
typename Layout_t,
325 bool unselect_on_focus_out =
true,
327 [[nodiscard]]
auto selecting(Args&&... args)
328 -> std::unique_ptr<Selecting<Layout_t, unselect_on_focus_out>>
330 return std::make_unique<Selecting<Layout_t, unselect_on_focus_out>>(
331 std::forward<Args>(args)...);
static auto focus_widget() -> Widget *
Return a pointer to the currently focused Widget.
Definition: system.cpp:37
Adds a 'Selected Child' concept to Layout_t.
Definition: selecting.hpp:24
auto selected_index() const -> std::size_t
Return the index into get_children() corresponding to the selected child.
Definition: selecting.hpp:98
auto selected_child() const -> typename Layout_t::Child_t const &
Return the currently selected child, UB if no children in Layout.
Definition: selecting.hpp:80
auto selected_child() -> typename Layout_t::Child_t &
Return the currently selected child, UB if no children in Layout.
Definition: selecting.hpp:89
void select_first_child()
Set the first visible child as the selected child.
Definition: selecting.hpp:104
auto is_child_selected() const -> bool
Return whether or not a child widget is currently selected.
Definition: selecting.hpp:74
auto resize_event(ox::Area new_size, ox::Area old_size) -> bool override
Reset the selected child if needed.
Definition: selecting.hpp:153