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
18hi_export_module(hikogui.widgets.window_controls_win32_widget);
19
20hi_export namespace hi { inline namespace v1 {
21
30public:
31 using super = widget;
32
34
36 [[nodiscard]] box_constraints update_constraints() noexcept override
37 {
38 _layout = {};
39
40 auto const size = extent2{theme().large_size() * 3.0f, theme().large_size()};
41 return {size, size, size};
42 }
43
44 void set_layout(widget_layout const& context) noexcept override
45 {
46 if (compare_store(_layout, context)) {
47 auto extent = context.size();
48 if (extent.height() > floor_cast<int>(theme().large_size() * 1.2f)) {
49 extent = extent2{extent.width(), theme().large_size()};
50 }
51 auto y = context.height() - extent.height();
52
53 closeRectangle =
54 aarectangle{point2(extent.width() * 2.0f / 3.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
55
56 maximizeRectangle =
57 aarectangle{point2(extent.width() * 1.0f / 3.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
58
59 minimizeRectangle = aarectangle{point2(0.0f, y), extent2{extent.width() * 1.0f / 3.0f, extent.height()}};
60
61 closeWindowGlyph = find_glyph(hikogui_icon::CloseWindow);
62 minimizeWindowGlyph = find_glyph(hikogui_icon::MinimizeWindow);
63 maximizeWindowGlyph = find_glyph(hikogui_icon::MaximizeWindowMS);
64 restoreWindowGlyph = find_glyph(hikogui_icon::RestoreWindowMS);
65 auto const glyph_size = theme().icon_size();
66
67 auto const closeWindowGlyphBB = closeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
68 auto const minimizeWindowGlyphBB = minimizeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
69 auto const maximizeWindowGlyphBB = maximizeWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
70 auto const restoreWindowGlyphBB = restoreWindowGlyph.get_metrics().bounding_rectangle * glyph_size;
71
72 closeWindowGlyphRectangle = align(closeRectangle, closeWindowGlyphBB, alignment::middle_center());
73 minimizeWindowGlyphRectangle = align(minimizeRectangle, minimizeWindowGlyphBB, alignment::middle_center());
74 maximizeWindowGlyphRectangle = align(maximizeRectangle, maximizeWindowGlyphBB, alignment::middle_center());
75 restoreWindowGlyphRectangle = align(maximizeRectangle, restoreWindowGlyphBB, alignment::middle_center());
76 }
77 }
78
79 void draw(draw_context const& context) noexcept override
80 {
81 if (mode() > widget_mode::invisible and overlaps(context, layout())) {
82 if (pressedClose) {
83 context.draw_box(layout(), closeRectangle, color{1.0f, 0.0f, 0.0f});
84 } else if (hoverClose) {
85 context.draw_box(layout(), closeRectangle, color{0.5f, 0.0f, 0.0f});
86 } else {
87 context.draw_box(layout(), closeRectangle, theme().color(semantic_color::fill, _layout.layer));
88 }
89
90 if (pressedMinimize) {
91 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, _layout.layer + 2));
92 } else if (hoverMinimize) {
93 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, _layout.layer + 1));
94 } else {
95 context.draw_box(layout(), minimizeRectangle, theme().color(semantic_color::fill, _layout.layer));
96 }
97
98 if (pressedMaximize) {
99 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, _layout.layer + 2));
100 } else if (hoverMaximize) {
101 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, _layout.layer + 1));
102 } else {
103 context.draw_box(layout(), maximizeRectangle, theme().color(semantic_color::fill, _layout.layer));
104 }
105
106 auto const glyph_color = phase() >= widget_phase::normal ? label_color() : foreground_color();
107
108 context.draw_glyph(layout(), translate_z(0.1f) * closeWindowGlyphRectangle, closeWindowGlyph, glyph_color);
109 context.draw_glyph(layout(), translate_z(0.1f) * minimizeWindowGlyphRectangle, minimizeWindowGlyph, glyph_color);
110 if (layout().window_size_state == gui_window_size::maximized) {
111 context.draw_glyph(layout(), translate_z(0.1f) * restoreWindowGlyphRectangle, restoreWindowGlyph, glyph_color);
112 } else {
113 context.draw_glyph(layout(), translate_z(0.1f) * maximizeWindowGlyphRectangle, maximizeWindowGlyph, glyph_color);
114 }
115 }
116 }
117
118 bool handle_event(gui_event const& event) noexcept override
119 {
120 switch (event.type()) {
121 case gui_event_type::mouse_move:
122 case gui_event_type::mouse_drag:
123 {
124 // Check the hover states of each button.
125 auto state_has_changed = false;
126 state_has_changed |= compare_store(hoverClose, closeRectangle.contains(event.mouse().position));
127 state_has_changed |= compare_store(hoverMinimize, minimizeRectangle.contains(event.mouse().position));
128 state_has_changed |= compare_store(hoverMaximize, maximizeRectangle.contains(event.mouse().position));
129 if (state_has_changed) {
131 }
132 }
133 break;
134
135 case gui_event_type::mouse_exit:
136 hoverClose = false;
137 hoverMinimize = false;
138 hoverMaximize = false;
140 return super::handle_event(event);
141
142 case gui_event_type::mouse_down:
143 if (event.mouse().cause.left_button) {
144 if (closeRectangle.contains(event.mouse().position)) {
145 pressedClose = true;
146
147 } else if (minimizeRectangle.contains(event.mouse().position)) {
148 pressedMinimize = true;
149
150 } else if (maximizeRectangle.contains(event.mouse().position)) {
151 pressedMaximize = true;
152 }
154 return true;
155 }
156 break;
157
158 case gui_event_type::mouse_up:
159 if (event.mouse().cause.left_button) {
160 pressedClose = false;
161 pressedMinimize = false;
162 pressedMaximize = false;
164
165 if (closeRectangle.contains(event.mouse().position)) {
167
168 } else if (minimizeRectangle.contains(event.mouse().position)) {
170
171 } else if (maximizeRectangle.contains(event.mouse().position)) {
172 switch (layout().window_size_state) {
173 case gui_window_size::normal:
175
176 case gui_window_size::maximized:
178
179 default:
180 hi_no_default();
181 }
182 }
183 return true;
184 }
185 break;
186
187 default:;
188 }
189 return super::handle_event(event);
190 }
191
192 [[nodiscard]] hitbox hitbox_test(point2 position) const noexcept override
193 {
194 hi_axiom(loop::main().on_thread());
195
196 if (mode() >= widget_mode::partial and layout().contains(position) and
197 (closeRectangle.contains(position) or minimizeRectangle.contains(position) or maximizeRectangle.contains(position))) {
198 return hitbox{id, _layout.elevation, hitbox_type::button};
199 } else {
200 return {};
201 }
202 }
204private:
205 aarectangle closeRectangle;
206 aarectangle minimizeRectangle;
207 aarectangle maximizeRectangle;
208
209 font_book::font_glyph_type closeWindowGlyph;
210 font_book::font_glyph_type minimizeWindowGlyph;
211 font_book::font_glyph_type maximizeWindowGlyph;
212 font_book::font_glyph_type restoreWindowGlyph;
213
214 aarectangle closeWindowGlyphRectangle;
215 aarectangle minimizeWindowGlyphRectangle;
216 aarectangle maximizeWindowGlyphRectangle;
217 aarectangle restoreWindowGlyphRectangle;
218
219 bool hoverClose = false;
220 bool hoverMinimize = false;
221 bool hoverMaximize = false;
222
223 bool pressedClose = false;
224 bool pressedMinimize = false;
225 bool pressedMaximize = false;
226};
227
228}} // 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.
The HikoGUI namespace.
Definition array_generic.hpp:20
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
This is a RGBA floating point color.
Definition color_intf.hpp:49
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
constexpr bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition aarectangle.hpp:268
A high-level geometric extent.
Definition extent2.hpp:32
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent2.hpp:107
Draw context for drawing using the HikoGUI shaders.
Definition draw_context_intf.hpp:209
point2 position
The current position of the mouse pointer.
Definition gui_event.hpp:44
mouse_buttons cause
Buttons which have caused this event.
Definition gui_event.hpp:62
A user interface event.
Definition gui_event.hpp:82
constexpr gui_event_type type() const noexcept
Get the event type.
Definition gui_event.hpp:221
mouse_event_data & mouse() noexcept
Get the mouse event information.
Definition gui_event.hpp:264
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
The layout of a widget.
Definition widget_layout.hpp:56
2D constraints.
Definition box_constraints.hpp:25
An interactive graphical object as part of the user-interface.
Definition widget.hpp:37
void request_redraw() const noexcept override
Request the widget to be redrawn on the next frame.
Definition widget.hpp:141
widget() noexcept
Constructor for creating sub views.
Definition widget.hpp:55
bool process_event(gui_event const &event) const noexcept override
Send a event to the window.
Definition widget.hpp:130
bool handle_event(gui_event const &event) noexcept override
Handle command.
Definition widget.hpp:150
Window control button widget.
Definition window_controls_win32_widget.hpp:29