aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-04-24 00:03:16 +0800
committercrupest <crupest@outlook.com>2020-04-24 00:03:16 +0800
commit75ff8a6a05afd02aaadf7e3049b0a0e305241182 (patch)
tree5444bbb3ef80036cc38a827b8ccf03f48b310728
parent922d7f6c96f81a33538900f8a8992a5b6f640874 (diff)
downloadcru-75ff8a6a05afd02aaadf7e3049b0a0e305241182.tar.gz
cru-75ff8a6a05afd02aaadf7e3049b0a0e305241182.tar.bz2
cru-75ff8a6a05afd02aaadf7e3049b0a0e305241182.zip
...
-rw-r--r--demos/input_method/main.cpp2
-rw-r--r--include/cru/platform/graph/text_layout.hpp2
-rw-r--r--include/cru/platform/native/ui_application.hpp15
-rw-r--r--include/cru/ui/controls/text_block.hpp5
-rw-r--r--include/cru/ui/controls/text_box.hpp5
-rw-r--r--include/cru/ui/render/text_render_object.hpp40
-rw-r--r--include/cru/win/graph/direct/text_layout.hpp2
-rw-r--r--include/cru/win/native/ui_application.hpp10
-rw-r--r--src/ui/controls/text_block.cpp24
-rw-r--r--src/ui/controls/text_box.cpp27
-rw-r--r--src/ui/controls/text_control_service.hpp111
-rw-r--r--src/ui/render/text_render_object.cpp88
-rw-r--r--src/win/graph/direct/text_layout.cpp2
-rw-r--r--src/win/native/ui_application.cpp15
14 files changed, 193 insertions, 155 deletions
diff --git a/demos/input_method/main.cpp b/demos/input_method/main.cpp
index a3ac1b96..74654a2d 100644
--- a/demos/input_method/main.cpp
+++ b/demos/input_method/main.cpp
@@ -92,7 +92,7 @@ int main() {
gsl::narrow_cast<int>(committed_text.size());
const auto cursor_lefttop =
- text_layout->TextSingleRect(cursor_pos, false);
+ text_layout->TextSinglePoint(cursor_pos, false);
painter->FillRectangle(Rect{cursor_lefttop.x, cursor_lefttop.y + anchor_y,
3, font->GetFontSize()},
diff --git a/include/cru/platform/graph/text_layout.hpp b/include/cru/platform/graph/text_layout.hpp
index 0d104742..d91834c0 100644
--- a/include/cru/platform/graph/text_layout.hpp
+++ b/include/cru/platform/graph/text_layout.hpp
@@ -17,7 +17,7 @@ struct ITextLayout : virtual IGraphResource {
virtual Rect GetTextBounds() = 0;
virtual std::vector<Rect> TextRangeRect(const TextRange& text_range) = 0;
- virtual Point TextSingleRect(gsl::index position, bool trailing) = 0;
+ virtual Point TextSinglePoint(gsl::index position, bool trailing) = 0;
virtual TextHitTestResult HitTest(const Point& point) = 0;
};
} // namespace cru::platform::graph
diff --git a/include/cru/platform/native/ui_application.hpp b/include/cru/platform/native/ui_application.hpp
index 006255db..afcc7117 100644
--- a/include/cru/platform/native/ui_application.hpp
+++ b/include/cru/platform/native/ui_application.hpp
@@ -32,11 +32,16 @@ struct IUiApplication : public virtual INativeResource {
virtual void AddOnQuitHandler(std::function<void()> handler) = 0;
virtual void InvokeLater(std::function<void()> action) = 0;
- virtual unsigned long SetTimeout(std::chrono::milliseconds milliseconds,
- std::function<void()> action) = 0;
- virtual unsigned long SetInterval(std::chrono::milliseconds milliseconds,
- std::function<void()> action) = 0;
- virtual void CancelTimer(unsigned long id) = 0;
+ // Timer id should always be positive and never the same. So it's ok to use
+ // negative value to represent no timer.
+ virtual long long SetTimeout(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) = 0;
+ virtual long long SetInterval(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) = 0;
+ // Implementation should guarantee calls on timer id already canceled have no
+ // effects and do not crash. Also canceling negative id should always result
+ // in no-op.
+ virtual void CancelTimer(long long id) = 0;
virtual std::vector<INativeWindow*> GetAllWindow() = 0;
virtual std::shared_ptr<INativeWindowResolver> CreateWindow(
diff --git a/include/cru/ui/controls/text_block.hpp b/include/cru/ui/controls/text_block.hpp
index ec61a08c..61f568c4 100644
--- a/include/cru/ui/controls/text_block.hpp
+++ b/include/cru/ui/controls/text_block.hpp
@@ -29,14 +29,9 @@ class TextBlock : public NoChildControl {
void SetText(std::string text);
render::TextRenderObject* GetTextRenderObject();
- render::CanvasRenderObject* GetCaretRenderObject();
- std::shared_ptr<platform::graph::IBrush> GetCaretBrush();
private:
- std::unique_ptr<render::StackLayoutRenderObject> root_render_object_;
std::unique_ptr<render::TextRenderObject> text_render_object_;
- std::unique_ptr<render::CanvasRenderObject> caret_render_object_;
- std::shared_ptr<platform::graph::IBrush> caret_brush_;
std::unique_ptr<TextControlService<TextBlock>> service_;
};
diff --git a/include/cru/ui/controls/text_box.hpp b/include/cru/ui/controls/text_box.hpp
index f5347430..c0160658 100644
--- a/include/cru/ui/controls/text_box.hpp
+++ b/include/cru/ui/controls/text_box.hpp
@@ -22,8 +22,6 @@ class TextBox : public NoChildControl {
std::string_view GetControlType() const final { return control_type; }
render::TextRenderObject* GetTextRenderObject();
- render::CanvasRenderObject* GetCaretRenderObject();
- std::shared_ptr<platform::graph::IBrush> GetCaretBrush();
const TextBoxBorderStyle& GetBorderStyle();
void SetBorderStyle(TextBoxBorderStyle border_style);
@@ -36,11 +34,8 @@ class TextBox : public NoChildControl {
private:
std::unique_ptr<render::BorderRenderObject> border_render_object_;
- std::unique_ptr<render::StackLayoutRenderObject> stack_layout_render_object_;
std::unique_ptr<render::TextRenderObject> text_render_object_;
- std::unique_ptr<render::CanvasRenderObject> caret_render_object_;
- std::shared_ptr<platform::graph::IBrush> caret_brush_;
TextBoxBorderStyle border_style_;
std::unique_ptr<TextControlService<TextBox>> service_;
diff --git a/include/cru/ui/render/text_render_object.hpp b/include/cru/ui/render/text_render_object.hpp
index c062c9f0..4b1e91e0 100644
--- a/include/cru/ui/render/text_render_object.hpp
+++ b/include/cru/ui/render/text_render_object.hpp
@@ -6,9 +6,13 @@
namespace cru::ui::render {
class TextRenderObject : public RenderObject {
public:
+ constexpr static float default_caret_width = 2;
+
+ public:
TextRenderObject(std::shared_ptr<platform::graph::IBrush> brush,
std::shared_ptr<platform::graph::IFont> font,
- std::shared_ptr<platform::graph::IBrush> selection_brush);
+ std::shared_ptr<platform::graph::IBrush> selection_brush,
+ std::shared_ptr<platform::graph::IBrush> caret_brush);
TextRenderObject(const TextRenderObject& other) = delete;
TextRenderObject(TextRenderObject&& other) = delete;
TextRenderObject& operator=(const TextRenderObject& other) = delete;
@@ -19,30 +23,41 @@ class TextRenderObject : public RenderObject {
void SetText(std::string new_text);
std::shared_ptr<platform::graph::IBrush> GetBrush() const { return brush_; }
- void SetBrush(std::shared_ptr<platform::graph::IBrush> new_brush) {
- new_brush.swap(brush_);
- }
+ void SetBrush(std::shared_ptr<platform::graph::IBrush> new_brush);
std::shared_ptr<platform::graph::IFont> GetFont() const;
void SetFont(std::shared_ptr<platform::graph::IFont> font);
std::vector<Rect> TextRangeRect(const TextRange& text_range);
- Point TextSingleRect(gsl::index position, bool trailing);
+ Point TextSinglePoint(gsl::index position, bool trailing);
platform::graph::TextHitTestResult TextHitTest(const Point& point);
std::optional<TextRange> GetSelectionRange() const {
return selection_range_;
}
- void SetSelectionRange(std::optional<TextRange> new_range) {
- selection_range_ = std::move(new_range);
- }
+ void SetSelectionRange(std::optional<TextRange> new_range);
std::shared_ptr<platform::graph::IBrush> GetSelectionBrush() const {
return selection_brush_;
}
- void SetSelectionBrush(std::shared_ptr<platform::graph::IBrush> new_brush) {
- new_brush.swap(selection_brush_);
+ void SetSelectionBrush(std::shared_ptr<platform::graph::IBrush> new_brush);
+
+ bool IsDrawCaret() const { return draw_caret_; }
+ void SetDrawCaret(bool draw_caret);
+ void ToggleDrawCaret() { SetDrawCaret(!IsDrawCaret()); }
+
+ // Caret position can be any value. When it is negative, 0 is used. When it
+ // exceeds the size of the string, the size of the string is used.
+ gsl::index GetCaretPosition() const { return caret_position_; }
+ void SetCaretPosition(gsl::index position);
+
+ std::shared_ptr<platform::graph::IBrush> GetCaretBrush() const {
+ return caret_brush_;
}
+ void GetCaretBrush(std::shared_ptr<platform::graph::IBrush> brush);
+
+ float GetCaretWidth() const { return caret_width_; }
+ void SetCaretWidth(float width);
void Draw(platform::graph::IPainter* painter) override;
@@ -61,5 +76,10 @@ class TextRenderObject : public RenderObject {
std::optional<TextRange> selection_range_ = std::nullopt;
std::shared_ptr<platform::graph::IBrush> selection_brush_;
+
+ bool draw_caret_ = false;
+ gsl::index caret_position_ = 0;
+ std::shared_ptr<platform::graph::IBrush> caret_brush_;
+ float caret_width_ = default_caret_width;
};
} // namespace cru::ui::render
diff --git a/include/cru/win/graph/direct/text_layout.hpp b/include/cru/win/graph/direct/text_layout.hpp
index 90faadf9..2870db96 100644
--- a/include/cru/win/graph/direct/text_layout.hpp
+++ b/include/cru/win/graph/direct/text_layout.hpp
@@ -40,7 +40,7 @@ class DWriteTextLayout : public DirectGraphResource,
// Return empty vector if text_range.count is 0. Text range could be in
// reverse direction, it should be normalized first in implementation.
std::vector<Rect> TextRangeRect(const TextRange& text_range) override;
- Point TextSingleRect(gsl::index position, bool trailing) override;
+ Point TextSinglePoint(gsl::index position, bool trailing) override;
TextHitTestResult HitTest(const Point& point) override;
private:
diff --git a/include/cru/win/native/ui_application.hpp b/include/cru/win/native/ui_application.hpp
index 1c54cc05..250e855c 100644
--- a/include/cru/win/native/ui_application.hpp
+++ b/include/cru/win/native/ui_application.hpp
@@ -33,11 +33,11 @@ class WinUiApplication : public WinNativeResource,
void AddOnQuitHandler(std::function<void()> handler) override;
void InvokeLater(std::function<void()> action) override;
- unsigned long SetTimeout(std::chrono::milliseconds milliseconds,
- std::function<void()> action) override;
- unsigned long SetInterval(std::chrono::milliseconds milliseconds,
- std::function<void()> action) override;
- void CancelTimer(unsigned long id) override;
+ long long SetTimeout(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) override;
+ long long SetInterval(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) override;
+ void CancelTimer(long long id) override;
std::vector<INativeWindow*> GetAllWindow() override;
std::shared_ptr<INativeWindowResolver> CreateWindow(
diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp
index 1c20540d..a3ec9f54 100644
--- a/src/ui/controls/text_block.cpp
+++ b/src/ui/controls/text_block.cpp
@@ -11,24 +11,14 @@ using render::CanvasRenderObject;
using render::StackLayoutRenderObject;
using render::TextRenderObject;
-TextBlock::TextBlock()
- : root_render_object_(new StackLayoutRenderObject()),
- text_render_object_(),
- caret_render_object_(new CanvasRenderObject()) {
+TextBlock::TextBlock() {
const auto theme_resources = UiManager::GetInstance()->GetThemeResources();
text_render_object_ = std::make_unique<TextRenderObject>(
theme_resources->text_brush, theme_resources->default_font,
- theme_resources->text_selection_brush);
+ theme_resources->text_selection_brush, theme_resources->caret_brush);
- 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<TextBlock>>(this);
service_->SetEnabled(true);
@@ -37,7 +27,7 @@ TextBlock::TextBlock()
TextBlock::~TextBlock() = default;
render::RenderObject* TextBlock::GetRenderObject() const {
- return root_render_object_.get();
+ return text_render_object_.get();
}
std::string TextBlock::GetText() const {
@@ -51,12 +41,4 @@ void TextBlock::SetText(std::string text) {
render::TextRenderObject* TextBlock::GetTextRenderObject() {
return text_render_object_.get();
}
-
-render::CanvasRenderObject* TextBlock::GetCaretRenderObject() {
- return caret_render_object_.get();
-}
-
-std::shared_ptr<platform::graph::IBrush> TextBlock::GetCaretBrush() {
- return caret_brush_;
-}
} // namespace cru::ui::controls
diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp
index 99164b9b..7b63eea1 100644
--- a/src/ui/controls/text_box.cpp
+++ b/src/ui/controls/text_box.cpp
@@ -13,29 +13,22 @@ 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()),
- service_(new TextControlService<TextBox>(this)) {
+TextBox::TextBox() : border_render_object_(new BorderRenderObject()) {
const auto theme_resources = UiManager::GetInstance()->GetThemeResources();
- caret_brush_ = theme_resources->caret_brush;
border_style_ = theme_resources->text_box_border_style;
text_render_object_ = std::make_unique<TextRenderObject>(
theme_resources->text_brush, theme_resources->default_font,
- theme_resources->text_selection_brush);
+ theme_resources->text_selection_brush, theme_resources->caret_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_->AddChild(text_render_object_.get(), 0);
border_render_object_->SetAttachedControl(this);
- stack_layout_render_object_->SetAttachedControl(this);
text_render_object_->SetAttachedControl(this);
- caret_render_object_->SetAttachedControl(this);
+
+ service_ = std::make_unique<TextControlService<TextBox>>(this);
+ service_->SetEnabled(true);
GainFocusEvent()->Direct()->AddHandler([this](event::FocusChangeEventArgs&) {
this->service_->SetEnabled(true);
@@ -54,14 +47,6 @@ render::TextRenderObject* TextBox::GetTextRenderObject() {
return text_render_object_.get();
}
-render::CanvasRenderObject* TextBox::GetCaretRenderObject() {
- return caret_render_object_.get();
-}
-
-std::shared_ptr<platform::graph::IBrush> TextBox::GetCaretBrush() {
- return caret_brush_;
-}
-
const TextBoxBorderStyle& TextBox::GetBorderStyle() { return border_style_; }
void TextBox::SetBorderStyle(TextBoxBorderStyle border_style) {
diff --git a/src/ui/controls/text_control_service.hpp b/src/ui/controls/text_control_service.hpp
index 0da8abed..57a6efa7 100644
--- a/src/ui/controls/text_control_service.hpp
+++ b/src/ui/controls/text_control_service.hpp
@@ -1,4 +1,5 @@
#pragma once
+#include "../helper.hpp"
#include "cru/common/logger.hpp"
#include "cru/platform/graph/font.hpp"
#include "cru/platform/graph/painter.hpp"
@@ -15,8 +16,6 @@ constexpr long long caret_blink_duration = 500;
// TControl should inherits `Control` and has following methods:
// ```
// render::TextRenderObject* GetTextRenderObject();
-// render::CanvasRenderObject* GetCaretRenderObject();
-// std::shared_ptr<platform::graph::IBrush> GetCaretBrush();
// ```
template <typename TControl>
class TextControlService : public Object {
@@ -44,8 +43,8 @@ class TextControlService : public Object {
private:
void AbortSelection();
- void SetupCaretTimer();
- void TearDownCaretTimer();
+ void SetupCaret();
+ void TearDownCaret();
void SetupHandlers();
@@ -62,12 +61,7 @@ class TextControlService : public Object {
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;
+ long long caret_timer_id_ = -1;
// nullopt means not selecting
std::optional<MouseButton> select_down_button_;
@@ -82,101 +76,86 @@ TextControlService<TControl>::TextControlService(TControl* control)
template <typename TControl>
TextControlService<TControl>::~TextControlService() {
- if (enable_ && caret_visible_) TearDownCaretTimer();
+ 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_);
}
template <typename TControl>
void TextControlService<TControl>::SetEnabled(bool enable) {
- if (enable == enable_) return;
+ if (enable == this->enable_) return;
if (enable) {
- AbortSelection();
- SetupHandlers();
- if (caret_visible_) {
- SetupCaretTimer();
+ this->SetupHandlers();
+ if (this->caret_visible_) {
+ this->SetupCaret();
}
} else {
- event_revoker_guards_.clear();
- if (caret_visible_) {
- TearDownCaretTimer();
- }
+ this->AbortSelection();
+ this->event_revoker_guards_.clear();
+ this->TearDownCaret();
}
}
template <typename TControl>
void TextControlService<TControl>::SetCaretVisible(bool visible) {
- if (visible == caret_visible_) return;
+ if (visible == this->caret_visible_) return;
+
+ this->caret_visible_ = visible;
- if (enable_) {
+ if (this->enable_) {
if (visible) {
- SetupCaretTimer();
+ this->SetupCaretTimer();
+ } else {
+ this->TearDownCaretTimer();
}
- } else {
- TearDownCaretTimer();
}
} // namespace cru::ui::controls
template <typename TControl>
-void TextControlService<TControl>::DrawCaret(
- platform::graph::IPainter* painter) {
- if (caret_show_) {
- const auto text_render_object = 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()}},
- control_->GetCaretBrush().get());
- }
-}
-
-template <typename TControl>
void TextControlService<TControl>::AbortSelection() {
- if (select_down_button_.has_value()) {
- control_->ReleaseMouse();
- select_down_button_ = std::nullopt;
+ if (this->select_down_button_.has_value()) {
+ this->control_->ReleaseMouse();
+ this->select_down_button_ = std::nullopt;
}
- control_->GetTextRenderObject()->SetSelectionRange(std::nullopt);
+ this->control_->GetTextRenderObject()->SetSelectionRange(std::nullopt);
}
template <typename TControl>
-void TextControlService<TControl>::SetupCaretTimer() {
-#ifdef CRU_DEBUG
- Expects(!caret_timer_set_);
- caret_timer_set_ = true;
-#endif
- caret_timer_tag_ =
- platform::native::IUiApplication::GetInstance()->SetInterval(
- std::chrono::milliseconds(caret_blink_duration), [this] {
- this->caret_show_ = !this->caret_show_;
- this->control_->GetCaretRenderObject()->InvalidatePaint();
- });
+void TextControlService<TControl>::SetupCaret() {
+ const auto application = GetUiApplication();
+
+ // Cancel first anyhow for safety.
+ application->CancelTimer(this->caret_timer_id_);
+
+ this->control_->GetTextRenderObject()->SetDrawCaret(true);
+ this->caret_timer_id_ = application->SetInterval(
+ std::chrono::milliseconds(caret_blink_duration),
+ [this] { this->control_->GetTextRenderObject()->ToggleDrawCaret(); });
}
template <typename TControl>
-void TextControlService<TControl>::TearDownCaretTimer() {
-#ifdef CRU_DEBUG
- Expects(!caret_timer_set_);
- caret_timer_set_ = false;
-#endif
- platform::native::IUiApplication::GetInstance()->CancelTimer(
- caret_timer_tag_);
+void TextControlService<TControl>::TearDownCaret() {
+ const auto application = GetUiApplication();
+ application->CancelTimer(this->caret_timer_id_);
+ this->control_->GetTextRenderObject()->SetDrawCaret(false);
}
template <typename TControl>
void TextControlService<TControl>::SetupHandlers() {
Expects(event_revoker_guards_.empty());
- event_revoker_guards_.push_back(
+ this->event_revoker_guards_.push_back(
EventRevokerGuard{control_->MouseMoveEvent()->Direct()->AddHandler(
std::bind(&TextControlService::MouseMoveHandler, this,
std::placeholders::_1))});
- event_revoker_guards_.push_back(
+ this->event_revoker_guards_.push_back(
EventRevokerGuard{control_->MouseDownEvent()->Direct()->AddHandler(
std::bind(&TextControlService::MouseDownHandler, this,
std::placeholders::_1))});
- event_revoker_guards_.push_back(EventRevokerGuard{
+ this->event_revoker_guards_.push_back(EventRevokerGuard{
control_->MouseUpEvent()->Direct()->AddHandler(std::bind(
&TextControlService::MouseUpHandler, this, std::placeholders::_1))});
- event_revoker_guards_.push_back(
+ this->event_revoker_guards_.push_back(
EventRevokerGuard{control_->LoseFocusEvent()->Direct()->AddHandler(
std::bind(&TextControlService::LoseFocusHandler, this,
std::placeholders::_1))});
@@ -199,8 +178,6 @@ void TextControlService<TControl>::MouseMoveHandler(
TextRange::FromTwoSides(
static_cast<unsigned>(position),
static_cast<unsigned>(this->select_start_position_)));
- this->control_->GetTextRenderObject()->InvalidatePaint();
- this->control_->GetCaretRenderObject()->InvalidatePaint();
}
}
diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp
index e8368480..cd670db1 100644
--- a/src/ui/render/text_render_object.cpp
+++ b/src/ui/render/text_render_object.cpp
@@ -7,22 +7,23 @@
#include <algorithm>
-// TODO: Null Check!!!
-
namespace cru::ui::render {
TextRenderObject::TextRenderObject(
std::shared_ptr<platform::graph::IBrush> brush,
std::shared_ptr<platform::graph::IFont> font,
- std::shared_ptr<platform::graph::IBrush> selection_brush) {
+ std::shared_ptr<platform::graph::IBrush> selection_brush,
+ std::shared_ptr<platform::graph::IBrush> caret_brush) {
Expects(brush);
Expects(font);
Expects(selection_brush);
+ Expects(caret_brush);
SetChildMode(ChildMode::None);
brush.swap(brush_);
font.swap(font_);
selection_brush.swap(selection_brush_);
+ caret_brush.swap(caret_brush_);
const auto graph_factory = GetGraphFactory();
text_layout_ = graph_factory->CreateTextLayout(font_, "");
@@ -38,11 +39,19 @@ void TextRenderObject::SetText(std::string new_text) {
text_layout_->SetText(std::move(new_text));
}
+void TextRenderObject::SetBrush(
+ std::shared_ptr<platform::graph::IBrush> new_brush) {
+ Expects(new_brush);
+ new_brush.swap(brush_);
+ InvalidatePaint();
+}
+
std::shared_ptr<platform::graph::IFont> TextRenderObject::GetFont() const {
return text_layout_->GetFont();
}
void TextRenderObject::SetFont(std::shared_ptr<platform::graph::IFont> font) {
+ Expects(font);
text_layout_->SetFont(std::move(font));
}
@@ -50,8 +59,8 @@ std::vector<Rect> TextRenderObject::TextRangeRect(const TextRange& text_range) {
return text_layout_->TextRangeRect(text_range);
}
-Point TextRenderObject::TextSingleRect(gsl::index position, bool trailing) {
- return text_layout_->TextSingleRect(position, trailing);
+Point TextRenderObject::TextSinglePoint(gsl::index position, bool trailing) {
+ return text_layout_->TextSinglePoint(position, trailing);
}
platform::graph::TextHitTestResult TextRenderObject::TextHitTest(
@@ -59,6 +68,54 @@ platform::graph::TextHitTestResult TextRenderObject::TextHitTest(
return text_layout_->HitTest(point);
}
+void TextRenderObject::SetSelectionRange(std::optional<TextRange> new_range) {
+ selection_range_ = std::move(new_range);
+ InvalidatePaint();
+}
+
+void TextRenderObject::SetSelectionBrush(
+ std::shared_ptr<platform::graph::IBrush> new_brush) {
+ Expects(new_brush);
+ new_brush.swap(selection_brush_);
+ if (selection_range_ && selection_range_->count) {
+ InvalidatePaint();
+ }
+}
+
+void TextRenderObject::SetDrawCaret(bool draw_caret) {
+ if (draw_caret_ != draw_caret) {
+ draw_caret_ = draw_caret;
+ InvalidatePaint();
+ }
+}
+
+void TextRenderObject::SetCaretPosition(gsl::index position) {
+ if (position != caret_position_) {
+ caret_position_ = position;
+ if (draw_caret_) {
+ InvalidatePaint();
+ }
+ }
+}
+
+void TextRenderObject::GetCaretBrush(
+ std::shared_ptr<platform::graph::IBrush> brush) {
+ Expects(brush);
+ brush.swap(caret_brush_);
+ if (draw_caret_) {
+ InvalidatePaint();
+ }
+}
+
+void TextRenderObject::SetCaretWidth(const float width) {
+ Expects(width >= 0.0f);
+
+ caret_width_ = width;
+ if (draw_caret_) {
+ InvalidatePaint();
+ }
+}
+
void TextRenderObject::Draw(platform::graph::IPainter* painter) {
platform::graph::util::WithTransform(
painter,
@@ -71,7 +128,28 @@ void TextRenderObject::Draw(platform::graph::IPainter* painter) {
for (const auto& rect : rects)
p->FillRectangle(rect, this->GetSelectionBrush().get());
}
+
p->DrawText(Point{}, text_layout_.get(), brush_.get());
+
+ if (this->draw_caret_ && this->caret_width_ != 0.0f) {
+ auto caret_pos = this->caret_position_;
+ gsl::index text_size = this->GetText().size();
+ if (caret_pos < 0) {
+ caret_pos = 0;
+ } else if (caret_pos > text_size) {
+ caret_pos = text_size;
+ }
+
+ const auto caret_top_center =
+ this->text_layout_->TextSinglePoint(caret_pos, false);
+
+ const auto font_height = this->font_->GetFontSize();
+ const auto caret_width = this->caret_width_;
+
+ p->FillRectangle(Rect{caret_top_center.x - caret_width / 2.0f,
+ caret_top_center.y, caret_width, font_height},
+ this->caret_brush_.get());
+ }
});
}
diff --git a/src/win/graph/direct/text_layout.cpp b/src/win/graph/direct/text_layout.cpp
index 4a742694..7b8d2ab0 100644
--- a/src/win/graph/direct/text_layout.cpp
+++ b/src/win/graph/direct/text_layout.cpp
@@ -105,7 +105,7 @@ std::vector<Rect> DWriteTextLayout::TextRangeRect(
return result;
}
-Point DWriteTextLayout::TextSingleRect(gsl::index position, bool trailing) {
+Point DWriteTextLayout::TextSinglePoint(gsl::index position, bool trailing) {
const auto index =
IndexUtf8ToUtf16(text_, static_cast<int>(position), w_text_);
diff --git a/src/win/native/ui_application.cpp b/src/win/native/ui_application.cpp
index 9aa3ebcd..599ecadc 100644
--- a/src/win/native/ui_application.cpp
+++ b/src/win/native/ui_application.cpp
@@ -73,19 +73,20 @@ void WinUiApplication::InvokeLater(std::function<void()> action) {
throw Win32Error(::GetLastError(), "InvokeLater failed to post message.");
}
-unsigned long WinUiApplication::SetTimeout(
- std::chrono::milliseconds milliseconds, std::function<void()> action) {
- return static_cast<unsigned long>(timer_manager_->CreateTimer(
+long long WinUiApplication::SetTimeout(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) {
+ return gsl::narrow<long long>(timer_manager_->CreateTimer(
static_cast<UINT>(milliseconds.count()), false, std::move(action)));
}
-unsigned long WinUiApplication::SetInterval(
- std::chrono::milliseconds milliseconds, std::function<void()> action) {
- return static_cast<unsigned long>(timer_manager_->CreateTimer(
+long long WinUiApplication::SetInterval(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) {
+ return gsl::narrow<long long>(timer_manager_->CreateTimer(
static_cast<UINT>(milliseconds.count()), true, std::move(action)));
}
-void WinUiApplication::CancelTimer(unsigned long id) {
+void WinUiApplication::CancelTimer(long long id) {
+ if (id < 0) return;
timer_manager_->KillTimer(static_cast<UINT_PTR>(id));
}