aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-02-28 14:58:20 +0800
committercrupest <crupest@outlook.com>2021-02-28 14:58:20 +0800
commit38a5b9eddb75e07695ee38f0bd41d3fd76341a15 (patch)
tree181d5c797e7b3930e3ef33267429aff97c72e8e6
parent568abec9c267d072b03f3fbb27b4bda33307f244 (diff)
downloadcru-38a5b9eddb75e07695ee38f0bd41d3fd76341a15.tar.gz
cru-38a5b9eddb75e07695ee38f0bd41d3fd76341a15.tar.bz2
cru-38a5b9eddb75e07695ee38f0bd41d3fd76341a15.zip
...
-rw-r--r--include/cru/common/Event.hpp70
-rw-r--r--src/ui/render/ScrollBar.cpp160
2 files changed, 127 insertions, 103 deletions
diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp
index aa8fadbb..b6999aa4 100644
--- a/include/cru/common/Event.hpp
+++ b/include/cru/common/Event.hpp
@@ -110,7 +110,7 @@ struct IEvent : virtual IBaseEvent {
public:
using EventArgs = DeducedEventArgs<TEventArgs>;
using EventHandler = std::function<void(EventArgs)>;
- using HandlerVariant = std::variant<SpyOnlyHandler, EventHandler>;
+ using ShortCircuitHandler = std::function<bool(EventArgs)>;
protected:
IEvent() = default;
@@ -121,24 +121,27 @@ struct IEvent : virtual IBaseEvent {
public:
virtual EventRevoker AddHandler(EventHandler handler) = 0;
+ virtual EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) = 0;
+ virtual EventRevoker PrependShortCircuitHandler(
+ ShortCircuitHandler handler) = 0;
};
// A non-copyable non-movable Event class.
// It stores a list of event handlers.
template <typename TEventArgs>
class Event : public details::EventBase, public IEvent<TEventArgs> {
+ using typename IEvent<TEventArgs>::EventArgs;
+
using typename IBaseEvent::SpyOnlyHandler;
using typename IEvent<TEventArgs>::EventHandler;
- using typename IEvent<TEventArgs>::HandlerVariant;
+ using typename IEvent<TEventArgs>::ShortCircuitHandler;
private:
struct HandlerData {
- HandlerData(EventHandlerToken token, EventHandler handler)
- : token(token), handler(handler) {}
- HandlerData(EventHandlerToken token, SpyOnlyHandler handler)
- : token(token), handler(handler) {}
+ HandlerData(EventHandlerToken token, ShortCircuitHandler handler)
+ : token(token), handler(std::move(handler)) {}
EventHandlerToken token;
- HandlerVariant handler;
+ ShortCircuitHandler handler;
};
public:
@@ -148,43 +151,50 @@ class Event : public details::EventBase, public IEvent<TEventArgs> {
~Event() = default;
EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) override {
+ return AddShortCircuitHandler([handler = std::move(handler)](EventArgs) {
+ handler();
+ return false;
+ });
+ }
+
+ EventRevoker AddHandler(EventHandler handler) override {
+ return AddShortCircuitHandler(
+ [handler = std::move(handler)](EventArgs args) {
+ handler(args);
+ return false;
+ });
+ }
+
+ // Handler return true to short circuit following handlers.
+ EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) override {
const auto token = current_token_++;
this->handler_data_list_.emplace_back(token, std::move(handler));
return CreateRevoker(token);
}
- EventRevoker AddHandler(EventHandler handler) override {
+ // Handler return true to short circuit following handlers.
+ EventRevoker PrependShortCircuitHandler(
+ ShortCircuitHandler handler) override {
const auto token = current_token_++;
- this->handler_data_list_.emplace_back(token, std::move(handler));
+ this->handler_data_list_.emplace(this->handler_data_list_.cbegin(), token,
+ std::move(handler));
return CreateRevoker(token);
}
// This method will make a copy of all handlers. Because user might delete a
- // handler in a handler, which may lead to seg fault as the handler is deleted
- // while being executed.
- // Thanks to this behavior, all handlers will be taken a snapshot when Raise
- // is called, so even if you delete a handler during this period, all handlers
- // in the snapshot will be executed.
- void Raise(typename IEvent<TEventArgs>::EventArgs args) {
- std::vector<HandlerVariant> handlers;
+ // handler in a handler, which may lead to seg fault as the handler is
+ // deleted while being executed. Thanks to this behavior, all handlers will
+ // be taken a snapshot when Raise is called, so even if you delete a handler
+ // during this period, all handlers in the snapshot will be executed.
+ void Raise(EventArgs args) {
+ std::vector<ShortCircuitHandler> handlers;
handlers.reserve(this->handler_data_list_.size());
for (const auto& data : this->handler_data_list_) {
handlers.push_back(data.handler);
}
- for (const auto& handler_variant : handlers) {
- std::visit(
- [&args](auto&& handler) {
- using T = std::decay_t<decltype(handler)>;
- if constexpr (std::is_same_v<T, SpyOnlyHandler>) {
- handler();
- } else if constexpr (std::is_same_v<T, EventHandler>) {
- handler(args);
- } else {
- static_assert(details::always_false_v<T>,
- "non-exhaustive visitor!");
- }
- },
- handler_variant);
+ for (const auto& handler : handlers) {
+ auto short_circuit = handler(args);
+ if (short_circuit) return;
}
}
diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp
index 70255752..00deb479 100644
--- a/src/ui/render/ScrollBar.cpp
+++ b/src/ui/render/ScrollBar.cpp
@@ -73,82 +73,96 @@ void ScrollBar::Draw(platform::graphics::IPainter* painter) {
void ScrollBar::InstallHandlers(controls::Control* control) {
event_guard_.Clear();
if (control != nullptr) {
- event_guard_ += control->MouseDownEvent()->Bubble()->AddHandler(
- [control, this](event::MouseButtonEventArgs& event) {
- if (event.GetButton() == mouse_buttons::left && IsEnabled() &&
- IsExpanded()) {
- auto hit_test_result =
- ExpandedHitTest(event.GetPoint(render_object_));
- if (!hit_test_result) return;
-
- switch (*hit_test_result) {
- case ScrollBarAreaKind::UpArrow:
- this->scroll_attempt_event_.Raise(
- {GetDirection(), ScrollKind::Line, -1});
- event.SetHandled();
- break;
- case ScrollBarAreaKind::DownArrow:
- this->scroll_attempt_event_.Raise(
- {GetDirection(), ScrollKind::Line, 1});
- event.SetHandled();
- break;
- case ScrollBarAreaKind::UpSlot:
- this->scroll_attempt_event_.Raise(
- {GetDirection(), ScrollKind::Page, -1});
- event.SetHandled();
- break;
- case ScrollBarAreaKind::DownSlot:
- this->scroll_attempt_event_.Raise(
- {GetDirection(), ScrollKind::Page, 1});
+ event_guard_ +=
+ control->MouseDownEvent()->Bubble()->PrependShortCircuitHandler(
+ [control, this](event::MouseButtonEventArgs& event) {
+ if (event.GetButton() == mouse_buttons::left && IsEnabled() &&
+ IsExpanded()) {
+ auto hit_test_result =
+ ExpandedHitTest(event.GetPoint(render_object_));
+ if (!hit_test_result) return false;
+
+ switch (*hit_test_result) {
+ case ScrollBarAreaKind::UpArrow:
+ this->scroll_attempt_event_.Raise(
+ {GetDirection(), ScrollKind::Line, -1});
+ event.SetHandled();
+ return true;
+ case ScrollBarAreaKind::DownArrow:
+ this->scroll_attempt_event_.Raise(
+ {GetDirection(), ScrollKind::Line, 1});
+ event.SetHandled();
+ return true;
+ case ScrollBarAreaKind::UpSlot:
+ this->scroll_attempt_event_.Raise(
+ {GetDirection(), ScrollKind::Page, -1});
+ event.SetHandled();
+ return true;
+ case ScrollBarAreaKind::DownSlot:
+ this->scroll_attempt_event_.Raise(
+ {GetDirection(), ScrollKind::Page, 1});
+ event.SetHandled();
+ return true;
+ case ScrollBarAreaKind::Thumb: {
+ auto thumb_rect =
+ GetExpandedAreaRect(ScrollBarAreaKind::Thumb);
+ assert(thumb_rect);
+
+ if (!control->CaptureMouse()) break;
+ move_thumb_thumb_original_rect_ = *thumb_rect;
+ move_thumb_start_ = event.GetPoint();
+ event.SetHandled();
+ return true;
+ }
+ default:
+ break;
+ }
+ }
+
+ return false;
+ });
+
+ event_guard_ +=
+ control->MouseUpEvent()->Bubble()->PrependShortCircuitHandler(
+ [control, this](event::MouseButtonEventArgs& event) {
+ if (event.GetButton() == mouse_buttons::left &&
+ move_thumb_start_) {
+ move_thumb_start_ = std::nullopt;
+ control->ReleaseMouse();
event.SetHandled();
- break;
- case ScrollBarAreaKind::Thumb: {
- auto thumb_rect = GetExpandedAreaRect(ScrollBarAreaKind::Thumb);
- assert(thumb_rect);
-
- if (!control->CaptureMouse()) break;
- move_thumb_thumb_original_rect_ = *thumb_rect;
- move_thumb_start_ = event.GetPoint();
+ return true;
+ }
+ return false;
+ });
+
+ event_guard_ +=
+ control->MouseMoveEvent()->Bubble()->PrependShortCircuitHandler(
+ [this](event::MouseEventArgs& event) {
+ if (move_thumb_start_) {
+ auto new_scroll_position = CalculateNewScrollPosition(
+ move_thumb_thumb_original_rect_,
+ event.GetPoint() - *move_thumb_start_);
+
+ this->scroll_attempt_event_.Raise({GetDirection(),
+ ScrollKind::Absolute,
+ new_scroll_position});
event.SetHandled();
- break;
+ return true;
}
- default:
- break;
- }
- }
- });
-
- event_guard_ += control->MouseUpEvent()->Bubble()->AddHandler(
- [control, this](event::MouseButtonEventArgs& event) {
- if (event.GetButton() == mouse_buttons::left && move_thumb_start_) {
- move_thumb_start_ = std::nullopt;
- control->ReleaseMouse();
- event.SetHandled();
- }
- });
-
- event_guard_ += control->MouseMoveEvent()->Bubble()->AddHandler(
- [this](event::MouseEventArgs& event) {
- if (move_thumb_start_) {
- auto new_scroll_position = CalculateNewScrollPosition(
- move_thumb_thumb_original_rect_,
- event.GetPoint() - *move_thumb_start_);
-
- this->scroll_attempt_event_.Raise(
- {GetDirection(), ScrollKind::Absolute, new_scroll_position});
- event.SetHandled();
- return;
- }
-
- if (IsEnabled() && !IsExpanded()) {
- auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect();
- if (trigger_expand_area &&
- trigger_expand_area->IsPointInside(
- event.GetPoint(this->render_object_)))
- SetExpanded(true);
- event.SetHandled();
- }
- });
+
+ if (IsEnabled() && !IsExpanded()) {
+ auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect();
+ if (trigger_expand_area &&
+ trigger_expand_area->IsPointInside(
+ event.GetPoint(this->render_object_))) {
+ SetExpanded(true);
+ event.SetHandled();
+ return true;
+ }
+ }
+
+ return false;
+ });
}
}