aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp4
-rw-r--r--src/ui/CMakeLists.txt2
-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
-rw-r--r--src/ui/render/render_object.cpp21
-rw-r--r--src/ui/render/text_render_object.cpp13
-rw-r--r--src/ui/ui_manager.cpp5
-rw-r--r--src/win/graph/direct/font.cpp2
-rw-r--r--src/win/graph/direct/text_layout.cpp6
10 files changed, 208 insertions, 10 deletions
diff --git a/src/main.cpp b/src/main.cpp
index e2a766dc..fb2222d4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,6 +47,10 @@ int main() {
stack_layout->AddChild(text_block3, 1);
flex_layout->AddChild(stack_layout, 1);
+ const auto text_block4 = TextBlock::Create();
+ text_block4->SetText("Hello World!!!");
+ flex_layout->AddChild(text_block4, 2);
+
window->ResolveNativeWindow()->SetVisible(true);
return application->Run();
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 2220740e..dbd42d38 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -18,6 +18,7 @@ add_library(cru_ui STATIC
controls/stack_layout.cpp
controls/text_block.cpp
controls/text_box.cpp
+ controls/text_common.cpp
render/border_render_object.cpp
render/canvas_render_object.cpp
render/flex_layout_render_object.cpp
@@ -43,6 +44,7 @@ target_sources(cru_ui PUBLIC
${CRU_UI_INCLUDE_DIR}/controls/stack_layout.hpp
${CRU_UI_INCLUDE_DIR}/controls/text_box.hpp
${CRU_UI_INCLUDE_DIR}/controls/text_block.hpp
+ ${CRU_UI_INCLUDE_DIR}/controls/text_common.hpp
${CRU_UI_INCLUDE_DIR}/render/border_render_object.hpp
${CRU_UI_INCLUDE_DIR}/render/canvas_render_object.hpp
${CRU_UI_INCLUDE_DIR}/render/flex_layout_render_object.hpp
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
diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp
index 54c2d1b4..5cce6d0d 100644
--- a/src/ui/render/render_object.cpp
+++ b/src/ui/render/render_object.cpp
@@ -35,6 +35,27 @@ void RenderObject::RemoveChild(const int position) {
OnRemoveChild(removed_child, position);
}
+Point RenderObject::GetTotalOffset() const {
+ Point result{};
+ const RenderObject* render_object = this;
+
+ while (render_object != nullptr) {
+ const auto o = render_object->GetOffset();
+ result.x += o.x;
+ result.y += o.y;
+ render_object = render_object->GetParent();
+ }
+
+ return result;
+}
+
+Point RenderObject::FromRootToContent(const Point& point) const {
+ const auto offset = GetTotalOffset();
+ const auto rect = GetContentRect();
+ return Point{point.x - (offset.x + rect.left),
+ point.y - (offset.y + rect.top)};
+}
+
void RenderObject::Measure(const Size& available_size) {
OnMeasureCore(available_size);
}
diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp
index 6048f731..d14a46e2 100644
--- a/src/ui/render/text_render_object.cpp
+++ b/src/ui/render/text_render_object.cpp
@@ -47,6 +47,19 @@ void TextRenderObject::SetFont(std::shared_ptr<platform::graph::IFont> font) {
text_layout_->SetFont(std::move(font));
}
+std::vector<Rect> TextRenderObject::TextRangeRect(const TextRange& text_range) {
+ return text_layout_->TextRangeRect(text_range);
+}
+
+Point TextRenderObject::TextSingleRect(int position, bool trailing) {
+ return text_layout_->TextSingleRect(position, trailing);
+}
+
+platform::graph::TextHitTestResult TextRenderObject::TextHitTest(
+ const Point& point) {
+ return text_layout_->HitTest(point);
+}
+
void TextRenderObject::Draw(platform::graph::IPainter* painter) {
platform::graph::util::WithTransform(
painter,
diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp
index f71f900f..2a6d6e5b 100644
--- a/src/ui/ui_manager.cpp
+++ b/src/ui/ui_manager.cpp
@@ -32,9 +32,12 @@ UiManager::UiManager() {
theme_resource_.default_font = factory->CreateFont("等线", 24.0f);
- theme_resource_.text_brush = CreateSolidColorBrush(factory, colors::black);
+ const auto black_brush = std::shared_ptr<platform::graph::ISolidColorBrush>(
+ CreateSolidColorBrush(factory, colors::black));
+ theme_resource_.text_brush = black_brush;
theme_resource_.text_selection_brush =
CreateSolidColorBrush(factory, colors::skyblue);
+ theme_resource_.caret_brush = black_brush;
theme_resource_.button_style.normal.border_brush =
CreateSolidColorBrush(factory, Color::FromHex(0x00bfff));
diff --git a/src/win/graph/direct/font.cpp b/src/win/graph/direct/font.cpp
index 8e881f84..6bd43078 100644
--- a/src/win/graph/direct/font.cpp
+++ b/src/win/graph/direct/font.cpp
@@ -29,4 +29,6 @@ DWriteFont::DWriteFont(DirectGraphFactory* factory,
ThrowIfFailed(
text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
}
+
+float DWriteFont::GetFontSize() { return text_format_->GetFontSize(); }
} // namespace cru::platform::graph::win::direct
diff --git a/src/win/graph/direct/text_layout.cpp b/src/win/graph/direct/text_layout.cpp
index ecee1727..59101163 100644
--- a/src/win/graph/direct/text_layout.cpp
+++ b/src/win/graph/direct/text_layout.cpp
@@ -1,5 +1,6 @@
#include "cru/win/graph/direct/text_layout.hpp"
+#include "cru/common/logger.hpp"
#include "cru/platform/check.hpp"
#include "cru/win/graph/direct/exception.hpp"
#include "cru/win/graph/direct/factory.hpp"
@@ -92,9 +93,8 @@ std::vector<Rect> DWriteTextLayout::TextRangeRect(const TextRange& text_range) {
result.reserve(actual_count);
for (const auto& metrics : hit_test_metrics) {
- result.push_back(Rect{metrics.left, metrics.top,
- metrics.left + metrics.width,
- metrics.top + metrics.height});
+ result.push_back(
+ Rect{metrics.left, metrics.top, metrics.width, metrics.height});
}
return result;