1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#pragma once
#include "Base.hpp"
#include "cru/common/Event.hpp"
#include "cru/common/SelfResolvable.hpp"
#include "render/Base.hpp"
namespace cru::ui {
struct AfterLayoutEventArgs {};
// The host of all controls and render objects.
//
// 3 situations on destroy:
// 1. Native window destroyed, IsRetainAfterDestroy: false:
// OnNativeDestroy(set native_window_destroyed_ to true, call ~Window due to
// deleting_ is false and IsRetainAfterDestroy is false) -> ~Window ->
// ~UiHost(not destroy native window repeatedly due to native_window_destroyed_
// is true)
// 2. Native window destroyed, IsRetainAfterDestroy: true:
// OnNativeDestroy(set native_window_destroyed_ to true, not call ~Window
// because deleting_ is false and IsRetainAfterDestroy is true)
// then, ~Window -> ~UiHost(not destroy native window repeatedly due to
// native_window_destroyed_ is true)
// 3. Native window not destroyed, ~Window is called:
// ~Window -> ~UiHost(set deleting_ to true, destroy native window
// due to native_window_destroyed is false) -> OnNativeDestroy(not call ~Window
// due to deleting_ is true and IsRetainAfterDestroy is whatever)
// In conclusion:
// 1. Set native_window_destroyed_ to true at the beginning of OnNativeDestroy.
// 2. Set deleting_ to true at the beginning of ~UiHost.
// 3. Destroy native window when native_window_destroy_ is false in ~Window.
// 4. Delete Window when deleting_ is false and IsRetainAfterDestroy is false in
// OnNativeDestroy.
class UiHost : public Object, public SelfResolvable<UiHost> {
CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::UiHost")
public:
// This will create root window render object and attach it to window.
// It will also create and manage a native window.
UiHost(Window* window);
CRU_DELETE_COPY(UiHost)
CRU_DELETE_MOVE(UiHost)
~UiHost() override;
public:
// Mark the layout as invalid, and arrange a re-layout later.
// This method could be called more than one times in a message cycle. But
// layout only takes place once.
void InvalidateLayout();
// Mark the paint as invalid, and arrange a re-paint later.
// This method could be called more than one times in a message cycle. But
// paint only takes place once.
void InvalidatePaint();
IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() {
return &after_layout_event_;
}
void Relayout();
// Get current control that mouse hovers on. This ignores the mouse-capture
// control. Even when mouse is captured by another control, this function
// return the control under cursor. You can use `GetMouseCaptureControl` to
// get more info.
Control* GetMouseHoverControl() const { return mouse_hover_control_; }
//*************** region: focus ***************
// Request focus for specified control.
bool RequestFocusFor(Control* control);
// Get the control that has focus.
Control* GetFocusControl();
//*************** region: focus ***************
// Pass nullptr to release capture. If mouse is already capture by a control,
// this capture will fail and return false. If control is identical to the
// capturing control, capture is not changed and this function will return
// true.
//
// When capturing control changes,
// appropriate event will be sent. If mouse is not on the capturing control
// and capture is released, mouse enter event will be sent to the mouse-hover
// control. If mouse is not on the capturing control and capture is set, mouse
// leave event will be sent to the mouse-hover control.
bool CaptureMouseFor(Control* control);
// Return null if not captured.
Control* GetMouseCaptureControl();
Control* HitTest(const Point& point);
void UpdateCursor();
std::shared_ptr<platform::native::INativeWindowResolver>
GetNativeWindowResolver() {
return native_window_resolver_;
}
bool IsRetainAfterDestroy() { return retain_after_destroy_; }
void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; }
private:
//*************** region: native messages ***************
void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t);
void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t);
void OnNativeResize(platform::native::INativeWindow* window,
const Size& size);
void OnNativeFocus(platform::native::INativeWindow* window,
cru::platform::native::FocusChangeType focus);
void OnNativeMouseEnterLeave(
platform::native::INativeWindow* window,
cru::platform::native::MouseEnterLeaveType enter);
void OnNativeMouseMove(platform::native::INativeWindow* window,
const Point& point);
void OnNativeMouseDown(
platform::native::INativeWindow* window,
const platform::native::NativeMouseButtonEventArgs& args);
void OnNativeMouseUp(
platform::native::INativeWindow* window,
const platform::native::NativeMouseButtonEventArgs& args);
void OnNativeKeyDown(platform::native::INativeWindow* window,
const platform::native::NativeKeyEventArgs& args);
void OnNativeKeyUp(platform::native::INativeWindow* window,
const platform::native::NativeKeyEventArgs& args);
//*************** region: event dispatcher helper ***************
void DispatchMouseHoverControlChangeEvent(Control* old_control,
Control* new_control,
const Point& point, bool no_leave,
bool no_enter);
private:
bool need_layout_ = false;
Event<AfterLayoutEventArgs> after_layout_event_;
std::shared_ptr<platform::native::INativeWindowResolver>
native_window_resolver_;
// See remarks of UiHost.
bool retain_after_destroy_ = false;
// See remarks of UiHost.
bool deleting_ = false;
// We need this because calling Resolve on resolver in handler of destroy
// event is bad and will always get the dying window. But we need to label the
// window as destroyed so the destructor will not destroy native window
// repeatedly. See remarks of UiHost.
bool native_window_destroyed_ = false;
std::vector<EventRevokerGuard> event_revoker_guards_;
Window* window_control_;
std::unique_ptr<render::WindowRenderObject> root_render_object_;
Control* mouse_hover_control_;
Control* focus_control_; // "focus_control_" can't be nullptr
Control* mouse_captured_control_;
};
} // namespace cru::ui
|