aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/ui/controls/text_common.hpp17
-rw-r--r--src/ui/controls/text_block.cpp1
-rw-r--r--src/ui/controls/text_common.cpp168
3 files changed, 128 insertions, 58 deletions
diff --git a/include/cru/ui/controls/text_common.hpp b/include/cru/ui/controls/text_common.hpp
index 7398764f..fbef6b06 100644
--- a/include/cru/ui/controls/text_common.hpp
+++ b/include/cru/ui/controls/text_common.hpp
@@ -33,6 +33,9 @@ class TextControlService : public Object {
~TextControlService();
public:
+ bool IsEnabled() { return enable_; }
+ void SetEnabled(bool enable);
+
int GetCaretPosition() { return caret_position_; }
void SetCaretPosition(int position) { caret_position_ = position; }
@@ -43,16 +46,30 @@ class TextControlService : public Object {
void DrawCaret(platform::graph::IPainter* painter);
private:
+ void AbortSelection();
+
void SetupCaretTimer();
void TearDownCaretTimer();
+ void SetupHandlers();
+
+ void MouseMoveHandler(event::MouseEventArgs& args);
+ void MouseDownHandler(event::MouseButtonEventArgs& args);
+ void MouseUpHandler(event::MouseButtonEventArgs& args);
+ void LoseFocusHandler(event::FocusChangeEventArgs& args);
+
private:
Control* control_;
ITextControl* text_control_;
std::vector<EventRevokerGuard> event_revoker_guards_;
+ bool enable_ = false;
+
bool caret_visible_ = false;
int caret_position_ = 0;
+#ifdef CRU_DEBUG
+ bool caret_timer_set_ = false;
+#endif
unsigned long caret_timer_tag_;
// this is used for blinking of caret
bool caret_show_ = true;
diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp
index be064ea0..ece599e8 100644
--- a/src/ui/controls/text_block.cpp
+++ b/src/ui/controls/text_block.cpp
@@ -30,6 +30,7 @@ TextBlock::TextBlock()
caret_brush_ = theme_resources->caret_brush;
service_ = std::make_unique<TextControlService>(this, this);
+ service_->SetEnabled(true);
}
TextBlock::~TextBlock() = default;
diff --git a/src/ui/controls/text_common.cpp b/src/ui/controls/text_common.cpp
index d6bcdd03..fdfdcc2c 100644
--- a/src/ui/controls/text_common.cpp
+++ b/src/ui/controls/text_common.cpp
@@ -18,67 +18,39 @@ constexpr long long caret_blink_duration = 500;
TextControlService::TextControlService(Control* control,
ITextControl* text_control)
- : control_(control), text_control_(text_control) {
- event_revoker_guards_.push_back(
- EventRevokerGuard{control->MouseDownEvent()->Direct()->AddHandler(
- [this](event::MouseButtonEventArgs& args) {
- if (this->select_down_button_.has_value()) {
- return;
- } else {
- if (!this->control_->CaptureMouse()) return;
- const auto text_render_object =
- this->text_control_->GetTextRenderObject();
- this->select_down_button_ = args.GetButton();
- const auto result = text_render_object->TextHitTest(
- text_render_object->FromRootToContent(args.GetPoint()));
- const auto position = result.position + (result.trailing ? 1 : 0);
- this->select_start_position_ = position;
- log::Debug("Begin to select text. Position: {}", position);
- }
- })});
-
- event_revoker_guards_.push_back(
- EventRevokerGuard{control->MouseMoveEvent()->Direct()->AddHandler(
- [this](event::MouseEventArgs& args) {
- if (this->select_down_button_.has_value()) {
- const auto text_render_object =
- this->text_control_->GetTextRenderObject();
- const auto result = text_render_object->TextHitTest(
- text_render_object->FromRootToContent(args.GetPoint()));
- const auto position = result.position + (result.trailing ? 1 : 0);
- this->caret_position_ = position;
- log::Debug("Text select range: {} : {}.", position,
- this->select_start_position_);
- this->text_control_->GetTextRenderObject()->SetSelectionRange(
- TextRange::FromTwoSides(
- static_cast<unsigned>(position),
- static_cast<unsigned>(this->select_start_position_)));
- this->text_control_->GetTextRenderObject()->InvalidatePaint();
- this->text_control_->GetCaretRenderObject()->InvalidatePaint();
- }
- })});
+ : control_(control), text_control_(text_control) {}
- event_revoker_guards_.push_back(
- EventRevokerGuard{control->MouseUpEvent()->Direct()->AddHandler(
- [this](event::MouseButtonEventArgs& args) {
- if (this->select_down_button_.has_value() &&
- this->select_down_button_.value() == args.GetButton()) {
- this->control_->ReleaseMouse();
- this->select_down_button_ = std::nullopt;
- log::Debug("End selecting text.");
- }
- })});
+TextControlService::~TextControlService() {
+ if (enable_ && caret_visible_) TearDownCaretTimer();
}
-TextControlService::~TextControlService() { TearDownCaretTimer(); }
+void TextControlService::SetEnabled(bool enable) {
+ if (enable == enable_) return;
+ if (enable) {
+ AbortSelection();
+ SetupHandlers();
+ if (caret_visible_) {
+ SetupCaretTimer();
+ }
+ } else {
+ event_revoker_guards_.clear();
+ if (caret_visible_) {
+ TearDownCaretTimer();
+ }
+ }
+}
void TextControlService::SetCaretVisible(bool visible) {
- if (visible) {
- SetupCaretTimer();
+ if (visible == caret_visible_) return;
+
+ if (enable_) {
+ if (visible) {
+ SetupCaretTimer();
+ }
} else {
TearDownCaretTimer();
}
-}
+} // namespace cru::ui::controls
void TextControlService::DrawCaret(platform::graph::IPainter* painter) {
if (caret_show_) {
@@ -92,10 +64,19 @@ void TextControlService::DrawCaret(platform::graph::IPainter* painter) {
}
}
-void TextControlService::SetupCaretTimer() {
- if (caret_visible_) return;
+void TextControlService::AbortSelection() {
+ if (select_down_button_.has_value()) {
+ control_->ReleaseMouse();
+ select_down_button_ = std::nullopt;
+ }
+ text_control_->GetTextRenderObject()->SetSelectionRange(std::nullopt);
+}
- caret_visible_ = true;
+void TextControlService::SetupCaretTimer() {
+#ifdef CRU_DEBUG
+ assert(!caret_timer_set_);
+ caret_timer_set_ = true;
+#endif
caret_timer_tag_ =
platform::native::IUiApplication::GetInstance()->SetInterval(
std::chrono::milliseconds(caret_blink_duration), [this] {
@@ -105,9 +86,80 @@ void TextControlService::SetupCaretTimer() {
}
void TextControlService::TearDownCaretTimer() {
- if (!caret_visible_) return;
- caret_visible_ = false;
+#ifdef CRU_DEBUG
+ assert(!caret_timer_set_);
+ caret_timer_set_ = false;
+#endif
platform::native::IUiApplication::GetInstance()->CancelTimer(
caret_timer_tag_);
}
+
+void TextControlService::SetupHandlers() {
+ assert(event_revoker_guards_.empty());
+ event_revoker_guards_.push_back(
+ EventRevokerGuard{control_->MouseMoveEvent()->Direct()->AddHandler(
+ std::bind(&TextControlService::MouseMoveHandler, this,
+ std::placeholders::_1))});
+ event_revoker_guards_.push_back(
+ EventRevokerGuard{control_->MouseDownEvent()->Direct()->AddHandler(
+ std::bind(&TextControlService::MouseDownHandler, this,
+ std::placeholders::_1))});
+ event_revoker_guards_.push_back(EventRevokerGuard{
+ control_->MouseUpEvent()->Direct()->AddHandler(std::bind(
+ &TextControlService::MouseUpHandler, this, std::placeholders::_1))});
+ event_revoker_guards_.push_back(
+ EventRevokerGuard{control_->LoseFocusEvent()->Direct()->AddHandler(
+ std::bind(&TextControlService::LoseFocusHandler, this,
+ std::placeholders::_1))});
+}
+
+void TextControlService::MouseMoveHandler(event::MouseEventArgs& args) {
+ if (this->select_down_button_.has_value()) {
+ const auto text_render_object = this->text_control_->GetTextRenderObject();
+ const auto result = text_render_object->TextHitTest(
+ text_render_object->FromRootToContent(args.GetPoint()));
+ const auto position = result.position + (result.trailing ? 1 : 0);
+ this->caret_position_ = position;
+ log::Debug(
+ "TextControlService: Text selection changed on mouse move, range: {}, "
+ "{}.",
+ position, this->select_start_position_);
+ this->text_control_->GetTextRenderObject()->SetSelectionRange(
+ TextRange::FromTwoSides(
+ static_cast<unsigned>(position),
+ static_cast<unsigned>(this->select_start_position_)));
+ this->text_control_->GetTextRenderObject()->InvalidatePaint();
+ this->text_control_->GetCaretRenderObject()->InvalidatePaint();
+ }
+}
+
+void TextControlService::MouseDownHandler(event::MouseButtonEventArgs& args) {
+ if (this->select_down_button_.has_value()) {
+ return;
+ } else {
+ if (!this->control_->CaptureMouse()) return;
+ if (!this->control_->RequestFocus()) return;
+ const auto text_render_object = this->text_control_->GetTextRenderObject();
+ this->select_down_button_ = args.GetButton();
+ const auto result = text_render_object->TextHitTest(
+ text_render_object->FromRootToContent(args.GetPoint()));
+ const auto position = result.position + (result.trailing ? 1 : 0);
+ this->select_start_position_ = position;
+ log::Debug("TextControlService: Begin to select text, start position: {}.",
+ position);
+ }
+}
+
+void TextControlService::MouseUpHandler(event::MouseButtonEventArgs& args) {
+ if (this->select_down_button_.has_value() &&
+ this->select_down_button_.value() == args.GetButton()) {
+ this->control_->ReleaseMouse();
+ this->select_down_button_ = std::nullopt;
+ log::Debug("TextControlService: End selecting text.");
+ }
+}
+
+void TextControlService::LoseFocusHandler(event::FocusChangeEventArgs& args) {
+ if (!args.IsWindow()) this->AbortSelection();
+}
} // namespace cru::ui::controls