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 
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 
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 
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.
66 }
67 
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 
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 
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 
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 
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::TextInputManager::GetResultString
virtual std::optional< std::u16string > GetResultString() const
Definition: text_input_manager.cc:122
flutter::ImmContext
Definition: text_input_manager.cc:17
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::Rect
Definition: geometry.h:56
flutter::TextInputManager::GetComposingCursorPosition
virtual long GetComposingCursorPosition() const
Definition: text_input_manager.cc:104
flutter::ImmContext::ImmContext
ImmContext(HWND window_handle)
Definition: text_input_manager.cc:19
text_input_manager.h
type
enum flutter::testing::@87::KeyboardChange::Type type
flutter::Rect::left
double left() const
Definition: geometry.h:63
text
std::u16string text
Definition: keyboard_unittests.cc:332
flutter::ImmContext::IsValid
bool IsValid() const
Definition: text_input_manager.cc:32
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::Rect::top
double top() const
Definition: geometry.h:64
flutter::TextInputManager::AbortComposing
void AbortComposing()
Definition: text_input_manager.cc:126
flutter::Rect::bottom
double bottom() const
Definition: geometry.h:66
flutter::TextInputManager::GetComposingString
virtual std::optional< std::u16string > GetComposingString() const
Definition: text_input_manager.cc:118
flutter::ImmContext::~ImmContext
~ImmContext()
Definition: text_input_manager.cc:25
flutter::ImmContext::get
HIMC get()
Definition: text_input_manager.cc:35
flutter::TextInputManager::DestroyImeWindow
void DestroyImeWindow()
Definition: text_input_manager.cc:68
flutter::Rect::right
double right() const
Definition: geometry.h:65
flutter::TextInputManager::SetWindowHandle
void SetWindowHandle(HWND window_handle)
Definition: text_input_manager.cc:47
flutter::TextInputManager::UpdateImeWindow
void UpdateImeWindow()
Definition: text_input_manager.cc:80