aboutsummaryrefslogtreecommitdiff
path: root/src/ui/helper
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-11-08 17:45:41 +0800
committercrupest <crupest@outlook.com>2020-11-08 17:45:41 +0800
commit2188845a7acffa653015a1000139ec0a9a3984bc (patch)
tree7e8ed6eca5868a0943af6fcad6467115f369987c /src/ui/helper
parent93265251d56c91b05f160423077ce95339786f87 (diff)
downloadcru-2188845a7acffa653015a1000139ec0a9a3984bc.tar.gz
cru-2188845a7acffa653015a1000139ec0a9a3984bc.tar.bz2
cru-2188845a7acffa653015a1000139ec0a9a3984bc.zip
...
Diffstat (limited to 'src/ui/helper')
-rw-r--r--src/ui/helper/ClickDetector.cpp131
-rw-r--r--src/ui/helper/ShortcutHub.cpp120
2 files changed, 251 insertions, 0 deletions
diff --git a/src/ui/helper/ClickDetector.cpp b/src/ui/helper/ClickDetector.cpp
new file mode 100644
index 00000000..4059f890
--- /dev/null
+++ b/src/ui/helper/ClickDetector.cpp
@@ -0,0 +1,131 @@
+#include "cru/ui/helper/ClickDetector.hpp"
+
+#include "cru/common/Logger.hpp"
+
+#include <optional>
+
+namespace cru::ui::helper {
+ClickDetector::ClickDetector(controls::Control* control) {
+ Expects(control);
+ control_ = control;
+
+ event_rovoker_guards_.push_back(
+ EventRevokerGuard(control->MouseEnterEvent()->Direct()->AddHandler(
+ [this](event::MouseEventArgs&) {
+ if (this->enable_) {
+ if (this->state_ == ClickState::PressInactive) {
+ if ((this->button_ & this->trigger_button_)) {
+ this->SetState(ClickState::Press);
+ }
+ } else {
+ this->SetState(ClickState::Hover);
+ }
+ }
+ })));
+
+ event_rovoker_guards_.push_back(
+ EventRevokerGuard(control->MouseLeaveEvent()->Direct()->AddHandler(
+ [this](event::MouseEventArgs&) {
+ if (this->enable_) {
+ if (this->state_ == ClickState::Press) {
+ if ((this->button_ & this->trigger_button_)) {
+ this->SetState(ClickState::PressInactive);
+ }
+ } else {
+ this->SetState(ClickState::None);
+ }
+ }
+ })));
+
+ event_rovoker_guards_.push_back(
+ EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler(
+ [this](event::MouseButtonEventArgs& args) {
+ const auto button = args.GetButton();
+ if (this->enable_ && (button & this->trigger_button_) &&
+ this->state_ == ClickState::Hover) {
+ if (!this->control_->CaptureMouse()) {
+ log::TagDebug(log_tag,
+ u"Failed to capture mouse when begin click.");
+ return;
+ }
+ this->down_point_ = args.GetPoint();
+ this->button_ = button;
+ this->SetState(ClickState::Press);
+ }
+ })));
+
+ event_rovoker_guards_.push_back(
+ EventRevokerGuard(control->MouseUpEvent()->Direct()->AddHandler(
+ [this](event::MouseButtonEventArgs& args) {
+ const auto button = args.GetButton();
+ if (this->enable_ && (button & this->trigger_button_) &&
+ button == button_) {
+ if (this->state_ == ClickState::Press) {
+ this->SetState(ClickState::Hover);
+ this->event_.Raise(ClickEventArgs{this->control_,
+ this->down_point_,
+ args.GetPoint(), button});
+ this->control_->ReleaseMouse();
+ } else if (this->state_ == ClickState::PressInactive) {
+ this->SetState(ClickState::None);
+ this->control_->ReleaseMouse();
+ }
+ }
+ })));
+} // namespace cru::ui
+
+void ClickDetector::SetEnabled(bool enable) {
+ if (enable == enable_) {
+ return;
+ }
+
+ enable_ = enable;
+ if (enable) {
+ SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None);
+ } else {
+ if (state_ == ClickState::Press || state_ == ClickState::PressInactive) {
+ SetState(ClickState::None);
+ control_->ReleaseMouse();
+ } else if (state_ == ClickState::Hover) {
+ SetState(ClickState::None);
+ }
+ }
+}
+
+void ClickDetector::SetTriggerButton(MouseButton trigger_button) {
+ if (trigger_button == trigger_button_) {
+ return;
+ }
+
+ trigger_button_ = trigger_button;
+ if ((state_ == ClickState::Press || state_ == ClickState::PressInactive) &&
+ !(button_ & trigger_button)) {
+ SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None);
+ control_->ReleaseMouse();
+ }
+}
+
+void ClickDetector::SetState(ClickState state) {
+#ifdef CRU_DEBUG
+ auto to_string = [](ClickState state) -> std::u16string_view {
+ switch (state) {
+ case ClickState::None:
+ return u"None";
+ case ClickState::Hover:
+ return u"Hover";
+ case ClickState::Press:
+ return u"Press";
+ case ClickState::PressInactive:
+ return u"PressInvactive";
+ default:
+ UnreachableCode();
+ }
+ };
+ log::TagDebug(log_tag, u"Click state changed, new state: {}.",
+ to_string(state));
+#endif
+
+ state_ = state;
+ state_change_event_.Raise(state);
+}
+} // namespace cru::ui::helper
diff --git a/src/ui/helper/ShortcutHub.cpp b/src/ui/helper/ShortcutHub.cpp
new file mode 100644
index 00000000..823072f2
--- /dev/null
+++ b/src/ui/helper/ShortcutHub.cpp
@@ -0,0 +1,120 @@
+#include "cru/ui/helper/ShortcutHub.hpp"
+
+#include "cru/common/Logger.hpp"
+#include "cru/ui/DebugFlags.hpp"
+#include "cru/ui/controls/Control.hpp"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <optional>
+
+namespace cru::ui::helper {
+int ShortcutHub::RegisterShortcut(Shortcut shortcut) {
+ const int id = current_id_++;
+ map_[shortcut.key_bind].push_back({id, std::move(shortcut.name),
+ shortcut.key_bind,
+ std::move(shortcut.handler)});
+ return id;
+}
+
+void ShortcutHub::UnregisterShortcut(int id) {
+ if (id <= 0) return;
+ for (auto& pair : map_) {
+ auto& list = pair.second;
+ auto result =
+ std::find_if(list.cbegin(), list.cend(),
+ [id](const ShortcutInfo& info) { return info.id == id; });
+ if (result != list.cend()) {
+ list.erase(result);
+ }
+ }
+}
+
+std::vector<ShortcutInfo> ShortcutHub::GetAllShortcuts() const {
+ std::vector<ShortcutInfo> result;
+
+ for (const auto& pair : map_) {
+ std::copy(pair.second.cbegin(), pair.second.cend(),
+ std::back_inserter(result));
+ }
+
+ return result;
+}
+
+std::optional<ShortcutInfo> ShortcutHub::GetShortcut(int id) const {
+ for (auto& pair : map_) {
+ auto& list = pair.second;
+ auto result =
+ std::find_if(list.cbegin(), list.cend(),
+ [id](const ShortcutInfo& info) { return info.id == id; });
+ if (result != list.cend()) {
+ return *result;
+ }
+ }
+ return std::nullopt;
+}
+
+const std::vector<ShortcutInfo>& ShortcutHub::GetShortcutByKeyBind(
+ const ShortcutKeyBind& key_bind) const {
+ auto result = map_.find(key_bind);
+ if (result != map_.cend()) return result->second;
+ return empty_list_;
+}
+
+void ShortcutHub::Install(controls::Control* control) {
+ if (!event_guard_.IsEmpty()) {
+ log::Error(u"Shortcut hub is already installed. Failed to install.");
+ return;
+ }
+
+ event_guard_ += control->KeyDownEvent()->Bubble()->AddHandler(
+ std::bind(&ShortcutHub::OnKeyDown, this, std::placeholders::_1));
+}
+
+void ShortcutHub::Uninstall() {
+ if (event_guard_.IsEmpty()) {
+ log::Warn(u"Shortcut hub is not installed. Failed to uninstall.");
+ return;
+ }
+
+ event_guard_.Clear();
+}
+
+void ShortcutHub::OnKeyDown(event::KeyEventArgs& event) {
+ ShortcutKeyBind key_bind(event.GetKeyCode(), event.GetKeyModifier());
+ const auto& shortcut_list = this->GetShortcutByKeyBind(key_bind);
+
+ if constexpr (debug_flags::shortcut) {
+ if (shortcut_list.empty()) {
+ log::Debug(u"No shortcut for key bind {}.", key_bind.ToString());
+ }
+ log::Debug(u"Begin to handle shortcut for key bind {}.",
+ key_bind.ToString());
+ }
+
+ for (const auto& shortcut : shortcut_list) {
+ auto is_handled = shortcut.handler();
+ if (is_handled) {
+ if constexpr (debug_flags::shortcut) {
+ log::Debug(u"Handle {} handled it.", shortcut.name);
+ }
+
+ event.SetHandled();
+
+ break;
+ } else {
+ if constexpr (debug_flags::shortcut) {
+ log::Debug(u"Handle {} disdn't handle it.", shortcut.name);
+ }
+ }
+ }
+
+ if constexpr (debug_flags::shortcut) {
+ if (!shortcut_list.empty()) {
+ log::Debug(u"End handling shortcut for key bind {}.",
+ key_bind.ToString());
+ }
+ }
+}
+} // namespace cru::ui::helper