| Flutter Windows Embedder |

text_input_manager.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/windows/text_input_manager.h"
6
7 #include <imm.h>
8
9 #include <memory>
10
11 #include "flutter/fml/logging.h"
12 #include "flutter/fml/macros.h"
13
14 namespace flutter {
15
16 // RAII wrapper for the Win32 Input Method Manager context.
17 class ImmContext {
18 public:
19 ImmContext(HWND window_handle)
20 : context_(::ImmGetContext(window_handle)),
21 window_handle_(window_handle) {
22 FML_DCHECK(window_handle);
23 }
24
25 ~ImmContext() {
26 if (context_ != nullptr) {
27 ::ImmReleaseContext(window_handle_, context_);
28 }
29 }
30
31 // Returns true if a valid IMM context has been obtained.
32 bool IsValid() const { return context_ != nullptr; }
33
34 // Returns the IMM context.
35 HIMC get() {
36 FML_DCHECK(context_);
37 return context_;
38 }
39
40 private:
41 HWND window_handle_;
42 HIMC context_;
43
44 FML_DISALLOW_COPY_AND_ASSIGN(ImmContext);
45 };
46
47 void TextInputManager::SetWindowHandle(HWND window_handle) {
48 window_handle_ = window_handle;
49 }
50
51 void TextInputManager::CreateImeWindow() {
52 if (window_handle_ == nullptr) {
53 return;
54 }
55
56 // Some IMEs ignore calls to ::ImmSetCandidateWindow() and use the position of
57 // the current system caret instead via ::GetCaretPos(). In order to behave
58 // as expected with these IMEs, we create a temporary system caret.
59 if (!ime_active_) {
60 ::CreateCaret(window_handle_, nullptr, 1, 1);
61 }
62 ime_active_ = true;
63
64 // Set the position of the IME windows.
65 UpdateImeWindow();
66 }
67
68 void TextInputManager::DestroyImeWindow() {
69 if (window_handle_ == nullptr) {
70 return;
71 }
72
73 // Destroy the system caret created in CreateImeWindow().
74 if (ime_active_) {
75 ::DestroyCaret();
76 }
77 ime_active_ = false;
78 }
79
80 void TextInputManager::UpdateImeWindow() {
81 if (window_handle_ == nullptr) {
82 return;
83 }
84
85 ImmContext imm_context(window_handle_);
86 if (imm_context.IsValid()) {
87 MoveImeWindow(imm_context.get());
88 }
89 }
90
91 void TextInputManager::UpdateCaretRect(const Rect& rect) {
92 caret_rect_ = rect;
93
94 if (window_handle_ == nullptr) {
95 return;
96 }
97
98 ImmContext imm_context(window_handle_);
99 if (imm_context.IsValid()) {
100 MoveImeWindow(imm_context.get());
101 }
102 }
103
104 long TextInputManager::GetComposingCursorPosition() const {
105 if (window_handle_ == nullptr) {
106 return false;
107 }
108
109 ImmContext imm_context(window_handle_);
110 if (imm_context.IsValid()) {
111 // Read the cursor position within the composing string.
112 return ImmGetCompositionString(imm_context.get(), GCS_CURSORPOS, nullptr,
113 0);
114 }
115 return -1;
116 }
117
118 std::optional<std::u16string> TextInputManager::GetComposingString() const {
119 return GetString(GCS_COMPSTR);
120 }
121
122 std::optional<std::u16string> TextInputManager::GetResultString() const {
123 return GetString(GCS_RESULTSTR);
124 }
125
126 void TextInputManager::AbortComposing() {
127 if (window_handle_ == nullptr || !ime_active_) {
128 return;
129 }
130
131 ImmContext imm_context(window_handle_);
132 if (imm_context.IsValid()) {
133 // Cancel composing and close the candidates window.
134 ::ImmNotifyIME(imm_context.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
135 ::ImmNotifyIME(imm_context.get(), NI_CLOSECANDIDATE, 0, 0);
136
137 // Clear the composing string.
138 wchar_t composition_str[] = L"";
139 wchar_t reading_str[] = L"";
140 ::ImmSetCompositionStringW(imm_context.get(), SCS_SETSTR, composition_str,
141 sizeof(wchar_t), reading_str, sizeof(wchar_t));
142 }
143 }
144
145 std::optional<std::u16string> TextInputManager::GetString(int type) const {
146 if (window_handle_ == nullptr || !ime_active_) {
147 return std::nullopt;
148 }
149 ImmContext imm_context(window_handle_);
150 if (imm_context.IsValid()) {
151 // Read the composing string length.
152 const long compose_bytes =
153 ::ImmGetCompositionString(imm_context.get(), type, nullptr, 0);
154 const long compose_length = compose_bytes / sizeof(wchar_t);
155 if (compose_length < 0) {
156 return std::nullopt;
157 }
158
159 std::u16string text(compose_length, '\0');
160 ::ImmGetCompositionString(imm_context.get(), type, &text[0], compose_bytes);
161 return text;
162 }
163 return std::nullopt;
164 }
165
166 void TextInputManager::MoveImeWindow(HIMC imm_context) {
167 if (GetFocus() != window_handle_ || !ime_active_) {
168 return;
169 }
170 LONG left = caret_rect_.left();
171 LONG top = caret_rect_.top();
172 LONG right = caret_rect_.right();
173 LONG bottom = caret_rect_.bottom();
174 ::SetCaretPos(left, bottom);
175
176 // Set the position of composition text.
177 COMPOSITIONFORM composition_form = {CFS_POINT, {left, top}};
178 ::ImmSetCompositionWindow(imm_context, &composition_form);
179
180 // Set the position of candidate window.
181 CANDIDATEFORM candidate_form = {
182 0, CFS_EXCLUDE, {left, bottom}, {left, top, right, bottom}};
183 ::ImmSetCandidateWindow(imm_context, &candidate_form);
184 }
185
186 } // namespace flutter
flutter::ImmContext
Definition: text_input_manager.cc:17
flutter::ImmContext::get
HIMC get()
Definition: text_input_manager.cc:35
flutter::ImmContext::ImmContext
ImmContext(HWND window_handle)
Definition: text_input_manager.cc:19
flutter::ImmContext::~ImmContext
~ImmContext()
Definition: text_input_manager.cc:25
flutter::ImmContext::IsValid
bool IsValid() const
Definition: text_input_manager.cc:32
flutter::Rect
Definition: geometry.h:59
flutter::Rect::bottom
double bottom() const
Definition: geometry.h:69
flutter::Rect::right
double right() const
Definition: geometry.h:68
flutter::Rect::top
double top() const
Definition: geometry.h:67
flutter::Rect::left
double left() const
Definition: geometry.h:66
flutter::TextInputManager::CreateImeWindow
void CreateImeWindow()
Definition: text_input_manager.cc:51
flutter::TextInputManager::UpdateCaretRect
void UpdateCaretRect(const Rect &rect)
Definition: text_input_manager.cc:91
flutter::TextInputManager::GetComposingString
virtual std::optional< std::u16string > GetComposingString() const
Definition: text_input_manager.cc:118
flutter::TextInputManager::SetWindowHandle
void SetWindowHandle(HWND window_handle)
Definition: text_input_manager.cc:47
flutter::TextInputManager::DestroyImeWindow
void DestroyImeWindow()
Definition: text_input_manager.cc:68
flutter::TextInputManager::UpdateImeWindow
void UpdateImeWindow()
Definition: text_input_manager.cc:80
flutter::TextInputManager::AbortComposing
void AbortComposing()
Definition: text_input_manager.cc:126
flutter::TextInputManager::GetResultString
virtual std::optional< std::u16string > GetResultString() const
Definition: text_input_manager.cc:122
flutter::TextInputManager::GetComposingCursorPosition
virtual long GetComposingCursorPosition() const
Definition: text_input_manager.cc:104
text
std::u16string text
Definition: keyboard_unittests.cc:332
type
enum flutter::testing::@90::KeyboardChange::Type type
flutter
Definition: accessibility_bridge_windows.cc:11
text_input_manager.h