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
8
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 {
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
68 [[nodiscard]] virtual std::unique_ptr<widget> make_option_widget(widget_intf const& sender, size_t index) noexcept = 0;
69
75 [[nodiscard]] virtual std::optional<label> selected_label(widget_intf const& sender) const noexcept
76 {
77 return std::nullopt;
78 }
79
82 template<forward_of<void()> Func>
83 [[nodiscard]] callback<void()> subscribe_on_value(Func&& func, callback_flags flags = callback_flags::synchronous) noexcept
84 {
85 return _value_notifier.subscribe(std::forward<Func>(func), flags);
86 }
87
90 template<forward_of<void()> Func>
91 [[nodiscard]] callback<void()> subscribe_on_options(Func&& func, callback_flags flags = callback_flags::synchronous) noexcept
92 {
93 return _options_notifier.subscribe(std::forward<Func>(func), flags);
94 }
95
96protected:
97 notifier<void()> _value_notifier;
98 notifier<void()> _options_notifier;
99};
100
106template<typename T>
108public:
109 using value_type = T;
110 using option_type = std::pair<value_type, label>;
111 using options_type = std::vector<option_type>;
112
115
121 template<forward_of<observer<value_type>> Value, forward_of<observer<options_type>> Options>
122 default_selection_delegate(Value&& value, Options&& options) noexcept :
123 value(std::forward<Value>(value)), options(std::forward<Options>(options))
124 {
125 _option_delegate = std::make_shared<option_delegate_type>(*this);
126
127 // clang-format off
128 _value_cbt = this->value.subscribe([&](auto...){ this->_value_notifier(); });
129 _options_cbt = this->options.subscribe([&](auto...){ this->_options_notifier(); });
130 // clang-format on
131 }
132
133 [[nodiscard]] size_t size(widget_intf const& sender) const noexcept override
134 {
135 return options->size();
136 }
137
138 [[nodiscard]] std::optional<label> selected_label(widget_intf const& sender) const noexcept override
139 {
140 for (auto&& option : *options) {
141 if (option.first == *value) {
142 return option.second;
143 }
144 }
145 return std::nullopt;
146 }
147
148 [[nodiscard]] std::optional<widget_id> keyboard_focus_id(widget_intf const& sender) const noexcept override
149 {
150 return _option_delegate->keyboard_focus_id();
151 }
152
159 [[nodiscard]] std::unique_ptr<widget> make_option_widget(widget_intf const& sender, size_t index) noexcept override
160 {
161 auto& [option_value, option_label] = options->at(index);
162 return _option_delegate->make_option_widget(sender, option_value, option_label, _option_delegate);
163 }
164
165private:
166 class option_delegate_type : public radio_delegate {
167 public:
168 option_delegate_type(default_selection_delegate &parent) noexcept : _parent(&parent)
169 {
170 _value_cbt = _parent->value.subscribe([&](auto...) {
171 this->_notifier();
172 });
173 }
174
175 void init(widget_intf const& sender) noexcept override
176 {
177 hi_assert(_next_value, "The value was not set of this option widget.");
178
179 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
180 hi_assert(it == _senders.end() or it->id != sender.id, "button was already registered with selection-delegate.");
181
182 _senders.emplace(it, sender.id, *_next_value);
183 _next_value = std::nullopt;
184 }
185
186 void deinit(widget_intf const& sender) noexcept override
187 {
188 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
189 if (it != _senders.end() and it->id == sender.id) {
190 _senders.erase(it);
191 }
192 }
193
194 [[nodiscard]] std::optional<widget_id> keyboard_focus_id() const noexcept
195 {
196 if (_senders.empty()) {
197 return std::nullopt;
198 }
199
200 for (auto const& sender : _senders) {
201 if (sender.value == *_parent->value) {
202 return sender.id;
203 }
204 }
205 return _senders.front().id;
206 }
207
208 [[nodiscard]] std::unique_ptr<widget>
209 make_option_widget(widget_intf const& sender, value_type const& value, label const& label, std::shared_ptr<option_delegate_type> shared_this) noexcept
210 {
211 using button_widget = radio_menu_button_widget;
212 using button_attributes = radio_menu_button_widget::attributes_type;
213
214 // Prepare the value for the next widget, so that the widget immediately can retrieve its value.
215 _next_value = value;
216 return std::make_unique<button_widget>(button_attributes{label}, std::move(shared_this));
217 }
218
219 [[nodiscard]] widget_value state(widget_intf const& sender) const noexcept override
220 {
221 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
222
223 if (it != _senders.end() and it->id == sender.id) {
224 return *_parent->value == it->value ? widget_value::on : widget_value::off;
225
226 } else {
227 // button-button was not yet registered.
228 return widget_value::off;
229 }
230 }
231
232 void activate(widget_intf const& sender) noexcept override
233 {
234 auto const it = std::lower_bound(_senders.begin(), _senders.end(), sender.id);
235
236 if (it != _senders.end() and it->id == sender.id) {
237 _parent->value = it->value;
238 }
239 }
240
241 private:
242 struct sender_info_type {
243 widget_id id = {};
244 value_type value;
245
246 [[nodiscard]] friend bool operator==(sender_info_type const& lhs, widget_id const& rhs) noexcept
247 {
248 return lhs.id == rhs;
249 }
250
251 [[nodiscard]] friend auto operator<=>(sender_info_type const& lhs, widget_id const& rhs) noexcept
252 {
253 return lhs.id <=> rhs;
254 }
255 };
256
258 std::vector<sender_info_type> _senders;
259 std::optional<value_type> _next_value;
260 callback<void(value_type)> _value_cbt;
261 };
262
263 std::shared_ptr<option_delegate_type> _option_delegate;
264
265 callback<void(value_type)> _value_cbt;
266 callback<void(options_type)> _options_cbt;
267};
268
269template<typename Value, typename Options>
271
272}} // namespace hi::v1
Defines radio_widget.
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
Definition callback.hpp:77
Definition widget_intf.hpp:25
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:458
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:83
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::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:91
virtual std::optional< label > selected_label(widget_intf const &sender) const noexcept
Get the label of the selected option.
Definition selection_delegate.hpp:75
virtual std::unique_ptr< widget > make_option_widget(widget_intf const &sender, size_t index) noexcept=0
Create a new widget that represents the button in the selection menu.
A delegate that control the state of a selection_widget.
Definition selection_delegate.hpp:107
size_t size(widget_intf const &sender) const noexcept override
The number of options in the pull-down menu.
Definition selection_delegate.hpp:133
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:148
std::unique_ptr< widget > make_option_widget(widget_intf const &sender, size_t index) noexcept override
Create a new widget that represents the button in the selection menu.
Definition selection_delegate.hpp:159
default_selection_delegate(Value &&value, Options &&options) noexcept
Construct a default selection delegate.
Definition selection_delegate.hpp:122
std::optional< label > selected_label(widget_intf const &sender) const noexcept override
Get the label of the selected option.
Definition selection_delegate.hpp:138
A graphical control element that allows the user to choose only one of a predefined set of mutually e...
Definition selection_widget.hpp:48
T begin(T... args)
T emplace(T... args)
T end(T... args)
T forward(T... args)
T lower_bound(T... args)
T make_shared(T... args)
T move(T... args)