HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
label_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2020-2022.
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 "widget.hpp"
12#include "text_widget.hpp"
13#include "icon_widget.hpp"
14#include "../geometry/module.hpp"
15#include "../layout/module.hpp"
16#include "../l10n/l10n.hpp"
17#include "../macros.hpp"
18#include <memory>
19#include <string>
20#include <array>
21#include <optional>
22#include <future>
23
24namespace hi { inline namespace v1 {
25
26template<typename Context>
29
43class label_widget final : public widget {
44public:
45 using super = widget;
46
50
68 observer<alignment> alignment = hi::alignment::top_flush();
69
72 observer<semantic_text_style> text_style = semantic_text_style::label;
73
82 {
83 set_attributes(hi_forward(attributes)...);
84 }
85
87 [[nodiscard]] generator<widget_intf &> children(bool include_invisible) noexcept override
88 {
89 co_yield *_icon_widget;
90 co_yield *_text_widget;
91 }
92
93 [[nodiscard]] box_constraints update_constraints() noexcept override
94 {
95 _layout = {};
96
97 // Resolve as if in left-to-right mode, the grid will flip itself.
98 hilet resolved_alignment = resolve(*alignment, true);
99
100 _grid.clear();
101 if (to_bool(label->icon) and to_bool(label->text)) {
102 // Both of the icon and text are set, so configure the grid to hold both.
103 if (resolved_alignment == horizontal_alignment::left) {
104 // icon text
105 _grid.add_cell(0, 0, _icon_widget.get());
106 _grid.add_cell(1, 0, _text_widget.get(), true);
107 } else if (resolved_alignment == horizontal_alignment::right) {
108 // text icon
109 _grid.add_cell(0, 0, _text_widget.get(), true);
110 _grid.add_cell(1, 0, _icon_widget.get());
111 } else if (resolved_alignment == vertical_alignment::top) {
112 // icon
113 // text
114 _grid.add_cell(0, 0, _icon_widget.get());
115 _grid.add_cell(0, 1, _text_widget.get(), true);
116 } else if (resolved_alignment == vertical_alignment::bottom) {
117 // text
118 // icon
119 _grid.add_cell(0, 0, _text_widget.get(), true);
120 _grid.add_cell(0, 1, _icon_widget.get());
121 } else {
122 hi_no_default("alignment is not allowed to be middle-center.");
123 }
124 } else if (to_bool(label->icon)) {
125 // Only the icon-widget is used.
126 _grid.add_cell(0, 0, _icon_widget.get());
127 } else if (to_bool(label->text)) {
128 // Only the text-widget is used.
129 _grid.add_cell(0, 0, _text_widget.get());
130 }
131
132 hilet icon_size =
133 (resolved_alignment == horizontal_alignment::center or resolved_alignment == horizontal_alignment::justified) ?
134 theme().large_icon_size() :
135 theme().text_style(*text_style)->size * theme().scale;
136
137 _icon_widget->minimum = extent2{icon_size, icon_size};
138 _icon_widget->maximum = extent2{icon_size, icon_size};
139
140 for (auto& cell : _grid) {
141 cell.set_constraints(cell.value->update_constraints());
142 }
143
144 return _grid.constraints(os_settings::left_to_right());
145 }
146 void set_layout(widget_layout const& context) noexcept override
147 {
148 if (compare_store(_layout, context)) {
149 _grid.set_layout(context.shape, theme().baseline_adjustment());
150 }
151
152 for (hilet& cell : _grid) {
153 cell.value->set_layout(context.transform(cell.shape, 0.0f));
154 }
155 }
156 void draw(draw_context const& context) noexcept override
157 {
158 if (*mode > widget_mode::invisible and overlaps(context, layout())) {
159 for (hilet& cell : _grid) {
160 cell.value->draw(context);
161 }
162 }
163 }
164 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
165 {
166 hi_axiom(loop::main().on_thread());
167
169 return _text_widget->hitbox_test_from_parent(position);
170 } else {
171 return {};
172 }
173 }
175private:
176 float _icon_size;
177 float _inner_margin;
178
179 decltype(label)::callback_token _label_cbt;
180 decltype(text_style)::callback_token _text_style_cbt;
181 decltype(alignment)::callback_token _alignment_cbt;
182
183 std::unique_ptr<icon_widget> _icon_widget;
184 std::unique_ptr<text_widget> _text_widget;
186
187 void set_attributes() noexcept {}
188 void set_attributes(label_widget_attribute auto&& first, label_widget_attribute auto&&...rest) noexcept
189 {
190 if constexpr (forward_of<decltype(first), observer<hi::label>>) {
191 label = hi_forward(first);
192 } else if constexpr (forward_of<decltype(first), observer<hi::alignment>>) {
193 alignment = hi_forward(first);
194 } else if constexpr (forward_of<decltype(first), observer<hi::semantic_text_style>>) {
195 text_style = hi_forward(first);
196 } else {
197 hi_static_no_default();
198 }
199
200 set_attributes(hi_forward(rest)...);
201 }
202
204 {
206
207 _icon_widget = std::make_unique<icon_widget>(this, label.get<"icon">());
208 _text_widget = std::make_unique<text_widget>(this, label.get<"text">());
209 _text_widget->alignment = alignment;
210 _text_widget->text_style = text_style;
211 _text_widget->mode = mode;
212
213 _alignment_cbt = alignment.subscribe([this](auto...) {
215 _icon_widget->alignment = hi::alignment::middle_center();
216 } else {
217 _icon_widget->alignment = *alignment;
218 }
219 });
220 (*_alignment_cbt)(*alignment);
221
222 _text_style_cbt = text_style.subscribe([this](auto...) {
223 switch (*text_style) {
224 case semantic_text_style::label:
225 _icon_widget->color = color::foreground();
226 break;
227 case semantic_text_style::small_label:
228 _icon_widget->color = color::foreground();
229 break;
230 case semantic_text_style::warning:
231 _icon_widget->color = color::orange();
232 break;
233 case semantic_text_style::error:
234 _icon_widget->color = color::red();
235 break;
236 case semantic_text_style::help:
237 _icon_widget->color = color::indigo();
238 break;
239 case semantic_text_style::placeholder:
240 _icon_widget->color = color::gray();
241 break;
242 case semantic_text_style::link:
243 _icon_widget->color = color::blue();
244 break;
245 default:
246 _icon_widget->color = color::foreground();
247 }
248 });
249 }
250};
251
252}} // namespace hi::v1
Defines widget.
Defines icon_widget.
Defines text_widget.
@ bottom
Align to the bottom.
@ top
Align to the top.
@ right
Align the text to the right side.
@ left
Align the text to the left side.
@ justified
Stretch the text to be flush to both sides.
@ center
Align the text in the center.
@ invisible
The widget is invisible.
@ select
The widget is selectable.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:56
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Horizontal/Vertical alignment combination.
Definition alignment.hpp:242
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:28
2D constraints.
Definition box_constraints.hpp:25
The GUI widget displays and lays out text together with an icon.
Definition label_widget.hpp:43
observer< label > label
The label to display.
Definition label_widget.hpp:49
observer< alignment > alignment
How the label and icon are aligned.
Definition label_widget.hpp:68
observer< semantic_text_style > text_style
The text style to display the label's text in and color of the label's (non-color) icon.
Definition label_widget.hpp:72
label_widget(widget *parent, label_widget_attribute auto &&...attributes) noexcept
Construct a label widget.
Definition label_widget.hpp:81
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
widget_layout const & layout() const noexcept override
Get the current layout for this widget.
Definition widget.hpp:169
widget(widget *parent) noexcept
Definition widget.hpp:87
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:42
True if T is a forwarded type of Forward.
Definition concepts.hpp:131
Definition label_widget.hpp:27