Flutter Engine
The Flutter Engine
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
14namespace flutter {
15
16// RAII wrapper for the Win32 Input Method Manager context.
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
47void 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
118std::optional<std::u16string> TextInputManager::GetComposingString() const {
119 return GetString(GCS_COMPSTR);
120}
121
122std::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
145std::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
166void 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
GLenum type
ImmContext(HWND window_handle)
double bottom() const
Definition: geometry.h:66
double right() const
Definition: geometry.h:65
double top() const
Definition: geometry.h:64
double left() const
Definition: geometry.h:63
void UpdateCaretRect(const Rect &rect)
virtual std::optional< std::u16string > GetComposingString() const
void SetWindowHandle(HWND window_handle)
virtual std::optional< std::u16string > GetResultString() const
virtual long GetComposingCursorPosition() const
#define FML_DCHECK(condition)
Definition: logging.h:103
std::u16string text
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
long LONG
Definition: windows_types.h:23