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 "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
47 {
48 hi_axiom(loop::main().on_thread());
49 _children.push_back(std::make_unique<spacer_widget>(this));
50 }
51
67 template<typename Widget, horizontal_alignment Alignment = horizontal_alignment::left, typename... Args>
68 Widget& emplace(Args&&...args)
69 {
70 auto widget = std::make_unique<Widget>(this, std::forward<Args>(args)...);
71 return static_cast<Widget&>(insert(Alignment, std::move(widget)));
72 }
73
75 [[nodiscard]] generator<widget_intf &> children(bool include_invisible) noexcept override
76 {
77 for (auto const& child : _children) {
78 co_yield *child.value;
79 }
80 }
81
82 [[nodiscard]] box_constraints update_constraints() noexcept override
83 {
84 _layout = {};
85
86 for (auto& child : _children) {
87 child.set_constraints(child.value->update_constraints());
88 }
89
90 auto r = _children.constraints(os_settings::left_to_right());
91 _child_height_adjustment = -r.margins.top();
92
93 r.minimum.height() += r.margins.top();
94 r.preferred.height() += r.margins.top();
95 r.maximum.height() += r.margins.top();
96 r.margins.top() = 0;
97
98 return r;
99 }
100 void set_layout(widget_layout const& context) noexcept override
101 {
102 // Clip directly around the toolbar, so that tab buttons looks proper.
103 if (compare_store(_layout, context)) {
104 auto shape = context.shape;
105 shape.rectangle = aarectangle{shape.x(), shape.y(), shape.width(), shape.height() + _child_height_adjustment};
106 _children.set_layout(shape, theme().baseline_adjustment());
107 }
108
109 auto const overhang = context.redraw_overhang;
110
111 for (auto const& child : _children) {
112 auto const child_clipping_rectangle =
113 aarectangle{child.shape.x() - overhang, 0, child.shape.width() + overhang * 2, context.height() + overhang * 2};
114
115 child.value->set_layout(context.transform(child.shape, transform_command::menu_item, child_clipping_rectangle));
116 }
117 }
118 void draw(draw_context const& context) noexcept override
119 {
120 if (mode() > widget_mode::invisible) {
121 if (overlaps(context, layout())) {
122 context.draw_box(layout(), layout().rectangle(), theme().color(semantic_color::fill, _layout.layer));
123
124 if (tab_button_has_focus()) {
125 // Draw the line at a higher elevation, so that the tab buttons can draw above or below the focus
126 // line depending if that specific button is in focus or not.
127 auto const focus_rectangle = aarectangle{0.0f, 0.0f, layout().rectangle().width(), theme().border_width()};
128 context.draw_box(layout(), translate3{0.0f, 0.0f, 1.5f} * focus_rectangle, focus_color());
129 }
130 }
131
132 for (auto const& child : _children) {
133 hi_assert_not_null(child.value);
134 child.value->draw(context);
135 }
136 }
137 }
138 hitbox hitbox_test(point2 position) const noexcept override
139 {
140 hi_axiom(loop::main().on_thread());
141
142 // By default the toolbar is used for dragging the window.
143 if (mode() >= widget_mode::partial) {
144 auto r = layout().contains(position) ? hitbox{id, _layout.elevation, hitbox_type::move_area} : hitbox{};
145
146 for (auto const& child : _children) {
147 hi_assert_not_null(child.value);
148 r = child.value->hitbox_test_from_parent(position, r);
149 }
150
151 return r;
152 } else {
153 return {};
154 }
155 }
156 [[nodiscard]] color focus_color() const noexcept override
157 {
158 if (mode() >= widget_mode::partial) {
159 return theme().color(semantic_color::accent);
160 } else {
161 return theme().color(semantic_color::border, _layout.layer - 1);
162 }
163 }
165private:
166 mutable row_layout<std::unique_ptr<widget>> _children;
167 mutable float _child_height_adjustment = 0.0f;
168 size_t _spacer_index = 0;
169
170 void update_layout_for_child(widget& child, ssize_t index, widget_layout const& context) const noexcept;
171
174 widget& insert(horizontal_alignment alignment, std::unique_ptr<widget> widget) noexcept
175 {
176 auto& ref = *widget;
177 switch (alignment) {
178 using enum horizontal_alignment;
179 case left:
180 _children.insert(_children.cbegin() + _spacer_index, std::move(widget));
181 ++_spacer_index;
182 break;
183 case right:
184 _children.insert(_children.cbegin() + _spacer_index + 1, std::move(widget));
185 break;
186 default:
187 hi_no_default();
188 }
189
190 return ref;
191 }
192
197 bool tab_button_has_focus() const noexcept
198 {
199 for (auto const& cell : _children) {
200 if (auto const *const c = dynamic_cast<toolbar_tab_button_widget *>(cell.value.get())) {
201 if (c->focus() and c->value() == widget_value::on) {
202 return true;
203 }
204 }
205 }
206
207 return false;
208 }
209};
210
211}} // namespace hi::v1
Defines widget.
horizontal_alignment
Horizontal alignment.
Definition alignment.hpp:102
@ right
Align the text to the right side.
@ left
Align the text to the left side.
@ rectangle
The gui_event has rectangle data.
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
The HikoGUI namespace.
Definition array_generic.hpp:20
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.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition widget_intf.hpp:24
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:30
widget_layout const & layout() const noexcept
Get the current layout for this widget.
Definition widget_intf.hpp:206
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:35
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:68
toolbar_widget(widget_intf const *parent) noexcept
Constructs an empty row/column widget.
Definition toolbar_widget.hpp:46
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:55
T move(T... args)
T ref(T... args)