aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-10-28 16:58:56 +0800
committercrupest <crupest@outlook.com>2020-10-28 16:58:56 +0800
commit2df47ffbfff02fb6b64d19e404adc41a93677afe (patch)
treee15a566c95b68b6266bdcab14bba64be729ab300
parentfde24556042b76863bdec34dcc213cb7298f68f9 (diff)
downloadcru-2df47ffbfff02fb6b64d19e404adc41a93677afe.tar.gz
cru-2df47ffbfff02fb6b64d19e404adc41a93677afe.tar.bz2
cru-2df47ffbfff02fb6b64d19e404adc41a93677afe.zip
...
-rw-r--r--include/cru/platform/native/UiApplication.hpp55
-rw-r--r--include/cru/ui/ShortcutHub.hpp20
-rw-r--r--src/ui/ShortcutHub.cpp8
-rw-r--r--src/ui/controls/TextControlService.hpp62
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_;