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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
#pragma once
#include "Control.h"
#include <cru/base/Base.h>
#include <cru/base/Event.h>
#include <cru/base/Guard.h>
#include <cru/base/log/Logger.h>
#include <cru/platform/gui/UiApplication.h>
#include <cru/platform/gui/Window.h>
namespace cru::ui::controls {
class CRU_UI_API ControlHost : public Object {
CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::ControlHost")
friend Control;
public:
explicit ControlHost(Control* root_control);
~ControlHost() override;
platform::gui::INativeWindow* GetNativeWindow();
void ScheduleRepaint();
void ScheduleRelayout();
void Repaint();
void Relayout();
void RelayoutWithSize(const Size& available_size = Size::Infinite(),
bool set_window_size_to_fit_content = false);
// If true, preferred size of root render object is set to window size when
// measure. Default is true.
bool IsLayoutPreferToFillWindow() const;
void SetLayoutPreferToFillWindow(bool value);
// 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_; }
Control* GetFocusControl();
void SetFocusControl(Control* control);
Control* GetMouseCaptureControl();
bool SetMouseCaptureControl(Control* control);
std::shared_ptr<platform::gui::ICursor> GetOverrideCursor();
void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor);
bool IsInEventHandling();
CRU_DEFINE_EVENT(AfterLayout, std::nullptr_t)
private:
std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow();
void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
void OnNativeFocus(platform::gui::INativeWindow* window,
cru::platform::gui::FocusChangeType focus);
void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
cru::platform::gui::MouseEnterLeaveType enter);
void OnNativeMouseMove(platform::gui::INativeWindow* window,
const Point& point);
void OnNativeMouseDown(platform::gui::INativeWindow* window,
const platform::gui::NativeMouseButtonEventArgs& args);
void OnNativeMouseUp(platform::gui::INativeWindow* window,
const platform::gui::NativeMouseButtonEventArgs& args);
void OnNativeMouseWheel(platform::gui::INativeWindow* window,
const platform::gui::NativeMouseWheelEventArgs& args);
void OnNativeKeyDown(platform::gui::INativeWindow* window,
const platform::gui::NativeKeyEventArgs& args);
void OnNativeKeyUp(platform::gui::INativeWindow* window,
const platform::gui::NativeKeyEventArgs& args);
void DispatchFocusControlChangeEvent(Control* old_control,
Control* new_control, bool is_window);
void DispatchMouseHoverControlChangeEvent(Control* old_control,
Control* new_control,
const Point& point, bool no_leave,
bool no_enter);
template <typename EventArgs, typename... Args>
void DispatchEvent(Control* const original_sender,
events::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
Control* const last_receiver, Args&&... args) {
constexpr auto kLogTag = "cru::ui::controls::DispatchEvent";
event_handling_count_++;
Guard event_handling_count_guard([this] { event_handling_count_--; });
if (original_sender == nullptr || original_sender == last_receiver) return;
std::string log = "Begin dispatching routed event " +
(original_sender->*event_ptr)()->GetName() +
":\n\tTunnel:";
Guard logging_guard([&] {
log += "\nEnd dispatching routed event " +
(original_sender->*event_ptr)()->GetName() + ".";
CRU_LOG_TAG_DEBUG("{}", log);
});
std::vector<ObjectResolver<Control>> receive_list;
auto parent = original_sender;
while (parent != last_receiver) {
receive_list.push_back(parent->CreateResolver());
parent = parent->GetParent();
}
auto handled = false;
// tunnel
for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
auto control = i->Resolve();
log += " ";
if (!control) {
log += "(deleted)";
continue;
}
log += control->GetDebugId();
EventArgs event_args(control, original_sender,
std::forward<Args>(args)...);
(control->*event_ptr)()->tunnel_.Raise(event_args);
if (event_args.IsHandled()) {
log += " marked as handled.";
handled = true;
break;
}
}
// bubble
if (!handled) {
log += "\n\tBubble:";
for (auto resolver : receive_list) {
auto control = resolver.Resolve();
log += " ";
if (!control) {
log += "(deleted)";
continue;
}
log += control->GetDebugId();
EventArgs event_args(control, original_sender,
std::forward<Args>(args)...);
(control->*event_ptr)()->bubble_.Raise(event_args);
if (event_args.IsHandled()) {
log += " marked as handled.";
break;
}
}
}
log += "\n\tDirect:";
// direct
for (auto resolver : receive_list) {
auto control = resolver.Resolve();
log += " ";
if (!control) {
log += "(deleted)";
continue;
}
log += control->GetDebugId();
EventArgs event_args(control, original_sender,
std::forward<Args>(args)...);
(control->*event_ptr)()->direct_.Raise(event_args);
}
}
void UpdateCursor();
void NotifyControlParentChange(Control* control, Control* old_parent,
Control* new_parent);
private:
int event_handling_count_;
Control* root_control_;
std::unique_ptr<platform::gui::INativeWindow> native_window_;
Control* focus_control_;
Control* mouse_hover_control_;
Control* mouse_captured_control_;
std::shared_ptr<platform::gui::ICursor> override_cursor_;
bool layout_prefer_to_fill_window_;
platform::gui::TimerAutoCanceler relayout_schedule_canceler_;
};
} // namespace cru::ui::controls
|