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
8
9#pragma once
10
11#include "widget.hpp"
12#include "../layout/layout.hpp"
13#include "../geometry/geometry.hpp"
14#include "../macros.hpp"
15#include <memory>
16#include <ranges>
17#include <coroutine>
18
19hi_export_module(hikogui.widgets.toolbar_widget);
20
21hi_export namespace hi { inline namespace v1 {
22
38class toolbar_widget : public widget {
39public:
40 using super = widget;
41
46 toolbar_widget() noexcept : super()
47 {
48 hi_axiom(loop::main().on_thread());
49
50 auto spacer = std::make_unique<spacer_widget>();
51 spacer->set_parent(this);
52
53 _children.push_back(std::move(spacer));
54 }
55
71 template<typename Widget, horizontal_alignment Alignment = horizontal_alignment::left, typename... Args>
72 Widget& emplace(Args&&...args)
73 {
74 auto widget = std::make_unique<Widget>(std::forward<Args>(args)...);
75 auto &ref = *widget;
76 insert(Alignment, std::move(widget));
77 return ref;
78 }
79
81 [[nodiscard]] generator<widget_intf &> children(bool include_invisible) noexcept override
82 {
83 for (auto const& child : _children) {
84 co_yield *child.value;
85 }
86 }
87
88 [[nodiscard]] box_constraints update_constraints() noexcept override
89 {
90 _layout = {};
91
92 for (auto& child : _children) {
93 child.set_constraints(child.value->update_constraints());
94 }
95
96 auto r = _children.constraints(os_settings::left_to_right());
97 _child_height_adjustment = -r.margins.top();
98
99 r.minimum.height() += r.margins.top();
100 r.preferred.height() += r.margins.top();
101 r.maximum.height() += r.margins.top();
102 r.margins.top() = 0;
103
104 return r;
105 }
106 void set_layout(widget_layout const& context) noexcept override
107 {
108 // Clip directly around the toolbar, so that tab buttons looks proper.
109 if (compare_store(_layout, context)) {
110 auto shape = context.shape;
111 shape.rectangle = aarectangle{shape.x(), shape.y(), shape.width(), shape.height() + _child_height_adjustment};
112 _children.set_layout(shape, theme().baseline_adjustment());
113 }
114
115 auto const overhang = context.redraw_overhang;
116
117 for (auto const& child : _children) {
118 auto const child_clipping_rectangle =
119 aarectangle{child.shape.x() - overhang, 0, child.shape.width() + overhang * 2, context.height() + overhang * 2};
120
121 child.value->set_layout(context.transform(child.shape, transform_command::menu_item, child_clipping_rectangle));
122 }
123 }
124 void draw(draw_context const& context) noexcept override
125 {
126 if (mode() > widget_mode::invisible) {
127 if (overlaps(context, layout())) {
128 context.draw_box(layout(), layout().rectangle(), theme().fill_color(_layout.layer));
129
130 if (tab_button_has_focus()) {
131 // Draw the line at a higher elevation, so that the tab buttons can draw above or below the focus
132 // line depending if that specific button is in focus or not.
133 auto const focus_rectangle = aarectangle{0.0f, 0.0f, layout().rectangle().width(), theme().border_width()};
134 context.draw_box(layout(), translate3{0.0f, 0.0f, 1.5f} * focus_rectangle, focus_color());
135 }
136 }
137
138 for (auto const& child : _children) {
139 hi_assert_not_null(child.value);
140 child.value->draw(context);
141 }
142 }
143 }
144 hitbox hitbox_test(point2 position) const noexcept override
145 {
146 hi_axiom(loop::main().on_thread());
147
148 // By default the toolbar is used for dragging the window.
149 if (mode() >= widget_mode::partial) {
150 auto r = layout().contains(position) ? hitbox{id, _layout.elevation, hitbox_type::move_area} : hitbox{};
151
152 for (auto const& child : _children) {
153 hi_assert_not_null(child.value);
154 r = child.value->hitbox_test_from_parent(position, r);
155 }
156
157 return r;
158 } else {
159 return {};
160 }
161 }
162 [[nodiscard]] color focus_color() const noexcept override
163 {
164 if (mode() >= widget_mode::partial) {
165 return theme().accent_color();
166 } else {
167 return theme().border_color(_layout.layer - 1);
168 }
169 }
171private:
172 mutable row_layout<std::unique_ptr<widget>> _children;
173 mutable float _child_height_adjustment = 0.0f;
174 size_t _spacer_index = 0;
175
176 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept;
177
180 widget& insert(horizontal_alignment alignment, std::unique_ptr<widget> widget) noexcept
181 {
182 auto& ref = *widget;
183 switch (alignment) {
184 using enum horizontal_alignment;
185 case left:
186 widget->set_parent(this);
187 _children.insert(_children.cbegin() + _spacer_index, std::move(widget));
188 ++_spacer_index;
189 break;
190 case right:
191 widget->set_parent(this);
192 _children.insert(_children.cbegin() + _spacer_index + 1, std::move(widget));
193 break;
194 default:
195 hi_no_default();
196 }
197
198 return ref;
199 }
200
205 bool tab_button_has_focus() const noexcept
206 {
207 for (auto const& cell : _children) {
208 if (auto const *const c = dynamic_cast<toolbar_tab_button_widget *>(cell.value.get())) {
209 if (c->focus() and c->value() == widget_value::on) {
210 return true;
211 }
212 }
213 }
214
215 return false;
216 }
217};
218
219}} // namespace hi::v1
Defines widget.
horizontal_alignment
Horizontal alignment.
Definition alignment.hpp:102
@ right
Align the text to the right side.
Definition alignment.hpp:136
@ left
Align the text to the left side.
Definition alignment.hpp:118
@ rectangle
The gui_event has rectangle data.
Definition gui_event_variant.hpp:44
@ partial
A widget is partially enabled.
Definition widget_state.hpp:73
@ invisible
The widget is invisible.
Definition widget_state.hpp:41
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
@ alignment
A alignment was changed.
Definition style_modify_mask.hpp:43
@ color
A color value was modified.
Definition style_modify_mask.hpp:27
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition misc.hpp:32
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
@ menu_item
The child widget increments to the next elevation but layer stays the same.
Definition widget_layout.hpp:34
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:31
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:241
constexpr bool contains(point3 mouse_position) const noexcept
Check if the mouse position is inside the widget.
Definition widget_layout.hpp:179
A toolbar widget is located at the top of a window and lays out its children horizontally.
Definition toolbar_widget.hpp:38
Widget & emplace(Args &&...args)
Add a widget directly to this toolbar-widget.
Definition toolbar_widget.hpp:72
toolbar_widget() noexcept
Constructs an empty row/column widget.
Definition toolbar_widget.hpp:46
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
T forward(T... args)
T move(T... args)
T ref(T... args)