HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
toolbar_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 "spacer_widget.hpp"
13#include "../GUI/module.hpp"
14#include "../layout/row_column_layout.hpp"
15#include "../geometry/module.hpp"
16#include <memory>
17#include <ranges>
18
19namespace hi { inline namespace v1 {
20
36template<fixed_string Name = "">
37class toolbar_widget final : public widget {
38public:
39 using super = widget;
40 constexpr static auto prefix = Name / "toolbar";
41
47 {
48 hi_axiom(loop::main().on_thread());
49
50 // The toolbar is a top level widget, which draws its background as the next level.
52
53 _children.push_back(std::make_unique<spacer_widget>(this));
54 }
55
71 template<typename Widget, horizontal_alignment Alignment = horizontal_alignment::left, typename... Args>
72 Widget& make_widget(Args&&...args)
73 {
74 auto widget = std::make_unique<Widget>(this, std::forward<Args>(args)...);
75 return static_cast<Widget&>(add_widget(Alignment, std::move(widget)));
76 }
77
79 [[nodiscard]] generator<widget const&> children(bool include_invisible) const noexcept override
80 {
81 for (hilet& child : _children) {
82 co_yield *child.value;
83 }
84 }
85
86 [[nodiscard]] box_constraints update_constraints() noexcept override
87 {
88 for (auto& child : _children) {
89 child.set_constraints(child.value->update_constraints());
90 }
91
92 auto r = _children.constraints(os_settings::left_to_right());
93 _child_height_adjustment = -r.margins.top();
94
95 r.minimum.height() += r.margins.top();
96 r.preferred.height() += r.margins.top();
97 r.maximum.height() += r.margins.top();
98 r.margins.top() = 0;
99
100 return r;
101 }
102
103 void set_layout(widget_layout const& context) noexcept override
104 {
105 // Clip directly around the toolbar, so that tab buttons looks proper.
106 if (compare_store(layout, context)) {
107 auto shape = context.shape;
108 shape.rectangle = aarectangle{shape.x(), shape.y(), shape.width(), shape.height() + _child_height_adjustment};
109 _children.set_layout(shape, theme<prefix>.cap_height(this));
110 }
111
112 hilet overhang = context.redraw_overhang;
113
114 for (hilet& child : _children) {
115 hilet child_clipping_rectangle =
116 aarectangle{child.shape.x() - overhang, 0.0f, child.shape.width() + overhang * 2.0f, context.height() + overhang * 2.0f};
117
118 child.value->set_layout(context.transform(child.shape, 1.0f, child_clipping_rectangle));
119 }
120 }
121
122 void draw(widget_draw_context& context) noexcept override
123 {
125 if (overlaps(context, layout)) {
126 context.draw_box(layout, layout.rectangle(), theme<prefix>.background_color(this));
127
128 if (tab_button_has_focus()) {
129 // Draw the line at a higher elevation, so that the tab buttons can draw above or below the focus
130 // line depending if that specific button is in focus or not.
131 hilet focus_rectangle =
132 aarectangle{0.0f, 0.0f, layout.rectangle().width(), theme<prefix>.border_width(this)};
133 context.draw_box(
134 layout,
135 translate3{0.0f, 0.0f, 1.5f} * narrow_cast<aarectangle>(focus_rectangle),
136 theme<prefix>.background_color(this));
137 }
138 }
139
140 for (hilet& child : _children) {
141 hi_assert_not_null(child.value);
142 child.value->draw(context);
143 }
144 }
145 }
146
147 hitbox hitbox_test(point2 position) const noexcept override
148 {
149 hi_axiom(loop::main().on_thread());
150
151 // By default the toolbar is used for dragging the window.
152 if (*mode >= widget_mode::partial) {
153 auto r = layout.contains(position) ? hitbox{id, layout.elevation, hitbox_type::move_area} : hitbox{};
154
155 for (hilet& child : _children) {
156 hi_assert_not_null(child.value);
157 r = child.value->hitbox_test_from_parent(position, r);
158 }
159
160 return r;
161 } else {
162 return {};
163 }
164 }
166private:
167 mutable row_layout<std::unique_ptr<widget>> _children;
168 mutable float _child_height_adjustment = 0;
169 size_t _spacer_index = 0;
170
171 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept;
172
175 widget& add_widget(horizontal_alignment alignment, std::unique_ptr<widget> widget) noexcept
176 {
177 auto& ref = *widget;
178 switch (alignment) {
179 using enum horizontal_alignment;
180 case left:
181 _children.insert(_children.cbegin() + _spacer_index, std::move(widget));
182 ++_spacer_index;
183 break;
184 case right:
185 _children.insert(_children.cbegin() + _spacer_index + 1, std::move(widget));
186 break;
187 default:
189 }
190
191 return ref;
192 }
193
198 [[nodiscard]] bool tab_button_has_focus() const noexcept
199 {
200 for (hilet& cell : _children) {
201 hilet child = cell.value.get();
202 hi_assert_not_null(child);
203 if (child->is_tab_button()) {
204 if (*child->focus and *child->state != hi::widget_state::off) {
205 return true;
206 }
207 }
208 }
209
210 return false;
211 }
212};
213
214}} // namespace hi::v1
Defines spacer_widget.
Defines toolbar_tab_button_widget.
#define hi_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:279
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
horizontal_alignment
Horizontal alignment.
Definition alignment.hpp:100
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition utility.hpp:189
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition utility.hpp:212
Definition widget.hpp:26
widget_id id
The numeric identifier of a widget.
Definition widget.hpp:35
widget * parent
Pointer to the parent widget.
Definition widget.hpp:40
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:49
size_t semantic_layer
The draw layer of the widget.
Definition widget.hpp:81
A toolbar widget is located at the top of a window and lays out its children horizontally.
Definition toolbar_widget.hpp:37
toolbar_widget(widget *parent) noexcept
Constructs an empty row/column widget.
Definition toolbar_widget.hpp:46
Widget & make_widget(Args &&...args)
Add a widget directly to this toolbar-widget.
Definition toolbar_widget.hpp:72
T left(T... args)
T move(T... args)
T ref(T... args)