aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-07-05 18:13:10 +0800
committercrupest <crupest@outlook.com>2020-07-05 18:13:10 +0800
commitce56ab59a6d68c220fcc47c6977c618eaa43de7a (patch)
treed6775d0ffa6f4fde00e69ed82192bc6c5b25d45b
parente10ef322e5f6268aec5d7717a82fceb42607a000 (diff)
downloadcru-ce56ab59a6d68c220fcc47c6977c618eaa43de7a.tar.gz
cru-ce56ab59a6d68c220fcc47c6977c618eaa43de7a.tar.bz2
cru-ce56ab59a6d68c220fcc47c6977c618eaa43de7a.zip
...
-rw-r--r--.vscode/settings.json12
-rw-r--r--include/cru/ui/UiEvent.hpp4
-rw-r--r--include/cru/ui/controls/TextBlock.hpp3
-rw-r--r--include/cru/ui/controls/TextBox.hpp4
-rw-r--r--include/cru/ui/render/Base.hpp1
-rw-r--r--src/ui/controls/TextBlock.cpp6
-rw-r--r--src/ui/controls/TextBox.cpp20
-rw-r--r--src/ui/controls/TextControlService.hpp306
8 files changed, 177 insertions, 179 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a53c1bbb..a2ff60cb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -76,7 +76,17 @@
"cstdarg": "cpp",
"locale": "cpp",
"xlocbuf": "cpp",
- "xlocmes": "cpp"
+ "xlocmes": "cpp",
+ "codecvt": "cpp",
+ "deque": "cpp",
+ "filesystem": "cpp",
+ "fstream": "cpp",
+ "future": "cpp",
+ "random": "cpp",
+ "regex": "cpp",
+ "stack": "cpp",
+ "unordered_set": "cpp",
+ "variant": "cpp"
},
"cmake.configureSettings": {
"CMAKE_TOOLCHAIN_FILE": "${workspaceFolder}/vcpkg/scripts/buildsystems/vcpkg.cmake",
diff --git a/include/cru/ui/UiEvent.hpp b/include/cru/ui/UiEvent.hpp
index 29292d75..79d0f7e3 100644
--- a/include/cru/ui/UiEvent.hpp
+++ b/include/cru/ui/UiEvent.hpp
@@ -52,7 +52,9 @@ class RoutedEvent {
static_assert(!std::is_reference_v<TEventArgs>,
"TEventArgs must not be reference.");
- using EventArgs = TEventArgs;
+ using RawEventArgs = TEventArgs;
+ using IEventType = IEvent<TEventArgs&>;
+ using EventArgs = typename IEventType::EventArgs;
RoutedEvent() = default;
RoutedEvent(const RoutedEvent& other) = delete;
diff --git a/include/cru/ui/controls/TextBlock.hpp b/include/cru/ui/controls/TextBlock.hpp
index dd8b40b4..1b1b4a5c 100644
--- a/include/cru/ui/controls/TextBlock.hpp
+++ b/include/cru/ui/controls/TextBlock.hpp
@@ -28,7 +28,8 @@ class TextBlock : public NoChildControl {
std::string GetText() const;
void SetText(std::string text);
- render::TextRenderObject* GetTextRenderObject();
+ gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
+ render::ScrollRenderObject* GetScrollRenderObject() { return nullptr; }
private:
std::unique_ptr<render::TextRenderObject> text_render_object_;
diff --git a/include/cru/ui/controls/TextBox.hpp b/include/cru/ui/controls/TextBox.hpp
index 2f7a12b6..3d4de7c0 100644
--- a/include/cru/ui/controls/TextBox.hpp
+++ b/include/cru/ui/controls/TextBox.hpp
@@ -27,7 +27,8 @@ class TextBox : public NoChildControl {
render::RenderObject* GetRenderObject() const override;
- render::TextRenderObject* GetTextRenderObject();
+ gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
+ render::ScrollRenderObject* GetScrollRenderObject();
const TextBoxBorderStyle& GetBorderStyle();
void SetBorderStyle(TextBoxBorderStyle border_style);
@@ -40,6 +41,7 @@ class TextBox : public NoChildControl {
private:
std::unique_ptr<render::BorderRenderObject> border_render_object_;
+ std::unique_ptr<render::ScrollRenderObject> scroll_render_object_;
std::unique_ptr<render::TextRenderObject> text_render_object_;
TextBoxBorderStyle border_style_;
diff --git a/include/cru/ui/render/Base.hpp b/include/cru/ui/render/Base.hpp
index c2af5e99..801d58bd 100644
--- a/include/cru/ui/render/Base.hpp
+++ b/include/cru/ui/render/Base.hpp
@@ -6,6 +6,7 @@ class RenderObject;
class BorderRenderObject;
class CanvasRenderObject;
class FlexLayoutRenderObject;
+class ScrollRenderObject;
class StackLayoutRenderObject;
class TextRenderObject;
class WindowRenderObject;
diff --git a/src/ui/controls/TextBlock.cpp b/src/ui/controls/TextBlock.cpp
index f77e279b..5ec15796 100644
--- a/src/ui/controls/TextBlock.cpp
+++ b/src/ui/controls/TextBlock.cpp
@@ -1,10 +1,10 @@
#include "cru/ui/controls/TextBlock.hpp"
+#include "TextControlService.hpp"
+#include "cru/ui/UiManager.hpp"
#include "cru/ui/render/CanvasRenderObject.hpp"
#include "cru/ui/render/StackLayoutRenderObject.hpp"
#include "cru/ui/render/TextRenderObject.hpp"
-#include "cru/ui/UiManager.hpp"
-#include "TextControlService.hpp"
namespace cru::ui::controls {
using render::CanvasRenderObject;
@@ -38,7 +38,7 @@ void TextBlock::SetText(std::string text) {
text_render_object_->SetText(std::move(text));
}
-render::TextRenderObject* TextBlock::GetTextRenderObject() {
+gsl::not_null<render::TextRenderObject*> TextBlock::GetTextRenderObject() {
return text_render_object_.get();
}
} // namespace cru::ui::controls
diff --git a/src/ui/controls/TextBox.cpp b/src/ui/controls/TextBox.cpp
index 64fd4c60..b25e3bcb 100644
--- a/src/ui/controls/TextBox.cpp
+++ b/src/ui/controls/TextBox.cpp
@@ -1,19 +1,23 @@
#include "cru/ui/controls/TextBox.hpp"
+#include "TextControlService.hpp"
+#include "cru/ui/UiManager.hpp"
#include "cru/ui/render/BorderRenderObject.hpp"
#include "cru/ui/render/CanvasRenderObject.hpp"
+#include "cru/ui/render/ScrollRenderObject.hpp"
#include "cru/ui/render/StackLayoutRenderObject.hpp"
#include "cru/ui/render/TextRenderObject.hpp"
-#include "cru/ui/UiManager.hpp"
-#include "TextControlService.hpp"
namespace cru::ui::controls {
using render::BorderRenderObject;
using render::CanvasRenderObject;
+using render::ScrollRenderObject;
using render::StackLayoutRenderObject;
using render::TextRenderObject;
-TextBox::TextBox() : border_render_object_(new BorderRenderObject()) {
+TextBox::TextBox()
+ : border_render_object_(new BorderRenderObject()),
+ scroll_render_object_(new ScrollRenderObject()) {
const auto theme_resources = UiManager::GetInstance()->GetThemeResources();
border_style_ = theme_resources->text_box_border_style;
@@ -22,9 +26,11 @@ TextBox::TextBox() : border_render_object_(new BorderRenderObject()) {
theme_resources->text_brush, theme_resources->default_font,
theme_resources->text_selection_brush, theme_resources->caret_brush);
- border_render_object_->AddChild(text_render_object_.get(), 0);
+ border_render_object_->AddChild(scroll_render_object_.get(), 0);
+ scroll_render_object_->AddChild(text_render_object_.get(), 0);
border_render_object_->SetAttachedControl(this);
+ scroll_render_object_->SetAttachedControl(this);
text_render_object_->SetAttachedControl(this);
service_ = std::make_unique<TextControlService<TextBox>>(this);
@@ -48,10 +54,14 @@ render::RenderObject* TextBox::GetRenderObject() const {
return border_render_object_.get();
}
-render::TextRenderObject* TextBox::GetTextRenderObject() {
+gsl::not_null<render::TextRenderObject*> TextBox::GetTextRenderObject() {
return text_render_object_.get();
}
+render::ScrollRenderObject* TextBox::GetScrollRenderObject() {
+ return scroll_render_object_.get();
+}
+
const TextBoxBorderStyle& TextBox::GetBorderStyle() { return border_style_; }
void TextBox::SetBorderStyle(TextBoxBorderStyle border_style) {
diff --git a/src/ui/controls/TextControlService.hpp b/src/ui/controls/TextControlService.hpp
index c320b0c5..5fc8d987 100644
--- a/src/ui/controls/TextControlService.hpp
+++ b/src/ui/controls/TextControlService.hpp
@@ -14,215 +14,187 @@ constexpr int k_default_caret_blink_duration = 500;
// TControl should inherits `Control` and has following methods:
// ```
-// render::TextRenderObject* GetTextRenderObject();
+// gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
+// render::ScrollRenderObject* GetScrollRenderObject();
// ```
template <typename TControl>
class TextControlService : public Object {
CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::TextControlService")
public:
- TextControlService(TControl* control);
+ TextControlService(gsl::not_null<TControl*> control) : control_(control) {}
CRU_DELETE_COPY(TextControlService)
CRU_DELETE_MOVE(TextControlService)
- ~TextControlService();
+ ~TextControlService() override {
+ 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_);
+ }
public:
bool IsEnabled() { return enable_; }
- void SetEnabled(bool enable);
+
+ void SetEnabled(bool enable) {
+ if (enable == this->enable_) return;
+ if (enable) {
+ this->SetupHandlers();
+ if (this->caret_visible_) {
+ this->SetupCaret();
+ }
+ } else {
+ this->AbortSelection();
+ this->event_revoker_guards_.clear();
+ this->TearDownCaret();
+ }
+ }
bool IsCaretVisible() { return caret_visible_; }
- void SetCaretVisible(bool visible);
- int GetCaretBlinkDuration() { return caret_blink_duration_; }
- void SetCaretBlinkDuration(int milliseconds);
+ void SetCaretVisible(bool visible) {
+ if (visible == this->caret_visible_) return;
- private:
- void AbortSelection();
+ this->caret_visible_ = visible;
- void SetupCaret();
- void TearDownCaret();
+ if (this->enable_) {
+ if (visible) {
+ this->SetupCaret();
+ } else {
+ this->TearDownCaret();
+ }
+ }
+ }
- void SetupHandlers();
+ int GetCaretBlinkDuration() { return caret_blink_duration_; }
- void MouseMoveHandler(event::MouseEventArgs& args);
- void MouseDownHandler(event::MouseButtonEventArgs& args);
- void MouseUpHandler(event::MouseButtonEventArgs& args);
- void LoseFocusHandler(event::FocusChangeEventArgs& args);
+ void SetCaretBlinkDuration(int milliseconds) {
+ if (this->caret_blink_duration_ == milliseconds) return;
- private:
- TControl* control_;
- std::vector<EventRevokerGuard> event_revoker_guards_;
+ if (this->enable_ && this->caret_visible_) {
+ this->TearDownCaret();
+ this->SetupCaret();
+ }
+ }
- bool enable_ = false;
+ std::optional<TextRange> GetSelection() {
+ return this->control_->GetTextRenderObject()->GetSelectionRange();
+ }
+ void SetSelection(std::optional<TextRange> selection) {
+ this->control_->GetTextRenderObject()->SetSelectionRange(selection);
+ }
- bool caret_visible_ = false;
- long long caret_timer_id_ = -1;
- int caret_blink_duration_ = k_default_caret_blink_duration;
+ private:
+ void AbortSelection() {
+ if (this->select_down_button_.has_value()) {
+ this->control_->ReleaseMouse();
+ this->select_down_button_ = std::nullopt;
+ }
+ this->control_->GetTextRenderObject()->SetSelectionRange(std::nullopt);
+ }
- // nullopt means not selecting
- std::optional<MouseButton> select_down_button_;
+ void SetupCaret() {
+ const auto application = GetUiApplication();
- // before the char
- int select_start_position_;
-};
+ // Cancel first anyhow for safety.
+ application->CancelTimer(this->caret_timer_id_);
-template <typename TControl>
-TextControlService<TControl>::TextControlService(TControl* control)
- : control_(control) {}
+ this->control_->GetTextRenderObject()->SetDrawCaret(true);
+ this->caret_timer_id_ = application->SetInterval(
+ std::chrono::milliseconds(this->caret_blink_duration_),
+ [this] { this->control_->GetTextRenderObject()->ToggleDrawCaret(); });
+ }
-template <typename TControl>
-TextControlService<TControl>::~TextControlService() {
- 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_);
-}
+ void TearDownCaret() {
+ const auto application = GetUiApplication();
+ application->CancelTimer(this->caret_timer_id_);
+ this->control_->GetTextRenderObject()->SetDrawCaret(false);
+ }
-template <typename TControl>
-void TextControlService<TControl>::SetEnabled(bool enable) {
- if (enable == this->enable_) return;
- if (enable) {
- this->SetupHandlers();
- if (this->caret_visible_) {
- this->SetupCaret();
- }
- } else {
- this->AbortSelection();
- this->event_revoker_guards_.clear();
- this->TearDownCaret();
+ template <typename TArgs>
+ void SetupOneHandler(event::RoutedEvent<TArgs>* (TControl::*event)(),
+ void (TextControlService::*handler)(
+ typename event::RoutedEvent<TArgs>::EventArgs)) {
+ this->event_revoker_guards_.push_back(
+ EventRevokerGuard{(this->control_->*event)()->Direct()->AddHandler(
+ std::bind(handler, this, std::placeholders::_1))});
}
-}
-template <typename TControl>
-void TextControlService<TControl>::SetCaretVisible(bool visible) {
- if (visible == this->caret_visible_) return;
+ void SetupHandlers() {
+ Expects(event_revoker_guards_.empty());
+
+ SetupOneHandler(&Control::MouseMoveEvent,
+ &TextControlService::MouseMoveHandler);
+ SetupOneHandler(&Control::MouseDownEvent,
+ &TextControlService::MouseDownHandler);
+ SetupOneHandler(&Control::MouseUpEvent,
+ &TextControlService::MouseUpHandler);
+ SetupOneHandler(&Control::LoseFocusEvent,
+ &TextControlService::LoseFocusHandler);
+ }
- this->caret_visible_ = visible;
+ void MouseMoveHandler(event::MouseEventArgs& args) {
+ if (this->select_down_button_.has_value()) {
+ const auto text_render_object = this->control_->GetTextRenderObject();
+ const auto result = text_render_object->TextHitTest(
+ text_render_object->FromRootToContent(args.GetPoint()));
+ const auto position = result.position + (result.trailing ? 1 : 0);
+ log::TagDebug(log_tag,
+ "Text selection changed on mouse move, range: {}, {}.",
+ position, this->select_start_position_);
+ this->control_->GetTextRenderObject()->SetSelectionRange(
+ TextRange::FromTwoSides(position, this->select_start_position_));
+ text_render_object->SetCaretPosition(position);
+ }
+ }
- if (this->enable_) {
- if (visible) {
- this->SetupCaret();
+ void MouseDownHandler(event::MouseButtonEventArgs& args) {
+ if (this->select_down_button_.has_value()) {
+ return;
} else {
- this->TearDownCaret();
+ if (!this->control_->CaptureMouse()) return;
+ if (!this->control_->RequestFocus()) return;
+ const auto text_render_object = this->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);
+ text_render_object->SetSelectionRange(std::nullopt);
+ text_render_object->SetCaretPosition(position);
+ this->select_start_position_ = position;
+ log::TagDebug(log_tag, "Begin to select text, start position: {}.",
+ position);
}
}
-}
-
-template <typename TControl>
-void TextControlService<TControl>::SetCaretBlinkDuration(int milliseconds) {
- if (this->caret_blink_duration_ == milliseconds) return;
- if (this->enable_ && this->caret_visible_) {
- this->TearDownCaret();
- this->SetupCaret();
+ void 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::TagDebug(log_tag, "End selecting text.");
+ }
}
-}
-template <typename TControl>
-void TextControlService<TControl>::AbortSelection() {
- if (this->select_down_button_.has_value()) {
- this->control_->ReleaseMouse();
- this->select_down_button_ = std::nullopt;
+ void LoseFocusHandler(event::FocusChangeEventArgs& args) {
+ if (!args.IsWindow()) this->AbortSelection();
}
- this->control_->GetTextRenderObject()->SetSelectionRange(std::nullopt);
-}
-
-template <typename TControl>
-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(this->caret_blink_duration_),
- [this] { this->control_->GetTextRenderObject()->ToggleDrawCaret(); });
-}
-
-template <typename TControl>
-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());
- this->event_revoker_guards_.push_back(
- EventRevokerGuard{control_->MouseMoveEvent()->Direct()->AddHandler(
- std::bind(&TextControlService::MouseMoveHandler, this,
- std::placeholders::_1))});
- this->event_revoker_guards_.push_back(
- EventRevokerGuard{control_->MouseDownEvent()->Direct()->AddHandler(
- std::bind(&TextControlService::MouseDownHandler, this,
- std::placeholders::_1))});
- this->event_revoker_guards_.push_back(EventRevokerGuard{
- control_->MouseUpEvent()->Direct()->AddHandler(std::bind(
- &TextControlService::MouseUpHandler, this, std::placeholders::_1))});
- this->event_revoker_guards_.push_back(
- EventRevokerGuard{control_->LoseFocusEvent()->Direct()->AddHandler(
- std::bind(&TextControlService::LoseFocusHandler, this,
- std::placeholders::_1))});
-}
+ private:
+ gsl::not_null<TControl*> control_;
+ std::vector<EventRevokerGuard> event_revoker_guards_;
-template <typename TControl>
-void TextControlService<TControl>::MouseMoveHandler(
- event::MouseEventArgs& args) {
- if (this->select_down_button_.has_value()) {
- const auto text_render_object = this->control_->GetTextRenderObject();
- const auto result = text_render_object->TextHitTest(
- text_render_object->FromRootToContent(args.GetPoint()));
- const auto position = result.position + (result.trailing ? 1 : 0);
- log::TagDebug(log_tag,
- "Text selection changed on mouse move, range: {}, {}.",
- position, this->select_start_position_);
- this->control_->GetTextRenderObject()->SetSelectionRange(
- TextRange::FromTwoSides(
- static_cast<unsigned>(position),
- static_cast<unsigned>(this->select_start_position_)));
- text_render_object->SetCaretPosition(position);
- }
-}
+ bool enable_ = false;
-template <typename TControl>
-void TextControlService<TControl>::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->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);
- text_render_object->SetSelectionRange(std::nullopt);
- text_render_object->SetCaretPosition(position);
- this->select_start_position_ = position;
- log::TagDebug(log_tag, "Begin to select text, start position: {}.",
- position);
- }
-}
+ bool caret_visible_ = false;
+ long long caret_timer_id_ = -1;
+ int caret_blink_duration_ = k_default_caret_blink_duration;
-template <typename TControl>
-void TextControlService<TControl>::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::TagDebug(log_tag, "End selecting text.");
- }
-}
+ // nullopt means not selecting
+ std::optional<MouseButton> select_down_button_;
-template <typename TControl>
-void TextControlService<TControl>::LoseFocusHandler(
- event::FocusChangeEventArgs& args) {
- if (!args.IsWindow()) this->AbortSelection();
-}
+ // before the char
+ int select_start_position_;
+};
} // namespace cru::ui::controls