diff options
Diffstat (limited to 'src/ui/controls')
-rw-r--r-- | src/ui/controls/text_block.cpp | 44 | ||||
-rw-r--r-- | src/ui/controls/text_box.cpp | 8 | ||||
-rw-r--r-- | src/ui/controls/text_common.cpp | 113 |
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 |