Skip to content

Commit 38d545e

Browse files
shoryukennstuartmorgan-g
authored andcommitted
Improve Unicode handling on Windows (flutter#11899)
Significantly improves the behavior of non-ASCII text input on Windows. Correctly processes incoming character events as UTF-16, and for now uses UTF-32 for the text model so that the existing index-based logic will work much more often. Future work is still needed, but this will handle far more cases correctly.
1 parent 89efb4c commit 38d545e

12 files changed

Lines changed: 56 additions & 30 deletions

shell/platform/common/cpp/text_input_model.cc

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
#include "flutter/shell/platform/common/cpp/text_input_model.h"
66

7+
#include <codecvt>
78
#include <iostream>
9+
#include <locale>
810

911
// TODO(awdavies): Need to fix this regarding issue #47.
1012
static constexpr char kComposingBaseKey[] = "composingBase";
@@ -26,11 +28,16 @@ static constexpr char kTextInputAction[] = "inputAction";
2628
static constexpr char kTextInputType[] = "inputType";
2729
static constexpr char kTextInputTypeName[] = "name";
2830

31+
#if defined(_MSC_VER)
32+
// TODO(naifu): This temporary code is to solve link error.(VS2015/2017)
33+
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error
34+
std::locale::id std::codecvt<char32_t, char, _Mbstatet>::id;
35+
#endif // defined(_MSC_VER)
36+
2937
namespace flutter {
3038

3139
TextInputModel::TextInputModel(int client_id, const rapidjson::Value& config)
32-
: text_(""),
33-
client_id_(client_id),
40+
: client_id_(client_id),
3441
selection_base_(text_.begin()),
3542
selection_extent_(text_.begin()) {
3643
// TODO: Improve error handling during refactoring; this is just minimal
@@ -64,7 +71,8 @@ bool TextInputModel::SetEditingState(size_t selection_base,
6471
if (selection_extent > text.size()) {
6572
return false;
6673
}
67-
text_ = std::string(text);
74+
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf32conv;
75+
text_ = utf32conv.from_bytes(text);
6876
selection_base_ = text_.begin() + selection_base;
6977
selection_extent_ = text_.begin() + selection_extent;
7078
return true;
@@ -76,7 +84,7 @@ void TextInputModel::DeleteSelected() {
7684
selection_extent_ = selection_base_;
7785
}
7886

79-
void TextInputModel::AddCharacter(char c) {
87+
void TextInputModel::AddCharacter(char32_t c) {
8088
if (selection_base_ != selection_extent_) {
8189
DeleteSelected();
8290
}
@@ -172,8 +180,10 @@ std::unique_ptr<rapidjson::Document> TextInputModel::GetState() const {
172180
static_cast<int>(selection_extent_ - text_.begin()),
173181
allocator);
174182
editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator);
175-
editing_state.AddMember(kTextKey, rapidjson::Value(text_, allocator).Move(),
176-
allocator);
183+
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf8conv;
184+
editing_state.AddMember(
185+
kTextKey, rapidjson::Value(utf8conv.to_bytes(text_), allocator).Move(),
186+
allocator);
177187
args->PushBack(editing_state, allocator);
178188
return args;
179189
}

shell/platform/common/cpp/text_input_model.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class TextInputModel {
3232
// Either appends after the cursor (when selection base and extent are the
3333
// same), or deletes the selected characters, replacing the text with the
3434
// character specified.
35-
void AddCharacter(char c);
35+
void AddCharacter(char32_t c);
3636

3737
// Deletes either the selection, or one character ahead of the cursor.
3838
//
@@ -89,12 +89,12 @@ class TextInputModel {
8989
private:
9090
void DeleteSelected();
9191

92-
std::string text_;
92+
std::u32string text_;
9393
int client_id_;
9494
std::string input_type_;
9595
std::string input_action_;
96-
std::string::iterator selection_base_;
97-
std::string::iterator selection_extent_;
96+
std::u32string::iterator selection_base_;
97+
std::u32string::iterator selection_extent_;
9898
};
9999

100100
} // namespace flutter

shell/platform/glfw/text_input_plugin.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ void TextInputPlugin::CharHook(GLFWwindow* window, unsigned int code_point) {
4040
if (active_model_ == nullptr) {
4141
return;
4242
}
43-
// TODO(awdavies): Actually handle potential unicode characters. Probably
44-
// requires some ICU data or something.
45-
active_model_->AddCharacter(static_cast<char>(code_point));
43+
active_model_->AddCharacter(code_point);
4644
SendStateUpdate(*active_model_);
4745
}
4846

shell/platform/windows/key_event_handler.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger)
3232
KeyEventHandler::~KeyEventHandler() = default;
3333

3434
void KeyEventHandler::CharHook(Win32FlutterWindow* window,
35-
unsigned int code_point) {}
35+
char32_t code_point) {}
3636

3737
void KeyEventHandler::KeyboardHook(Win32FlutterWindow* window,
3838
int key,

shell/platform/windows/key_event_handler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class KeyEventHandler : public KeyboardHookHandler {
3434
int mods) override;
3535

3636
// |KeyboardHookHandler|
37-
void CharHook(Win32FlutterWindow* window, unsigned int code_point) override;
37+
void CharHook(Win32FlutterWindow* window, char32_t code_point) override;
3838

3939
private:
4040
// The Flutter system channel for key event messages.

shell/platform/windows/keyboard_hook_handler.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class KeyboardHookHandler {
2424
int mods) = 0;
2525

2626
// A function for hooking into unicode code point input.
27-
virtual void CharHook(Win32FlutterWindow* window,
28-
unsigned int code_point) = 0;
27+
virtual void CharHook(Win32FlutterWindow* window, char32_t code_point) = 0;
2928
};
3029

3130
} // namespace flutter

shell/platform/windows/text_input_plugin.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ static constexpr uint32_t kInputModelLimit = 256;
3939
namespace flutter {
4040

4141
void TextInputPlugin::CharHook(Win32FlutterWindow* window,
42-
unsigned int code_point) {
42+
char32_t code_point) {
4343
if (active_model_ == nullptr) {
4444
return;
4545
}
46-
// TODO bug 30661
47-
active_model_->AddCharacter(static_cast<char>(code_point));
46+
active_model_->AddCharacter(code_point);
4847
SendStateUpdate(*active_model_);
4948
}
5049

shell/platform/windows/text_input_plugin.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class TextInputPlugin : public KeyboardHookHandler {
3535
int mods) override;
3636

3737
// |KeyboardHookHandler|
38-
void CharHook(Win32FlutterWindow* window, unsigned int code_point) override;
38+
void CharHook(Win32FlutterWindow* window, char32_t code_point) override;
3939

4040
private:
4141
// Sends the current state of the given model to the Flutter engine.

shell/platform/windows/win32_flutter_window.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ void Win32FlutterWindow::OnPointerUp(double x, double y) {
125125
}
126126
}
127127

128-
void Win32FlutterWindow::OnChar(unsigned int code_point) {
128+
void Win32FlutterWindow::OnChar(char32_t code_point) {
129129
if (process_events_) {
130130
SendChar(code_point);
131131
}
@@ -207,7 +207,7 @@ void Win32FlutterWindow::SendPointerUp(double x, double y) {
207207
SendPointerEventWithData(event);
208208
}
209209

210-
void Win32FlutterWindow::SendChar(unsigned int code_point) {
210+
void Win32FlutterWindow::SendChar(char32_t code_point) {
211211
for (const auto& handler : keyboard_hook_handlers_) {
212212
handler->CharHook(this, code_point);
213213
}

shell/platform/windows/win32_flutter_window.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Win32FlutterWindow : public Win32Window {
5454
void OnPointerUp(double x, double y) override;
5555

5656
// |Win32Window|
57-
void OnChar(unsigned int code_point) override;
57+
void OnChar(char32_t code_point) override;
5858

5959
// |Win32Window|
6060
void OnKey(int key, int scancode, int action, int mods) override;
@@ -103,7 +103,7 @@ class Win32FlutterWindow : public Win32Window {
103103
void SendPointerUp(double x, double y);
104104

105105
// Reports a keyboard character to Flutter engine.
106-
void SendChar(unsigned int code_point);
106+
void SendChar(char32_t code_point);
107107

108108
// Reports a raw keyboard message to Flutter engine.
109109
void SendKey(int key, int scancode, int action, int mods);

0 commit comments

Comments
 (0)