HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
icon_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-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 "../GUI/module.hpp"
12#include "../GFX/module.hpp"
13#include "../geometry/module.hpp"
14#include "../label.hpp"
15#include <memory>
16#include <string>
17#include <array>
18#include <optional>
19#include <future>
20
21namespace hi { inline namespace v1 {
22
23template<typename Context>
24concept icon_widget_attribute = forward_of<Context, observer<hi::icon>, observer<hi::alignment>>;
25
32template<fixed_string Name = "">
33class icon_widget final : public widget {
34public:
35 using super = widget;
36 constexpr static auto prefix = Name / "icon";
37
40 observer<icon> icon = hi::icon{};
41
44 observer<alignment> alignment = hi::alignment::middle_center();
45
46 icon_widget(widget *parent, icon_widget_attribute auto&&...attributes) noexcept : icon_widget(parent)
47 {
48 set_attributes(hi_forward(attributes)...);
49 }
50
51 void set_attributes() noexcept {}
52 void set_attributes(icon_widget_attribute auto&& first, icon_widget_attribute auto&&...rest) noexcept
53 {
54 if constexpr (forward_of<decltype(first), observer<hi::icon>>) {
55 icon = hi_forward(first);
56 } else if constexpr (forward_of<decltype(first), observer<hi::alignment>>) {
57 alignment = hi_forward(first);
58 } else {
60 }
61 set_attributes(hi_forward(rest)...);
62 }
63
65 [[nodiscard]] box_constraints update_constraints() noexcept override
66 {
67 if (_icon_has_modified.exchange(false)) {
68 _icon_type = icon_type::no;
69 _icon_size = {};
70 _glyph = {};
71 _pixmap_backing = {};
72
73 if (hilet pixmap = std::get_if<hi::pixmap<sfloat_rgba16>>(&icon.read())) {
74 _icon_type = icon_type::pixmap;
75 _icon_size = extent2{narrow_cast<float>(pixmap->width()), narrow_cast<float>(pixmap->height())};
76
77 if (_pixmap_backing = paged_image{surface, *pixmap}; not _pixmap_backing) {
78 // Could not get an image, retry.
79 _icon_has_modified = true;
80 ++global_counter<"icon_widget:no-backing-image:constrain">;
82 }
83
84 } else if (hilet g1 = std::get_if<font_book::font_glyph_type>(&icon.read())) {
85 _glyph = *g1;
86 _icon_type = icon_type::glyph;
87 _icon_size = _glyph.get_bounding_rectangle().size() * theme<prefix>.size(this);
88
89 } else if (hilet g2 = std::get_if<elusive_icon>(&icon.read())) {
90 _glyph = find_glyph(*g2);
91 _icon_type = icon_type::glyph;
92 _icon_size = _glyph.get_bounding_rectangle().size() * theme<prefix>.size(this);
93
94 } else if (hilet g3 = std::get_if<hikogui_icon>(&icon.read())) {
95 _glyph = find_glyph(*g3);
96 _icon_type = icon_type::glyph;
97 _icon_size = _glyph.get_bounding_rectangle().size() * theme<prefix>.size(this);
98 }
99 }
100
101 hilet resolved_alignment = resolve(*alignment, os_settings::left_to_right());
102 hilet icon_constraints = box_constraints{
103 extent2i{0, 0},
104 _icon_size,
105 _icon_size,
106 resolved_alignment,
107 theme<prefix>.margin(this)};
108 return icon_constraints.constrain(*minimum, *maximum);
109 }
110
111 void set_layout(widget_layout const& context) noexcept override
112 {
113 if (compare_store(layout, context)) {
114 if (_icon_type == icon_type::no or not _icon_size) {
115 _icon_rectangle = {};
116 } else {
117 hilet width = std::clamp(context.shape.width(), minimum->width(), maximum->width());
118 hilet height = std::clamp(context.shape.height(), minimum->height(), maximum->height());
119
120 hilet icon_scale = scale2::uniform(_icon_size, extent2{narrow_cast<float>(width), narrow_cast<float>(height)});
121 hilet new_icon_size = narrow_cast<extent2i>(icon_scale * _icon_size);
122 hilet resolved_alignment = resolve(*alignment, os_settings::left_to_right());
123 _icon_rectangle = align(context.rectangle(), new_icon_size, resolved_alignment);
124 }
125 }
126 }
127
128 void draw(widget_draw_context const& context) noexcept override
129 {
130 if (*mode > widget_mode::invisible and overlaps(context, layout)) {
131 switch (_icon_type) {
132 case icon_type::no:
133 break;
134
135 case icon_type::pixmap:
136 if (not context.draw_image(layout, _icon_rectangle, _pixmap_backing)) {
137 // Continue redrawing until the image is loaded.
139 }
140 break;
141
142 case icon_type::glyph:
143 {
144 context.draw_glyph(layout, _icon_rectangle, *_glyph.font, _glyph.glyph, theme<prefix>.fill_color(this));
145 }
146 break;
147
148 default:
150 }
151 }
152 }
154private:
155 enum class icon_type { no, glyph, pixmap };
156
157 icon_type _icon_type;
158 font_book::font_glyph_type _glyph;
159 paged_image _pixmap_backing;
160 decltype(icon)::callback_token _icon_cbt;
161 std::atomic<bool> _icon_has_modified = true;
162
163 extent2 _icon_size;
164 aarectanglei _icon_rectangle;
165
166 icon_widget(widget *parent) noexcept : super(parent)
167 {
168 _icon_cbt = icon.subscribe([this](auto...) {
169 _icon_has_modified = true;
170 ++global_counter<"icon_widget:icon:constrain">;
171 process_event({gui_event_type::window_reconstrain});
172 });
173 }
174};
175
176}} // namespace hi::v1
Functionality for labels, text and icons.
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:323
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:279
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
geo::extent< int, 2 > extent2i
A 2D extent.
Definition extent.hpp:512
geo::extent< float, 2 > extent2
A 2D extent.
Definition extent.hpp:502
@ window_reconstrain
Request that widget get constraint on the next frame.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:13
auto find_glyph(font const &font, grapheme grapheme) noexcept
Find a glyph using the given code-point.
Definition font_book.hpp:234
geometry/margins.hpp
Definition cache.hpp:11
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:212
Definition widget.hpp:26
observer< extent2i > minimum
The minimum size this widget is allowed to be.
Definition widget.hpp:89
virtual void request_redraw() const noexcept
Request the widget to be redrawn on the next frame.
Definition widget.hpp:270
gfx_surface * surface
The surface this widget is drawn on.
Definition widget.hpp:48
observer< extent2i > maximum
The maximum size this widget is allowed to be.
Definition widget.hpp:93
widget * parent
Pointer to the parent widget.
Definition widget.hpp:40
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:53
A 2D pixel-based image.
Definition pixmap.hpp:35
An simple GUI widget that displays an icon.
Definition icon_widget.hpp:33
observer< alignment > alignment
Alignment of the icon inside the widget.
Definition icon_widget.hpp:44
observer< icon > icon
The icon to be displayed.
Definition icon_widget.hpp:40
Definition icon_widget.hpp:24
T align(T... args)
T exchange(T... args)