aboutsummaryrefslogtreecommitdiff
path: root/src/ui/controls
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/controls')
-rw-r--r--src/ui/controls/text_block.cpp44
-rw-r--r--src/ui/controls/text_box.cpp8
-rw-r--r--src/ui/controls/text_common.cpp113
3 files changed, 159 insertions, 6 deletions
diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp
index 9d01dee9..be064ea0 100644
--- a/src/ui/controls/text_block.cpp
+++ b/src/ui/controls/text_block.cpp
@@ -1,28 +1,60 @@
#include "cru/ui/controls/text_block.hpp"
+#include "cru/ui/render/canvas_render_object.hpp"
+#include "cru/ui/render/stack_layout_render_object.hpp"
#include "cru/ui/render/text_render_object.hpp"
#include "cru/ui/ui_manager.hpp"
namespace cru::ui::controls {
+using render::CanvasRenderObject;
+using render::StackLayoutRenderObject;
using render::TextRenderObject;
-TextBlock::TextBlock() {
+TextBlock::TextBlock()
+ : root_render_object_(new StackLayoutRenderObject()),
+ text_render_object_(),
+ caret_render_object_(new CanvasRenderObject()) {
const auto theme_resources = UiManager::GetInstance()->GetThemeResources();
- render_object_ = std::make_unique<TextRenderObject>(
+
+ text_render_object_ = std::make_unique<TextRenderObject>(
theme_resources->text_brush, theme_resources->default_font,
theme_resources->text_selection_brush);
- render_object_->SetAttachedControl(this);
+
+ root_render_object_->AddChild(text_render_object_.get(), 0);
+ root_render_object_->AddChild(caret_render_object_.get(), 1);
+
+ root_render_object_->SetAttachedControl(this);
+ text_render_object_->SetAttachedControl(this);
+ caret_render_object_->SetAttachedControl(this);
+
+ caret_brush_ = theme_resources->caret_brush;
+
+ service_ = std::make_unique<TextControlService>(this, this);
}
TextBlock::~TextBlock() = default;
render::RenderObject* TextBlock::GetRenderObject() const {
- return render_object_.get();
+ return root_render_object_.get();
}
-std::string TextBlock::GetText() const { return render_object_->GetText(); }
+std::string TextBlock::GetText() const {
+ return text_render_object_->GetText();
+}
void TextBlock::SetText(std::string text) {
- render_object_->SetText(std::move(text));
+ text_render_object_->SetText(std::move(text));
+}
+
+render::TextRenderObject* TextBlock::GetTextRenderObject() {
+ return text_render_object_.get();
+}
+
+render::CanvasRenderObject* TextBlock::GetCaretRenderObject() {
+ return caret_render_object_.get();
+}
+
+platform::graph::IBrush* TextBlock::GetCaretBrush() {
+ return caret_brush_.get();
}
} // namespace cru::ui::controls
diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp
index 07b70b94..9c6fcbe5 100644
--- a/src/ui/controls/text_box.cpp
+++ b/src/ui/controls/text_box.cpp
@@ -2,16 +2,19 @@
#include "cru/ui/render/border_render_object.hpp"
#include "cru/ui/render/canvas_render_object.hpp"
+#include "cru/ui/render/stack_layout_render_object.hpp"
#include "cru/ui/render/text_render_object.hpp"
#include "cru/ui/ui_manager.hpp"
namespace cru::ui::controls {
using render::BorderRenderObject;
using render::CanvasRenderObject;
+using render::StackLayoutRenderObject;
using render::TextRenderObject;
TextBox::TextBox()
: border_render_object_(new BorderRenderObject()),
+ stack_layout_render_object_(new StackLayoutRenderObject()),
text_render_object_(),
caret_render_object_(new CanvasRenderObject()) {
const auto theme_resources = UiManager::GetInstance()->GetThemeResources();
@@ -20,7 +23,12 @@ TextBox::TextBox()
theme_resources->text_brush, theme_resources->default_font,
theme_resources->text_selection_brush);
+ border_render_object_->AddChild(stack_layout_render_object_.get(), 0);
+ stack_layout_render_object_->AddChild(text_render_object_.get(), 0);
+ stack_layout_render_object_->AddChild(caret_render_object_.get(), 1);
+
border_render_object_->SetAttachedControl(this);
+ stack_layout_render_object_->SetAttachedControl(this);
text_render_object_->SetAttachedControl(this);
caret_render_object_->SetAttachedControl(this);
}
diff --git a/src/ui/controls/text_common.cpp b/src/ui/controls/text_common.cpp
new file mode 100644
index 00000000..d6bcdd03
--- /dev/null
+++ b/src/ui/controls/text_common.cpp
@@ -0,0 +1,113 @@
+#include "cru/ui/controls/text_common.hpp"
+
+#include "cru/common/logger.hpp"
+#include "cru/platform/graph/font.hpp"
+#include "cru/platform/graph/painter.hpp"
+#include "cru/platform/native/ui_application.hpp"
+#include "cru/ui/control.hpp"
+#include "cru/ui/render/canvas_render_object.hpp"
+#include "cru/ui/render/text_render_object.hpp"
+
+#include <cassert>
+
+namespace cru::ui::controls {
+using platform::graph::ITextLayout;
+
+constexpr float caret_width = 2;
+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();
+ }
+ })});
+
+ 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() { TearDownCaretTimer(); }
+
+void TextControlService::SetCaretVisible(bool visible) {
+ if (visible) {
+ SetupCaretTimer();
+ } else {
+ TearDownCaretTimer();
+ }
+}
+
+void TextControlService::DrawCaret(platform::graph::IPainter* painter) {
+ if (caret_show_) {
+ const auto text_render_object = text_control_->GetTextRenderObject();
+ const auto point = text_render_object->TextSingleRect(
+ caret_position_, false); // Maybe cache the result???
+ painter->FillRectangle(
+ Rect{point,
+ Size{caret_width, text_render_object->GetFont()->GetFontSize()}},
+ text_control_->GetCaretBrush());
+ }
+}
+
+void TextControlService::SetupCaretTimer() {
+ if (caret_visible_) return;
+
+ caret_visible_ = true;
+ caret_timer_tag_ =
+ platform::native::IUiApplication::GetInstance()->SetInterval(
+ std::chrono::milliseconds(caret_blink_duration), [this] {
+ this->caret_show_ = !this->caret_show_;
+ this->text_control_->GetCaretRenderObject()->InvalidatePaint();
+ });
+}
+
+void TextControlService::TearDownCaretTimer() {
+ if (!caret_visible_) return;
+ caret_visible_ = false;
+ platform::native::IUiApplication::GetInstance()->CancelTimer(
+ caret_timer_tag_);
+}
+} // namespace cru::ui::controls