HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
selection_delegate.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
11#include "../l10n/l10n.hpp"
12#include "../observer/observer.hpp"
13#include "../utility/utility.hpp"
14#include "../concurrency/concurrency.hpp"
15#include "../dispatch/dispatch.hpp"
16#include "../GUI/GUI.hpp"
17#include "../macros.hpp"
18#include "radio_delegate.hpp"
19#include "radio_widget.hpp"
20#include <memory>
21#include <functional>
22#include <vector>
23
24hi_export_module(hikogui.widgets.selection_delegate);
25
26hi_export namespace hi { inline namespace v1 {
27class selection_widget;
28
34public:
35 virtual ~selection_delegate() = default;
36
37 virtual void init(widget_intf const &sender) {}
38 virtual void deinit(widget_intf const &sender) {}
39
45 [[nodiscard]] virtual std::optional<widget_id> keyboard_focus_id(widget_intf const& sender) const noexcept
46 {
47 return std::nullopt;
48 }
49
52 [[nodiscard]] virtual size_t size(widget_intf const& sender) const noexcept
53 {
54 return 0;
55 }
56
57 [[nodiscard]] bool empty(widget_intf const& sender) const noexcept
58 {
59 return size(sender) == 0;
60 }
61
69 [[nodiscard]] virtual std::unique_ptr<widget> make_option_widget(widget_intf const& sender, widget_intf const &parent, size_t index) noexcept = 0;
70
76 [[nodiscard]] virtual std::optional<label> selected_label(widget_intf const& sender) const noexcept
77 {
78 return std::nullopt;
79 }
80
83 template<forward_of<void()> Func>
84 [[nodiscard]] callback<void()> subscribe_on_value(Func&& func, callback_flags flags = callback_flags::synchronous) noexcept
85 {
86 return _value_notifier.subscribe(std::forward<Func>(func), flags);
87 }
88
91 template<forward_of<void()> Func>
92 [[nodiscard]] callback<void()> subscribe_on_options(Func&& func, callback_flags flags = callback_flags::synchronous) noexcept
93 {
94 return _options_notifier.subscribe(std::forward<Func>(func), flags);
95 }
96
97protected:
98 notifier<void()> _value_notifier;
99 notifier<void()> _options_notifier;
100};
101
107template<typename T>
109public:
110 using value_type = T;
111 using option_type = std::pair<value_type, label>;
112 using options_type = std::vector<option_type>;
113
116
122 template<forward_of<observer<value_type>> Value, forward_of<observer<options_type>> Options>
123 default_selection_delegate(Value&& value, Options&& options) noexcept :
124 value(std::forward<Value>(value)), options(std::forward<Options>(options))
125 {
126 _option_delegate = std::make_shared<option_delegate_type>(*this);
127
128 // clang-format off
129 _value_cbt = this->value.subscribe([&](auto...){ this->_value_notifier(); });
130 _options_cbt = this->options.subscribe([&](auto...){ this->_options_notifier(); });
131 // clang-format on
132 }
133
134 [[nodiscard]] size_t size(widget_intf const& sender) const noexcept override
135 {
136 return options->size();
137 }
138
139 [[nodiscard]] std::optional<label> selected_label(widget_intf const& sender) const noexcept override
140 {
141 for (auto&& option : *options) {
142 if (option.first == *value) {
143 return option.second;
144 }
145 }
146 return std::nullopt;
147 }
148
149 [[nodiscard]] std::optional<widget_id> keyboard_focus_id(widget_intf const& sender) const noexcept override
150 {
151 return _option_delegate->keyboard_focus_id();
152 }
153
161 [[nodiscard]] std::unique_ptr<widget> make_option_widget(widget_intf const& sender, widget_intf const &parent, size_t index) noexcept override
162 {
163 auto& [option_value, option_label] = options->at(index);
164 return _option_delegate->make_option_widget(sender, parent, option_value, option_label, _option_delegate);
165 }
166
167private:
168 class option_delegate_type : public radio_delegate {
169 public:
170 option_delegate_type(default_selection_delegate &parent) noexcept : _parent(&parent)
171 {
172 _value_cbt = _parent->value.subscribe([&](auto...) {
173 this->_notifier();
174 });
175 }
176
177 void init(widget_intf const& sender) noexcept override
178 {
179 hi_assert(_next_value, "The value was not set of this option widget.");
180
181 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
182 hi_assert(it == _senders.end() or it->id != sender.id, "button was already registered with selection-delegate.");
183
184 _senders.emplace(it, sender.id, *_next_value);
185 _next_value = std::nullopt;
186 }
187
188 void deinit(widget_intf const& sender) noexcept override
189 {
190 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
191 if (it != _senders.end() and it->id == sender.id) {
192 _senders.erase(it);
193 }
194 }
195
196 [[nodiscard]] std::optional<widget_id> keyboard_focus_id() const noexcept
197 {
198 if (_senders.empty()) {
199 return std::nullopt;
200 }
201
202 for (auto const& sender : _senders) {
203 if (sender.value == *_parent->value) {
204 return sender.id;
205 }
206 }
207 return _senders.front().id;
208 }
209
210 [[nodiscard]] std::unique_ptr<widget>
211 make_option_widget(widget_intf const& sender, widget_intf const &parent, value_type const& value, label const& label, std::shared_ptr<option_delegate_type> shared_this) noexcept
212 {
213 using button_widget = radio_menu_button_widget;
214 using button_attributes = radio_menu_button_widget::attributes_type;
215
216 // Prepare the value for the next widget, so that the widget immediately can retrieve its value.
217 _next_value = value;
218 return std::make_unique<button_widget>(std::addressof(parent), button_attributes{label}, std::move(shared_this));
219 }
220
221 [[nodiscard]] widget_value state(widget_intf const& sender) const noexcept override
222 {
223 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
224
225 if (it != _senders.end() and it->id == sender.id) {
226 return *_parent->value == it->value ? widget_value::on : widget_value::off;
227
228 } else {
229 // button-button was not yet registered.
230 return widget_value::off;
231 }
232 }
233
234 void activate(widget_intf const& sender) noexcept override
235 {
236 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
237
238 if (it != _senders.end() and it->id == sender.id) {
239 _parent->value = it->value;
240 }
241 }
242
243 private:
244 struct sender_info_type {
245 widget_id id = {};
246 value_type value;
247
248 [[nodiscard]] friend bool operator==(sender_info_type const& lhs, widget_id const& rhs) noexcept
249 {
250 return lhs.id == rhs;
251 }
252
253 [[nodiscard]] friend auto operator<=>(sender_info_type const& lhs, widget_id const& rhs) noexcept
254 {
255 return lhs.id <=> rhs;
256 }
257 };
258
261 std::optional<value_type> _next_value;
262 callback<void(value_type)> _value_cbt;
263 };
264
266
267 callback<void(value_type)> _value_cbt;
268 callback<void(options_type)> _options_cbt;
269};
270
271template<typename Value, typename Options>
272default_selection_delegate(Value&&, Options&&) -> default_selection_delegate<observer_decay_t<Value>>;
273
274}} // namespace hi::v1
Defines radio_widget.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition callback.hpp:77
Definition widget_intf.hpp:24
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
callback< void(value_type)> subscribe(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback to this observer.
Definition observer_intf.hpp:456
A radio delegate controls the state of a radio button widget.
Definition radio_delegate.hpp:16
A delegate that controls the state of a selection_widget.
Definition selection_delegate.hpp:33
callback< void()> subscribe_on_value(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback for notifying the widget of a change in the value.
Definition selection_delegate.hpp:84
virtual size_t size(widget_intf const &sender) const noexcept
The number of options in the pull-down menu.
Definition selection_delegate.hpp:52
virtual std::unique_ptr< widget > make_option_widget(widget_intf const &sender, widget_intf const &parent, size_t index) noexcept=0
Create a new widget that represents the button in the selection menu.
virtual std::optional< widget_id > keyboard_focus_id(widget_intf const &sender) const noexcept
The id of the widget that will need to get keyboard focus when the pull-down menu is opened.
Definition selection_delegate.hpp:45
callback< void()> subscribe_on_options(Func &&func, callback_flags flags=callback_flags::synchronous) noexcept
Subscribe a callback for notifying the widget of a change in the options.
Definition selection_delegate.hpp:92
virtual std::optional< label > selected_label(widget_intf const &sender) const noexcept
Get the label of the selected option.
Definition selection_delegate.hpp:76
A delegate that control the state of a selection_widget.
Definition selection_delegate.hpp:108
size_t size(widget_intf const &sender) const noexcept override
The number of options in the pull-down menu.
Definition selection_delegate.hpp:134
std::optional< widget_id > keyboard_focus_id(widget_intf const &sender) const noexcept override
The id of the widget that will need to get keyboard focus when the pull-down menu is opened.
Definition selection_delegate.hpp:149
default_selection_delegate(Value &&value, Options &&options) noexcept
Construct a default selection delegate.
Definition selection_delegate.hpp:123
std::unique_ptr< widget > make_option_widget(widget_intf const &sender, widget_intf const &parent, size_t index) noexcept override
Create a new widget that represents the button in the selection menu.
Definition selection_delegate.hpp:161
std::optional< label > selected_label(widget_intf const &sender) const noexcept override
Get the label of the selected option.
Definition selection_delegate.hpp:139
T addressof(T... args)
T begin(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T front(T... args)
T lower_bound(T... args)
T move(T... args)