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
|
#pragma once
#include "cru/ui/control.hpp"
#include "cru/common/logger.hpp"
#include <list>
namespace cru::ui {
// 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 std::string_view& event_name,
Control* const original_sender,
event::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
Control* const last_receiver, Args&&... args) {
#ifndef CRU_DEBUG
CRU_UNUSED(event_name)
#endif
#ifdef CRU_DEBUG
bool do_log = true;
if (event_name == "MouseMove") do_log = false;
#endif
if (original_sender == last_receiver) {
/*
#ifdef CRU_DEBUG
if (do_log)
log::Debug(
"Routed event {} no need to dispatch (original_sender == "
"last_receiver). Original sender is {}.",
event_name, original_sender->GetControlType());
#endif
*/
return;
}
std::list<Control*> receive_list;
auto parent = original_sender;
while (parent != last_receiver) {
receive_list.push_back(parent);
parent = parent->GetParent();
}
#ifdef CRU_DEBUG
if (do_log) {
std::string log = "Dispatch routed event ";
log += event_name;
log += ". Path (parent first): ";
auto i = receive_list.crbegin();
const auto end = --receive_list.crend();
for (; i != end; ++i) {
log += (*i)->GetControlType();
log += " -> ";
}
log += (*i)->GetControlType();
log::Debug(log);
}
#endif
auto handled = false;
#ifdef CRU_DEBUG
int count = 0;
#endif
// tunnel
for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
#ifdef CRU_DEBUG
count++;
#endif
EventArgs event_args(*i, original_sender, std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>(((*i)->*event_ptr)()->Tunnel())
->Raise(event_args);
if (event_args.IsHandled()) {
handled = true;
#ifdef CRU_DEBUG
if (do_log)
log::Debug(
"Routed event is short-circuit in TUNNEL at {}-st control (count "
"from parent).",
count);
#endif
break;
}
}
// bubble
if (!handled) {
for (auto i : receive_list) {
#ifdef CRU_DEBUG
count--;
#endif
EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>((i->*event_ptr)()->Bubble())
->Raise(event_args);
if (event_args.IsHandled()) {
#ifdef CRU_DEBUG
if (do_log)
log::Debug(
"Routed event is short-circuit in BUBBLE at {}-st control "
"(count "
"from parent).",
count);
#endif
break;
}
}
}
// direct
for (auto i : receive_list) {
EventArgs event_args(i, original_sender, std::forward<Args>(args)...);
static_cast<Event<EventArgs&>*>((i->*event_ptr)()->Direct())
->Raise(event_args);
}
#ifdef CRU_DEBUG
if (do_log) log::Debug("Routed event dispatch finished.");
#endif
}
} // namespace cru::ui
|