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.padding.top() += r.margins.top();
99 r.margins.top() = 0;
100
101 return r;
102 }
103
104 void set_layout(widget_layout const& context) noexcept override
105 {
106 // Clip directly around the toolbar, so that tab buttons looks proper.
107 if (compare_store(layout, context)) {
108 auto shape = context.shape;
109 shape.rectangle = aarectanglei{shape.x(), shape.y(), shape.width(), shape.height() + _child_height_adjustment};
110 _children.set_layout(shape, theme<prefix>.cap_height(this));
111 }
112
113 hilet overhang = context.redraw_overhang;
114
115 for (hilet& child : _children) {
116 hilet child_clipping_rectangle =
117 aarectanglei{child.shape.x() - overhang, 0, child.shape.width() + overhang * 2, context.height() + overhang * 2};
118
119 child.value->set_layout(context.transform(child.shape, 1.0f, child_clipping_rectangle));
120 }
121 }
122
123 void draw(widget_draw_context& context) noexcept override
124 {
126 if (overlaps(context, layout)) {
127 context.draw_box(layout, layout.rectangle(), theme<prefix>.background_color(this));
128
129 if (tab_button_has_focus()) {
130 // Draw the line at a higher elevation, so that the tab buttons can draw above or below the focus
131 // line depending if that specific button is in focus or not.
132 hilet focus_rectangle =
133 aarectanglei{0, 0, layout.rectangle().width(), theme<prefix>.border_width(this)};
134 context.draw_box(
135 layout,
136 translate3{0.0f, 0.0f, 1.5f} * narrow_cast<aarectangle>(focus_rectangle),
137 theme<prefix>.background_color(this));
138 }
139 }
140
141 for (hilet& child : _children) {
142 hi_assert_not_null(child.value);
143 child.value->draw(context);
144 }
145 }
146 }
147
148 hitbox hitbox_test(point2i position) const noexcept override
149 {
150 hi_axiom(loop::main().on_thread());
151
152 // By default the toolbar is used for dragging the window.
153 if (*mode >= widget_mode::partial) {
154 auto r = layout.contains(position) ? hitbox{id, layout.elevation, hitbox_type::move_area} : hitbox{};
155
156 for (hilet& child : _children) {
157 hi_assert_not_null(child.value);
158 r = child.value->hitbox_test_from_parent(position, r);
159 }
160
161 return r;
162 } else {
163 return {};
164 }
165 }
167private:
168 mutable row_layout<std::unique_ptr<widget>> _children;
169 mutable int _child_height_adjustment = 0;
170 size_t _spacer_index = 0;
171
172 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept;
173
176 widget& add_widget(horizontal_alignment alignment, std::unique_ptr<widget> widget) noexcept
177 {
178 auto& ref = *widget;
179 switch (alignment) {
180 using enum horizontal_alignment;
181 case left:
182 _children.insert(_children.cbegin() + _spacer_index, std::move(widget));
183 ++_spacer_index;
184 break;
185 case right:
186 _children.insert(_children.cbegin() + _spacer_index + 1, std::move(widget));
187 break;
188 default:
190 }
191
192 return ref;
193 }
194
199 [[nodiscard]] bool tab_button_has_focus() const noexcept
200 {
201 for (hilet& cell : _children) {
202 hilet child = cell.value.get();
203 hi_assert_not_null(child);
204 if (child->is_tab_button()) {
205 if (*child->focus and *child->state != hi::widget_state::off) {
206 return true;
207 }
208 }
209 }
210
211 return false;
212 }
213};
214
215}} // 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:96
@ 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
constexpr bool contains(point3i mouse_position) const noexcept
Check if the mouse position is inside the widget.
Definition widget_layout.hpp:126
float elevation
The elevation of the widget above the window.
Definition widget_layout.hpp:72
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)