HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
window_controls_macos_widget.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2020-2021.
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 "../font/font.hpp"
13#include "../macros.hpp"
14#include <memory>
15#include <string>
16#include <array>
17
18namespace hi { inline namespace v1 {
19
28public:
29 using super = widget;
30
32
34 [[nodiscard]] box_constraints update_constraints() noexcept override
35 {
36 _layout = {};
37
38 hilet size = extent2{DIAMETER * 3.0f + 2.0f * MARGIN + 2.0f * SPACING, DIAMETER + 2.0f * MARGIN};
39 return {size, size, size};
40 }
41
42 void set_layout(widget_layout const& context) noexcept override
43 {
44 if (compare_store(_layout, context)) {
45 auto extent = context.size();
46 if (extent.height() > floor_cast<int>(theme().large_size() * 1.2f)) {
47 extent = extent2{extent.width(), theme().large_size()};
48 }
49
50 closeRectangle = aarectangle{point2(MARGIN, extent.height() / 2.0f - RADIUS), extent2{DIAMETER, DIAMETER}};
51
52 minimizeRectangle =
53 aarectangle{point2(MARGIN + DIAMETER + SPACING, extent.height() / 2.0f - RADIUS), extent2{DIAMETER, DIAMETER}};
54
55 maximizeRectangle = aarectangle{
56 point2(MARGIN + DIAMETER + SPACING + DIAMETER + SPACING, extent.height() / 2.0f - RADIUS),
57 extent2{DIAMETER, DIAMETER}};
58
59 closeWindowGlyph = find_glyph(hikogui_icon::CloseWindow);
60 minimizeWindowGlyph = find_glyph(hikogui_icon::MinimizeWindow);
61 maximizeWindowGlyph = find_glyph(hikogui_icon::MaximizeWindowMacOS);
62 restoreWindowGlyph = find_glyph(hikogui_icon::RestoreWindowMacOS);
63 hilet glyph_size = 5.0f;
64
65 hilet closeWindowGlyphBB = closeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
66 hilet minimizeWindowGlyphBB = minimizeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
67 hilet maximizeWindowGlyphBB = maximizeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
68 hilet restoreWindowGlyphBB = restoreWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
69
70 closeWindowGlyphRectangle = align(closeRectangle, closeWindowGlyphBB, alignment::middle_center());
71 minimizeWindowGlyphRectangle = align(minimizeRectangle, minimizeWindowGlyphBB, alignment::middle_center());
72 maximizeWindowGlyphRectangle = align(maximizeRectangle, maximizeWindowGlyphBB, alignment::middle_center());
73 restoreWindowGlyphRectangle = align(maximizeRectangle, restoreWindowGlyphBB, alignment::middle_center());
74 }
75 }
76
77 void draw(draw_context const& context) noexcept override
78 {
79 if (*mode > widget_mode::invisible and overlaps(context, layout())) {
80 hilet close_circle_color = (not context.active and not *hover) ? color(0.246f, 0.246f, 0.246f) :
81 pressedClose ? color(1.0f, 0.242f, 0.212f) :
82 color(1.0f, 0.1f, 0.082f);
83 context.draw_box(layout(), closeRectangle, close_circle_color, corner_radii{RADIUS});
84
85 hilet minimize_circle_color = (not context.active and not *hover) ? color(0.246f, 0.246f, 0.246f) :
86 pressedMinimize ? color(1.0f, 0.847f, 0.093f) :
87 color(0.784f, 0.521f, 0.021f);
88 context.draw_box(layout(), minimizeRectangle, minimize_circle_color, corner_radii{RADIUS});
89
90 hilet maximize_circle_color = (not context.active and not *hover) ? color(0.246f, 0.246f, 0.246f) :
91 pressedMaximize ? color(0.223f, 0.863f, 0.1f) :
92 color(0.082f, 0.533f, 0.024f);
93
94 context.draw_box(layout(), maximizeRectangle, maximize_circle_color, corner_radii{RADIUS});
95
96 if (*hover) {
97 context.draw_glyph(
98 layout(), translate_z(0.1f) * closeWindowGlyphRectangle, closeWindowGlyph, color{0.319f, 0.0f, 0.0f});
99 context.draw_glyph(
100 layout(), translate_z(0.1f) * minimizeWindowGlyphRectangle, minimizeWindowGlyph, color{0.212f, 0.1f, 0.0f});
101
102 if (layout().window_size_state == gui_window_size::maximized) {
103 context.draw_glyph(
104 layout(), translate_z(0.1f) * restoreWindowGlyphRectangle, restoreWindowGlyph, color{0.0f, 0.133f, 0.0f});
105 } else {
106 context.draw_glyph(
107 layout(),
108 translate_z(0.1f) * maximizeWindowGlyphRectangle,
109 maximizeWindowGlyph,
110 color{0.0f, 0.133f, 0.0f});
111 }
112 }
113 }
114 }
115
116 bool handle_event(gui_event const& event) noexcept override
117 {
118 switch (event.type()) {
119 case gui_event_type::mouse_move:
120 case gui_event_type::mouse_drag:
121 {
122 // Check the hover states of each button.
123 auto state_has_changed = false;
124 state_has_changed |= compare_store(hoverClose, closeRectangle.contains(event.mouse().position));
125 state_has_changed |= compare_store(hoverMinimize, minimizeRectangle.contains(event.mouse().position));
126 state_has_changed |= compare_store(hoverMaximize, maximizeRectangle.contains(event.mouse().position));
127 if (state_has_changed) {
129 }
130 }
131 break;
132
133 case gui_event_type::mouse_exit:
134 hoverClose = false;
135 hoverMinimize = false;
136 hoverMaximize = false;
139
140 case gui_event_type::mouse_down:
141 if (event.mouse().cause.left_button) {
142 if (closeRectangle.contains(event.mouse().position)) {
143 pressedClose = true;
144
145 } else if (minimizeRectangle.contains(event.mouse().position)) {
146 pressedMinimize = true;
147
148 } else if (maximizeRectangle.contains(event.mouse().position)) {
149 pressedMaximize = true;
150 }
152 return true;
153 }
154 break;
155
156 case gui_event_type::mouse_up:
157 if (event.mouse().cause.left_button) {
158 pressedClose = false;
159 pressedMinimize = false;
160 pressedMaximize = false;
162
163 if (closeRectangle.contains(event.mouse().position)) {
165
166 } else if (minimizeRectangle.contains(event.mouse().position)) {
168
169 } else if (maximizeRectangle.contains(event.mouse().position)) {
170 switch (layout().window_size_state) {
171 case gui_window_size::normal:
173
174 case gui_window_size::maximized:
176
177 default:
178 hi_no_default();
179 }
180 }
181 return true;
182 }
183 break;
184
185 default:;
186 }
188 }
189
190 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
191 {
192 hi_axiom(loop::main().on_thread());
193
194 if (*mode >= widget_mode::partial and layout().contains(position) and
195 (closeRectangle.contains(position) or minimizeRectangle.contains(position) or maximizeRectangle.contains(position))) {
196 return hitbox{id, _layout.elevation, hitbox_type::button};
197 } else {
198 return {};
199 }
200 }
202private:
203 constexpr static int GLYPH_SIZE = 5;
204 constexpr static int RADIUS = 5;
205 constexpr static int DIAMETER = RADIUS * 2;
206 constexpr static int MARGIN = 10;
207 constexpr static int SPACING = 8;
208
209 aarectangle closeRectangle;
210 aarectangle minimizeRectangle;
211 aarectangle maximizeRectangle;
212
213 font_book::font_glyph_type closeWindowGlyph;
214 font_book::font_glyph_type minimizeWindowGlyph;
215 font_book::font_glyph_type maximizeWindowGlyph;
216 font_book::font_glyph_type restoreWindowGlyph;
217
218 aarectangle closeWindowGlyphRectangle;
219 aarectangle minimizeWindowGlyphRectangle;
220 aarectangle maximizeWindowGlyphRectangle;
221 aarectangle restoreWindowGlyphRectangle;
222
223 bool hoverClose = false;
224 bool hoverMinimize = false;
225 bool hoverMaximize = false;
226
227 bool pressedClose = false;
228 bool pressedMinimize = false;
229 bool pressedMaximize = false;
230};
231
232}} // namespace hi::v1
Defines widget.
@ window_normalize
Request the window to be restored to the original size after a minimize and maximize commands.
@ window_minimize
Request the window to minimize.
@ window_close
Request the window to be closed.
@ window_maximize
Request the window to maximize.
@ partial
A widget is partially enabled.
@ invisible
The widget is invisible.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:56
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
This is a RGBA floating point color.
Definition color.hpp:45
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:29
constexpr bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition aarectangle.hpp:264
The 4 radii of the corners of a quad or rectangle.
Definition corner_radii.hpp:19
A high-level geometric extent.
Definition extent2.hpp:29
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent2.hpp:104
Draw context for drawing using the HikoGUI shaders.
Definition draw_context.hpp:208
A user interface event.
Definition gui_event.hpp:75
widget_id id
The numeric identifier of a widget.
Definition widget_intf.hpp:23
widget_intf * parent
Pointer to the parent widget.
Definition widget_intf.hpp:28
The layout of a widget.
Definition widget_layout.hpp:38
2D constraints.
Definition box_constraints.hpp:25
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
widget_layout const & layout() const noexcept override
Get the current layout for this widget.
Definition widget.hpp:169
observer< bool > hover
Mouse cursor is hovering over the widget.
Definition widget.hpp:46
widget(widget *parent) noexcept
Definition widget.hpp:87
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:189
bool process_event(gui_event const &event) const noexcept override
Send a event to the window.
Definition widget.hpp:178
observer< widget_mode > mode
The widget mode.
Definition widget.hpp:42
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:198
Window control button widget.
Definition window_controls_macos_widget.hpp:27