HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
window_controls_win32_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{theme().large_size() * 3.0f, theme().large_size()};
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 auto y = context.height() - extent.height();
50
51 closeRectangle =
52 aarectangle{point2(extent.width() * 2.0f / 3.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
53
54 maximizeRectangle =
55 aarectangle{point2(extent.width() * 1.0f / 3.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
56
57 minimizeRectangle = aarectangle{point2(0.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
58
59 closeWindowGlyph = find_glyph(hikogui_icon::CloseWindow);
60 minimizeWindowGlyph = find_glyph(hikogui_icon::MinimizeWindow);
61 maximizeWindowGlyph = find_glyph(hikogui_icon::MaximizeWindowMS);
62 restoreWindowGlyph = find_glyph(hikogui_icon::RestoreWindowMS);
63 hilet glyph_size = theme().icon_size();
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 if (pressedClose) {
81 context.draw_box(layout(), closeRectangle, color{1.0f, 0.0f, 0.0f});
82 } else if (hoverClose) {
83 context.draw_box(layout(), closeRectangle, color{0.5f, 0.0f, 0.0f});
84 } else {
85 context.draw_box(layout(), closeRectangle, theme().color(semantic_color::fill, semantic_layer));
86 }
87
88 if (pressedMinimize) {
89 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, semantic_layer + 2));
90 } else if (hoverMinimize) {
91 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, semantic_layer + 1));
92 } else {
93 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, semantic_layer));
94 }
95
96 if (pressedMaximize) {
97 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, semantic_layer + 2));
98 } else if (hoverMaximize) {
99 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, semantic_layer + 1));
100 } else {
101 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, semantic_layer));
102 }
103
104 hilet glyph_color = context.active ? label_color() : foreground_color();
105
106 context.draw_glyph(layout(), translate_z(0.1f) * closeWindowGlyphRectangle, closeWindowGlyph, glyph_color);
107 context.draw_glyph(layout(), translate_z(0.1f) * minimizeWindowGlyphRectangle, minimizeWindowGlyph, glyph_color);
108 if (layout().window_size_state == gui_window_size::maximized) {
109 context.draw_glyph(layout(), translate_z(0.1f) * restoreWindowGlyphRectangle, restoreWindowGlyph, glyph_color);
110 } else {
111 context.draw_glyph(layout(), translate_z(0.1f) * maximizeWindowGlyphRectangle, maximizeWindowGlyph, glyph_color);
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 aarectangle closeRectangle;
204 aarectangle minimizeRectangle;
205 aarectangle maximizeRectangle;
206
207 font_book::font_glyph_type closeWindowGlyph;
208 font_book::font_glyph_type minimizeWindowGlyph;
209 font_book::font_glyph_type maximizeWindowGlyph;
210 font_book::font_glyph_type restoreWindowGlyph;
211
212 aarectangle closeWindowGlyphRectangle;
213 aarectangle minimizeWindowGlyphRectangle;
214 aarectangle maximizeWindowGlyphRectangle;
215 aarectangle restoreWindowGlyphRectangle;
216
217 bool hoverClose = false;
218 bool hoverMinimize = false;
219 bool hoverMaximize = false;
220
221 bool pressedClose = false;
222 bool pressedMinimize = false;
223 bool pressedMaximize = false;
224};
225
226}} // 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
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
int semantic_layer
The draw layer of the widget.
Definition widget.hpp:66
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_win32_widget.hpp:27