diff options
author | crupest <crupest@outlook.com> | 2020-10-28 16:58:56 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-10-28 16:58:56 +0800 |
commit | 2df47ffbfff02fb6b64d19e404adc41a93677afe (patch) | |
tree | e15a566c95b68b6266bdcab14bba64be729ab300 | |
parent | fde24556042b76863bdec34dcc213cb7298f68f9 (diff) | |
download | cru-2df47ffbfff02fb6b64d19e404adc41a93677afe.tar.gz cru-2df47ffbfff02fb6b64d19e404adc41a93677afe.tar.bz2 cru-2df47ffbfff02fb6b64d19e404adc41a93677afe.zip |
...
-rw-r--r-- | include/cru/platform/native/UiApplication.hpp | 55 | ||||
-rw-r--r-- | include/cru/ui/ShortcutHub.hpp | 20 | ||||
-rw-r--r-- | src/ui/ShortcutHub.cpp | 8 | ||||
-rw-r--r-- | src/ui/controls/TextControlService.hpp | 62 |
4 files changed, 105 insertions, 40 deletions
diff --git a/include/cru/platform/native/UiApplication.hpp b/include/cru/platform/native/UiApplication.hpp index 135e95c3..4c1b3456 100644 --- a/include/cru/platform/native/UiApplication.hpp +++ b/include/cru/platform/native/UiApplication.hpp @@ -1,6 +1,7 @@ #pragma once #include "../Resource.hpp" #include "Base.hpp" +#include "cru/common/Base.hpp" #include <chrono> #include <functional> @@ -53,6 +54,60 @@ struct IUiApplication : public virtual INativeResource { virtual IInputMethodManager* GetInputMethodManager() = 0; }; +class TimerAutoCanceler { + public: + TimerAutoCanceler() : id_(0) {} + explicit TimerAutoCanceler(long long id) : id_(id) {} + + CRU_DELETE_COPY(TimerAutoCanceler) + + TimerAutoCanceler(TimerAutoCanceler&& other) : id_(other.id_) { + other.id_ = 0; + } + + TimerAutoCanceler& operator=(TimerAutoCanceler&& other) { + Reset(other.id_); + other.id_ = 0; + return *this; + } + + ~TimerAutoCanceler() { Reset(); } + + long long Release() { + auto temp = id_; + id_ = 0; + return temp; + } + + void Reset(long long id = 0) { + if (id_ > 0) IUiApplication::GetInstance()->CancelTimer(id_); + id_ = id; + } + + private: + long long id_; +}; + +class TimerListAutoCanceler { + public: + TimerListAutoCanceler() = default; + CRU_DELETE_COPY(TimerListAutoCanceler) + CRU_DEFAULT_MOVE(TimerListAutoCanceler) + ~TimerListAutoCanceler() = default; + + TimerListAutoCanceler& operator+=(long long id) { + list_.push_back(TimerAutoCanceler(id)); + return *this; + } + + void Clear() { list_.clear(); } + + bool IsEmpty() const { return list_.empty(); } + + private: + std::vector<TimerAutoCanceler> list_; +}; + // Bootstrap from this. std::unique_ptr<IUiApplication> CreateUiApplication(); } // namespace cru::platform::native diff --git a/include/cru/ui/ShortcutHub.hpp b/include/cru/ui/ShortcutHub.hpp index a1dfcb7d..5382f63e 100644 --- a/include/cru/ui/ShortcutHub.hpp +++ b/include/cru/ui/ShortcutHub.hpp @@ -12,6 +12,7 @@ #include <optional> #include <string> #include <string_view> +#include <type_traits> #include <unordered_map> #include <vector> @@ -71,6 +72,15 @@ struct hash<cru::ui::ShortcutKeyBind> { } // namespace std namespace cru::ui { +struct Shortcut { + // Just for debug. + std::u16string name; + ShortcutKeyBind key_bind; + // Return true if it consumes the shortcut. Or return false if it does not + // handle the shortcut. + std::function<bool()> handler; +}; + struct ShortcutInfo { int id; std::u16string name; @@ -87,11 +97,13 @@ class ShortcutHub : public Object { ~ShortcutHub() override = default; - // Handler return true if it consumes the shortcut. Or return false if it does - // not handle the shortcut. Name is just for debug. Return an id used for - // unregistering. int RegisterShortcut(std::u16string name, ShortcutKeyBind bind, - std::function<bool()> handler); + std::function<bool()> handler) { + return RegisterShortcut({std::move(name), bind, std::move(handler)}); + } + + // Return an id used for unregistering. + int RegisterShortcut(Shortcut shortcut); void UnregisterShortcut(int id); diff --git a/src/ui/ShortcutHub.cpp b/src/ui/ShortcutHub.cpp index 145cfa87..c9ce6cdd 100644 --- a/src/ui/ShortcutHub.cpp +++ b/src/ui/ShortcutHub.cpp @@ -10,11 +10,11 @@ #include <optional> namespace cru::ui { -int ShortcutHub::RegisterShortcut(std::u16string name, ShortcutKeyBind bind, - std::function<bool()> handler) { +int ShortcutHub::RegisterShortcut(Shortcut shortcut) { const int id = current_id_++; - ShortcutInfo info{id, std::move(name), bind, std::move(handler)}; - map_[bind].push_back(std::move(info)); + map_[shortcut.key_bind].push_back({id, std::move(shortcut.name), + shortcut.key_bind, + std::move(shortcut.handler)}); return id; } diff --git a/src/ui/controls/TextControlService.hpp b/src/ui/controls/TextControlService.hpp index 5d8d4645..c1a879f6 100644 --- a/src/ui/controls/TextControlService.hpp +++ b/src/ui/controls/TextControlService.hpp @@ -8,6 +8,7 @@ #include "cru/platform/native/UiApplication.hpp" #include "cru/platform/native/Window.hpp" #include "cru/ui/Control.hpp" +#include "cru/ui/ShortcutHub.hpp" #include "cru/ui/UiEvent.hpp" #include "cru/ui/UiHost.hpp" #include "cru/ui/render/CanvasRenderObject.hpp" @@ -32,12 +33,7 @@ class TextControlService : public Object { CRU_DELETE_COPY(TextControlService) CRU_DELETE_MOVE(TextControlService) - ~TextControlService() override { - const auto application = GetUiApplication(); - // Don't call TearDownCaret, because it use text render object of control, - // which may be destroyed already. - application->CancelTimer(this->caret_timer_id_); - } + ~TextControlService() = default; public: bool IsEnabled() { return enable_; } @@ -46,13 +42,13 @@ class TextControlService : public Object { if (enable == this->enable_) return; this->enable_ = enable; if (enable) { - this->SetupHandlers(); + this->SetUpHandlers(); if (this->caret_visible_) { this->SetupCaret(); } } else { this->AbortSelection(); - this->event_revoker_guards_.clear(); + this->TearDownHandlers(); this->TearDownCaret(); } } @@ -162,19 +158,14 @@ class TextControlService : public Object { void SetupCaret() { const auto application = GetUiApplication(); - - // Cancel first anyhow for safety. - application->CancelTimer(this->caret_timer_id_); - this->GetTextRenderObject()->SetDrawCaret(true); - this->caret_timer_id_ = application->SetInterval( + this->caret_timer_canceler_.Reset(application->SetInterval( std::chrono::milliseconds(this->caret_blink_duration_), - [this] { this->GetTextRenderObject()->ToggleDrawCaret(); }); + [this] { this->GetTextRenderObject()->ToggleDrawCaret(); })); } void TearDownCaret() { - const auto application = GetUiApplication(); - application->CancelTimer(this->caret_timer_id_); + this->caret_timer_canceler_.Reset(); this->GetTextRenderObject()->SetDrawCaret(false); } @@ -198,15 +189,6 @@ class TextControlService : public Object { } } - template <typename TArgs> - void SetupOneHandler(event::RoutedEvent<TArgs>* (Control::*event)(), - void (TextControlService::*handler)( - typename event::RoutedEvent<TArgs>::EventArgs)) { - this->event_revoker_guards_.push_back( - EventRevokerGuard{(this->control_->*event)()->Direct()->AddHandler( - std::bind(handler, this, std::placeholders::_1))}); - } - void StartSelection(Index start) { SetSelection(start); log::TagDebug(log_tag, u"Text selection started, position: {}.", start); @@ -220,8 +202,16 @@ class TextControlService : public Object { selection.GetStart(), selection.GetEnd()); } - void SetupHandlers() { - Expects(event_revoker_guards_.empty()); + template <typename TArgs> + void SetupOneHandler(event::RoutedEvent<TArgs>* (Control::*event)(), + void (TextControlService::*handler)( + typename event::RoutedEvent<TArgs>::EventArgs)) { + this->event_guard_ += (this->control_->*event)()->Direct()->AddHandler( + std::bind(handler, this, std::placeholders::_1)); + } + + void SetUpHandlers() { + Expects(event_guard_.IsEmpty()); SetupOneHandler(&Control::MouseMoveEvent, &TextControlService::MouseMoveHandler); @@ -231,11 +221,17 @@ class TextControlService : public Object { &TextControlService::MouseUpHandler); SetupOneHandler(&Control::KeyDownEvent, &TextControlService::KeyDownHandler); - SetupOneHandler(&Control::KeyUpEvent, &TextControlService::KeyUpHandler); SetupOneHandler(&Control::GainFocusEvent, &TextControlService::GainFocusHandler); SetupOneHandler(&Control::LoseFocusEvent, &TextControlService::LoseFocusHandler); + + shortcut_hub_.Install(control_); + } + + void TearDownHandlers() { + event_guard_.Clear(); + shortcut_hub_.Uninstall(); } void MouseMoveHandler(event::MouseEventArgs& args) { @@ -344,11 +340,11 @@ class TextControlService : public Object { this->SetSelection(new_position); } } break; + default: + break; } } - void KeyUpHandler(event::KeyEventArgs& args) { CRU_UNUSED(args); } - void GainFocusHandler(event::FocusChangeEventArgs& args) { CRU_UNUSED(args); if (editable_) { @@ -383,7 +379,7 @@ class TextControlService : public Object { private: gsl::not_null<TControl*> control_; - std::vector<EventRevokerGuard> event_revoker_guards_; + EventRevokerListGuard event_guard_; std::u16string text_; TextRange selection_; @@ -392,9 +388,11 @@ class TextControlService : public Object { bool editable_ = false; bool caret_visible_ = false; - long long caret_timer_id_ = -1; + platform::native::TimerAutoCanceler caret_timer_canceler_; int caret_blink_duration_ = k_default_caret_blink_duration; + ShortcutHub shortcut_hub_; + // nullopt means not selecting std::optional<MouseButton> select_down_button_; |