diff options
author | crupest <crupest@outlook.com> | 2020-10-30 11:25:57 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-10-30 11:25:57 +0800 |
commit | a176c40ba0f913f98e966f11bad557833ae6dc57 (patch) | |
tree | 717f43cb8d6c8c189840d00df68c3d0463d94bdd /src/ui/host/RoutedEventDispatch.hpp | |
parent | 624552fb112f29b91dd96f9543e813b5ee16e87b (diff) | |
download | cru-a176c40ba0f913f98e966f11bad557833ae6dc57.tar.gz cru-a176c40ba0f913f98e966f11bad557833ae6dc57.tar.bz2 cru-a176c40ba0f913f98e966f11bad557833ae6dc57.zip |
...
Diffstat (limited to 'src/ui/host/RoutedEventDispatch.hpp')
-rw-r--r-- | src/ui/host/RoutedEventDispatch.hpp | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/ui/host/RoutedEventDispatch.hpp b/src/ui/host/RoutedEventDispatch.hpp new file mode 100644 index 00000000..de94a598 --- /dev/null +++ b/src/ui/host/RoutedEventDispatch.hpp @@ -0,0 +1,110 @@ +#pragma once +#include "cru/ui/Control.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/ui/DebugFlags.hpp" + +#include <vector> + +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::u16string_view& event_name, + Control* const original_sender, + event::RoutedEvent<EventArgs>* (Control::*event_ptr)(), + Control* const last_receiver, Args&&... args) { + CRU_UNUSED(event_name) + + if (original_sender == last_receiver) { + if constexpr (debug_flags::routed_event) + log::Debug( + "Routed event {} no need to dispatch (original_sender == " + "last_receiver). Original sender is {}.", + event_name, original_sender->GetControlType()); + return; + } + + std::vector<Control*> receive_list; + + auto parent = original_sender; + while (parent != last_receiver) { + receive_list.push_back(parent); + parent = parent->GetParent(); + } + + if constexpr (debug_flags::routed_event) { + std::u16string 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)->GetControlType(); + log += u" -> "; + } + log += (*i)->GetControlType(); + log::Debug(log); + } + + auto handled = false; + + int count = 0; + + // tunnel + for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { + count++; + 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; + if constexpr (debug_flags::routed_event) + log::Debug( + u"Routed event is short-circuit in TUNNEL at {}-st control (count " + u"from parent).", + count); + break; + } + } + + // bubble + if (!handled) { + for (auto i : receive_list) { + count--; + 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()) { + if constexpr (debug_flags::routed_event) + log::Debug( + u"Routed event is short-circuit in BUBBLE at {}-st control " + u"(count from parent).", + count); + 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); + } + + if constexpr (debug_flags::routed_event) + log::Debug(u"Routed event dispatch finished."); +} +} // namespace cru::ui |