aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui/controls/TextHostControlService.hpp
blob: f39d9456660e5f6e24ae7d18b891889c30eaf412 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#pragma once
#include "Base.hpp"

#include "cru/platform/gui/InputMethod.hpp"
#include "cru/platform/gui/TimerHelper.hpp"
#include "cru/platform/gui/UiApplication.hpp"
#include "cru/ui/controls/Control.hpp"
#include "cru/ui/helper/ShortcutHub.hpp"


#include <functional>
#include <string>

namespace cru::ui::render {
class TextRenderObject;
class ScrollRenderObject;
}  // namespace cru::ui::render

namespace cru::ui::controls {
constexpr int k_default_caret_blink_duration = 500;

struct ITextHostControl : virtual Interface {
  virtual gsl::not_null<render::TextRenderObject*> GetTextRenderObject() = 0;
  // May return nullptr.
  virtual render::ScrollRenderObject* GetScrollRenderObject() = 0;
};

class TextHostControlService : public Object {
  CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::controls::TextControlService")

 public:
  TextHostControlService(gsl::not_null<Control*> control);

  CRU_DELETE_COPY(TextHostControlService)
  CRU_DELETE_MOVE(TextHostControlService)

  ~TextHostControlService() = default;

 public:
  bool IsEnabled() { return enable_; }
  void SetEnabled(bool enable);

  bool IsEditable() { return this->editable_; }
  void SetEditable(bool editable);

  std::u16string GetText() { return this->text_; }
  std::u16string_view GetTextView() { return this->text_; }
  void SetText(std::u16string text, bool stop_composition = false);

  void InsertText(gsl::index position, std::u16string_view text,
                  bool stop_composition = false);
  void DeleteChar(gsl::index position, bool stop_composition = false);

  // Return the position of deleted character.
  gsl::index DeleteCharPrevious(gsl::index position,
                                bool stop_composition = false);
  void DeleteText(TextRange range, bool stop_composition = false);

  void CancelComposition();

  std::optional<platform::gui::CompositionText> GetCompositionInfo();

  bool IsCaretVisible() { return caret_visible_; }
  void SetCaretVisible(bool visible);

  int GetCaretBlinkDuration() { return caret_blink_duration_; }
  void SetCaretBlinkDuration(int milliseconds);

  gsl::index GetCaretPosition() { return selection_.GetEnd(); }
  TextRange GetSelection() { return selection_; }

  void SetSelection(gsl::index caret_position);
  void SetSelection(TextRange selection, bool scroll_to_caret = true);

  void ChangeSelectionEnd(gsl::index new_end);
  void AbortSelection();

  void DeleteSelectedText();
  // If some text is selected, then they are deleted first. Then insert text
  // into caret position.
  void ReplaceSelectedText(std::u16string_view text);

  void ScrollToCaret();

 private:
  gsl::not_null<render::TextRenderObject*> GetTextRenderObject();
  render::ScrollRenderObject* GetScrollRenderObject();

  // May return nullptr.
  platform::gui::IInputMethodContext* GetInputMethodContext();

  void CoerceSelection();

  void SetupCaret();
  void TearDownCaret();

  void SyncTextRenderObject();

  void UpdateInputMethodPosition();

  template <typename TArgs>
  void SetupOneHandler(event::RoutedEvent<TArgs>* (Control::*event)(),
                       void (TextHostControlService::*handler)(
                           typename event::RoutedEvent<TArgs>::EventArgs)) {
    this->event_guard_ += (this->control_->*event)()->Bubble()->AddHandler(
        std::bind(handler, this, std::placeholders::_1));
  }

  void MouseMoveHandler(event::MouseEventArgs& args);
  void MouseDownHandler(event::MouseButtonEventArgs& args);
  void MouseUpHandler(event::MouseButtonEventArgs& args);
  void GainFocusHandler(event::FocusChangeEventArgs& args);
  void LoseFocusHandler(event::FocusChangeEventArgs& args);

  void SetUpShortcuts();

 private:
  gsl::not_null<Control*> control_;
  gsl::not_null<ITextHostControl*> text_host_control_;

  EventRevokerListGuard event_guard_;
  EventRevokerListGuard input_method_context_event_guard_;

  std::u16string text_;
  TextRange selection_;

  bool enable_ = false;
  bool editable_ = false;

  bool caret_visible_ = false;
  platform::gui::TimerAutoCanceler caret_timer_canceler_;
  int caret_blink_duration_ = k_default_caret_blink_duration;

  helper::ShortcutHub shortcut_hub_;

  // true if left mouse is down and selecting
  bool mouse_move_selecting_ = false;
};
}  // namespace cru::ui::controls