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
|
#pragma once
#include "cru/common/SelfResolvable.h"
#include "cru/common/log/Logger.h"
#include "cru/ui/DebugFlags.h"
#include "cru/ui/controls/Control.h"
#include "cru/ui/host/WindowHost.h"
#include <vector>
namespace cru::ui::host {
// Dispatch the event.
//
// This will raise routed event of the control and its parent and parent's
// parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
//
// First tunnel from top to bottom possibly stopped by "handled" flag in
// EventArgs. Second bubble from bottom to top possibly stopped by "handled"
// flag in EventArgs. Last direct to each control.
//
// Args is of type "EventArgs". The first init argument is "sender", which is
// automatically bound to each receiving control. The second init argument is
// "original_sender", which is unchanged. And "args" will be perfectly forwarded
// as the rest arguments.
template <typename EventArgs, typename... Args>
void DispatchEvent(
const String& event_name, controls::Control* const original_sender,
events::RoutedEvent<EventArgs>* (controls::Control::*event_ptr)(),
controls::Control* const last_receiver, Args&&... args) {
constexpr auto kLogTag = u"DispatchEvent";
if (original_sender == nullptr) return;
CRU_UNUSED(event_name)
if (original_sender == last_receiver) {
if constexpr (debug_flags::routed_event)
CRU_LOG_DEBUG(
u"Routed event {} no need to dispatch (original_sender == "
"last_receiver). Original sender is {}.",
event_name, original_sender->GetControlType());
return;
}
WindowHost::EnterEventHandling();
std::vector<ObjectResolver<controls::Control>> receive_list;
auto parent = original_sender;
while (parent != last_receiver) {
receive_list.push_back(parent->CreateResolver());
auto p = parent->GetParent();
assert(!(p == nullptr && last_receiver != nullptr));
parent = p;
}
if constexpr (debug_flags::routed_event) {
String log = u"Dispatch routed event ";
log += event_name;
log += u". Path (parent first): ";
auto i = receive_list.crbegin();
const auto end = --receive_list.crend();
for (; i != end; ++i) {
log += i->Resolve()->GetControlType();
log += u" -> ";
}
log += i->Resolve()->GetControlType();
CRU_LOG_DEBUG(log);
}
auto handled = false;
int count = 0;
// tunnel
for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
count++;
auto control = i->Resolve();
if (!control) continue;
EventArgs event_args(control, original_sender, std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Tunnel())
->Raise(event_args);
if (event_args.IsHandled()) {
handled = true;
if constexpr (debug_flags::routed_event)
CRU_LOG_DEBUG(
u"Routed event is short-circuit in TUNNEL at {}-st control (count "
u"from parent).",
count);
break;
}
}
// bubble
if (!handled) {
for (const auto& resolver : receive_list) {
count--;
auto control = resolver.Resolve();
if (!control) continue;
EventArgs event_args(control, original_sender,
std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Bubble())
->Raise(event_args);
if (event_args.IsHandled()) {
if constexpr (debug_flags::routed_event)
CRU_LOG_DEBUG(
u"Routed event is short-circuit in BUBBLE at {}-st control "
u"(count from parent).",
count);
break;
}
}
}
// direct
for (auto resolver : receive_list) {
auto control = resolver.Resolve();
if (!control) continue;
EventArgs event_args(control, original_sender, std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Direct())
->Raise(event_args);
}
if constexpr (debug_flags::routed_event)
CRU_LOG_DEBUG(u"Routed event dispatch finished.");
WindowHost::LeaveEventHandling();
}
} // namespace cru::ui::host
|