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
8
9#pragma once
10
11#include "widget.hpp"
12#include "text_widget.hpp"
13#include "icon_widget.hpp"
14#include "../geometry/geometry.hpp"
15#include "../layout/layout.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#include <coroutine>
24
25hi_export_module(hikogui.widgets.label_widget);
26
27hi_export namespace hi { inline namespace v1 {
28
29template<typename Context>
32
46class label_widget : public widget {
47public:
48 using super = widget;
49
53
71 observer<alignment> alignment = hi::alignment::top_flush();
72
75 observer<hi::phrasing> phrasing = hi::phrasing::regular;
76
84 template<label_widget_attribute... Attributes>
85 label_widget(Attributes&&...attributes) noexcept : label_widget()
86 {
87 set_attributes(std::forward<Attributes>(attributes)...);
88 }
89
91 [[nodiscard]] generator<widget_intf &> children(bool include_invisible) noexcept override
92 {
93 co_yield *_icon_widget;
94 co_yield *_text_widget;
95 }
96
97 [[nodiscard]] box_constraints update_constraints() noexcept override
98 {
99 _layout = {};
100
101 // Resolve as if in left-to-right mode, the grid will flip itself.
102 auto const resolved_alignment = resolve(*alignment, true);
103
104 _grid.clear();
105 if (to_bool(label->icon) and to_bool(label->text)) {
106 // Both of the icon and text are set, so configure the grid to hold both.
107 if (resolved_alignment == horizontal_alignment::left) {
108 // icon text
109 _grid.add_cell(0, 0, _icon_widget.get());
110 _grid.add_cell(1, 0, _text_widget.get(), true);
111 } else if (resolved_alignment == horizontal_alignment::right) {
112 // text icon
113 _grid.add_cell(0, 0, _text_widget.get(), true);
114 _grid.add_cell(1, 0, _icon_widget.get());
115 } else if (resolved_alignment == vertical_alignment::top) {
116 // icon
117 // text
118 _grid.add_cell(0, 0, _icon_widget.get());
119 _grid.add_cell(0, 1, _text_widget.get(), true);
120 } else if (resolved_alignment == vertical_alignment::bottom) {
121 // text
122 // icon
123 _grid.add_cell(0, 0, _text_widget.get(), true);
124 _grid.add_cell(0, 1, _icon_widget.get());
125 } else {
126 hi_no_default("alignment is not allowed to be middle-center.");
127 }
128 } else if (to_bool(label->icon)) {
129 // Only the icon-widget is used.
130 _grid.add_cell(0, 0, _icon_widget.get());
131 } else if (to_bool(label->text)) {
132 // Only the text-widget is used.
133 _grid.add_cell(0, 0, _text_widget.get());
134 }
135
136 auto const label_style = theme().text_style_set()[{phrasing::regular}];
137 auto const icon_size =
138 (resolved_alignment == horizontal_alignment::center or resolved_alignment == horizontal_alignment::justified) ?
139 theme().large_icon_size() :
140 (label_style.size() * theme().pixel_density).in(unit::pixels_per_em);
141
142 _icon_widget->minimum = extent2{icon_size, icon_size};
143 _icon_widget->maximum = extent2{icon_size, icon_size};
144
145 for (auto& cell : _grid) {
146 cell.set_constraints(cell.value->update_constraints());
147 }
148
149 return _grid.constraints(os_settings::left_to_right());
150 }
151 void set_layout(widget_layout const& context) noexcept override
152 {
153 if (compare_store(_layout, context)) {
154 _grid.set_layout(context.shape, theme().baseline_adjustment());
155 }
156
157 for (auto const& cell : _grid) {
158 cell.value->set_layout(context.transform(cell.shape, transform_command::level));
159 }
160 }
161
162 void draw(draw_context const& context) noexcept override
163 {
164 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
165 for (auto const& cell : _grid) {
166 cell.value->draw(context);
167 }
168 }
169 }
170
171 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
172 {
173 hi_axiom(loop::main().on_thread());
174
175 if (mode() > widget_mode::invisible) {
176 return _text_widget->hitbox_test_from_parent(position);
177 } else {
178 return {};
179 }
180 }
182private:
183 float _icon_size;
184 float _inner_margin;
185
186 std::unique_ptr<icon_widget> _icon_widget;
187 std::unique_ptr<text_widget> _text_widget;
188 grid_layout<widget *> _grid;
189
190 callback<void(hi::label)> _label_cbt;
191 callback<void(hi::alignment)> _alignment_cbt;
192
193 void set_attributes() noexcept {}
194
195 template<label_widget_attribute First, label_widget_attribute... Rest>
196 void set_attributes(First&& first, Rest&&...rest) noexcept
197 {
198 if constexpr (forward_of<First, observer<hi::label>>) {
199 label = std::forward<First>(first);
200 } else if constexpr (forward_of<First, observer<hi::alignment>>) {
202 } else if constexpr (forward_of<First, observer<hi::phrasing>>) {
204 } else {
205 hi_static_no_default();
206 }
207
208 set_attributes(std::forward<Rest>(rest)...);
209 }
210
211 label_widget() noexcept : super()
212 {
213 set_mode(widget_mode::select);
214
215 _icon_widget = std::make_unique<icon_widget>(label.sub<"icon">());
216 _icon_widget->set_parent(this);
217 _icon_widget->phrasing = phrasing;
218
219 _text_widget = std::make_unique<text_widget>(label.sub<"text">());
220 _text_widget->set_parent(this);
221 _text_widget->alignment = alignment;
222 _text_widget->set_mode(mode());
223
224 _alignment_cbt = alignment.subscribe([this](auto...) {
226 _icon_widget->alignment = hi::alignment::middle_center();
227 } else {
228 _icon_widget->alignment = *alignment;
229 }
230 });
231 _alignment_cbt(*alignment);
232 }
233};
234
235}} // namespace hi::v1
Defines icon_widget.
Defines widget.
Defines text_widget.
@ bottom
Align to the bottom.
Definition alignment.hpp:40
@ top
Align to the top.
Definition alignment.hpp:32
@ right
Align the text to the right side.
Definition alignment.hpp:136
@ left
Align the text to the left side.
Definition alignment.hpp:118
@ justified
Stretch the text to be flush to both sides.
Definition alignment.hpp:130
@ center
Align the text in the center.
Definition alignment.hpp:124
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
@ select
The widget is selectable.
Definition widget_state.hpp:63
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
@ pixel_density
The attributes that need to be modified when the pixel density changes.
Definition style_modify_mask.hpp:59
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
@ level
The child widget stays at the same elevation and layer.
Definition widget_layout.hpp:26
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
2D constraints.
Definition box_constraints.hpp:25
A observer pointing to the whole or part of a observed_base.
Definition observer_intf.hpp:32
The GUI widget displays and lays out text together with an icon.
Definition label_widget.hpp:46
label_widget(Attributes &&...attributes) noexcept
Construct a label widget.
Definition label_widget.hpp:85
observer< label > label
The label to display.
Definition label_widget.hpp:52
observer< hi::phrasing > phrasing
The color of the label's (non-color) icon.
Definition label_widget.hpp:75
observer< alignment > alignment
How the label and icon are aligned.
Definition label_widget.hpp:71
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:50
box_constraints update_constraints() noexcept override
Update the constraints of the widget.
Definition widget.hpp:110
True if T is a forwarded type of Forward.
Definition concepts.hpp:137
Definition label_widget.hpp:30
T forward(T... args)