Apply TextEdit IME on most actions

This commit is contained in:
kit 2024-01-22 12:34:12 -05:00
parent fa48a51183
commit a6af442b05
7 changed files with 112 additions and 57 deletions

View file

@ -101,6 +101,12 @@
Adjust the viewport so the caret is visible.
</description>
</method>
<method name="apply_ime">
<return type="void" />
<description>
Applies text from the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME) to each caret and closes the IME if it is open.
</description>
</method>
<method name="backspace">
<return type="void" />
<param index="0" name="caret_index" type="int" default="-1" />
@ -114,6 +120,12 @@
Starts a multipart edit. All edits will be treated as one action until [method end_complex_operation] is called.
</description>
</method>
<method name="cancel_ime">
<return type="void" />
<description>
Closes the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME) if it is open. Any text in the IME will be lost.
</description>
</method>
<method name="center_viewport_to_caret">
<return type="void" />
<param index="0" name="caret_index" type="int" default="0" />
@ -611,7 +623,7 @@
<method name="has_ime_text" qualifiers="const">
<return type="bool" />
<description>
Returns if the user has IME text.
Returns [code]true[/code] if the user has text in the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] (IME).
</description>
</method>
<method name="has_redo" qualifiers="const">

View file

@ -1270,6 +1270,7 @@ void ScriptTextEditor::_gutter_clicked(int p_line, int p_gutter) {
void ScriptTextEditor::_edit_option(int p_op) {
CodeEdit *tx = code_editor->get_text_editor();
tx->apply_ime();
switch (p_op) {
case EDIT_UNDO: {
@ -1960,6 +1961,8 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
}
if (create_menu) {
tx->apply_ime();
Point2i pos = tx->get_line_column_at_pos(local_pos);
int row = pos.y;
int col = pos.x;

View file

@ -348,6 +348,7 @@ void TextEditor::set_find_replace_bar(FindReplaceBar *p_bar) {
void TextEditor::_edit_option(int p_op) {
CodeEdit *tx = code_editor->get_text_editor();
tx->apply_ime();
switch (p_op) {
case EDIT_UNDO: {
@ -502,6 +503,8 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
if (mb->get_button_index() == MouseButton::RIGHT) {
CodeEdit *tx = code_editor->get_text_editor();
tx->apply_ime();
Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
int row = pos.y;
int col = pos.x;

View file

@ -628,6 +628,8 @@ ShaderTextEditor::ShaderTextEditor() {
/*** SCRIPT EDITOR ******/
void TextShaderEditor::_menu_option(int p_option) {
shader_editor->get_text_editor()->apply_ime();
switch (p_option) {
case EDIT_UNDO: {
shader_editor->get_text_editor()->undo();
@ -978,6 +980,8 @@ void TextShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
CodeEdit *tx = shader_editor->get_text_editor();
tx->apply_ime();
Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
int row = pos.y;
int col = pos.x;

View file

@ -253,8 +253,9 @@ void CodeEdit::_notification(int p_what) {
void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
/* Ignore mouse clicks in IME input mode. */
// Ignore mouse clicks in IME input mode, let TextEdit handle it.
if (has_ime_text()) {
TextEdit::gui_input(p_gui_input);
return;
}

View file

@ -1489,14 +1489,7 @@ void TextEdit::_notification(int p_what) {
}
if (has_focus()) {
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
Point2 pos = get_global_position() + get_caret_draw_pos();
if (get_window()->get_embedder()) {
pos += get_viewport()->get_popup_base_transform().get_origin();
}
DisplayServer::get_singleton()->window_set_ime_position(pos, get_viewport()->get_window_id());
}
_update_ime_window_position();
}
} break;
@ -1507,14 +1500,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = true;
}
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
Point2 pos = get_global_position() + get_caret_draw_pos();
if (get_window()->get_embedder()) {
pos += get_viewport()->get_popup_base_transform().get_origin();
}
DisplayServer::get_singleton()->window_set_ime_position(pos, get_viewport()->get_window_id());
}
_update_ime_window_position();
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
int caret_start = -1;
@ -1541,17 +1527,7 @@ void TextEdit::_notification(int p_what) {
caret_blink_timer->stop();
}
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
if (!ime_text.is_empty()) {
ime_text = "";
ime_selection = Point2();
for (int i = 0; i < carets.size(); i++) {
text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, ime_text);
}
}
apply_ime();
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
@ -1571,15 +1547,8 @@ void TextEdit::_notification(int p_what) {
delete_selection();
}
for (int i = 0; i < carets.size(); i++) {
String t;
if (get_caret_column(i) >= 0) {
t = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length());
} else {
t = ime_text;
}
text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, t, structured_text_parser(st_parser, st_args, t));
}
_update_ime_text();
adjust_viewport_to_caret(0);
queue_redraw();
}
} break;
@ -1681,10 +1650,6 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (is_layout_rtl()) {
mpos.x = get_size().x - mpos.x;
}
if (ime_text.length() != 0) {
// Ignore mouse clicks in IME input mode.
return;
}
if (mb->is_pressed()) {
if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_or_control_pressed()) {
@ -1718,6 +1683,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == MouseButton::LEFT) {
_reset_caret_blink_timer();
apply_ime();
Point2i pos = get_line_column_at_pos(mpos);
int row = pos.y;
int col = pos.x;
@ -1865,11 +1832,13 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
apply_ime();
paste_primary_clipboard();
}
if (mb->get_button_index() == MouseButton::RIGHT && (context_menu_enabled || is_move_caret_on_right_click_enabled())) {
_reset_caret_blink_timer();
apply_ime();
Point2i pos = get_line_column_at_pos(mpos);
int row = pos.y;
@ -1909,6 +1878,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
} else {
if (has_ime_text()) {
// Ignore mouse up in IME input mode.
return;
}
if (mb->get_button_index() == MouseButton::LEFT) {
if (selection_drag_attempt && is_mouse_over_selection()) {
remove_secondary_carets();
@ -1967,7 +1941,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
_update_minimap_drag();
}
if (!dragging_minimap) {
if (!dragging_minimap && !has_ime_text()) {
switch (selecting_mode) {
case SelectionMode::SELECTION_MODE_POINTER: {
_update_selection_mode_pointer();
@ -2012,6 +1986,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (drag_action && can_drop_data(mpos, get_viewport()->gui_get_drag_data())) {
apply_ime();
drag_caret_force_displayed = true;
Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
set_caret_line(pos.y, false, true, 0, 0);
@ -3030,6 +3005,43 @@ void TextEdit::_update_caches() {
}
}
void TextEdit::_close_ime_window() {
if (get_viewport()->get_window_id() == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
return;
}
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
void TextEdit::_update_ime_window_position() {
if (get_viewport()->get_window_id() == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
return;
}
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
Point2 pos = get_global_position() + get_caret_draw_pos();
if (get_window()->get_embedder()) {
pos += get_viewport()->get_popup_base_transform().get_origin();
}
// The window will move to the updated position the next time the IME is updated, not immediately.
DisplayServer::get_singleton()->window_set_ime_position(pos, get_viewport()->get_window_id());
}
void TextEdit::_update_ime_text() {
if (has_ime_text()) {
// Update text to visually include IME text.
for (int i = 0; i < get_caret_count(); i++) {
String text_with_ime = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length());
text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, text_with_ime, structured_text_parser(st_parser, st_args, text_with_ime));
}
} else {
// Reset text.
for (int i = 0; i < get_caret_count(); i++) {
text.invalidate_cache(get_caret_line(i), get_caret_column(i), true);
}
}
queue_redraw();
}
/* General overrides. */
Size2 TextEdit::get_minimum_size() const {
Size2 size = theme_cache.style_normal->get_minimum_size();
@ -3189,6 +3201,26 @@ bool TextEdit::has_ime_text() const {
return !ime_text.is_empty();
}
void TextEdit::cancel_ime() {
if (!has_ime_text()) {
return;
}
ime_text = String();
ime_selection = Point2();
_close_ime_window();
_update_ime_text();
}
void TextEdit::apply_ime() {
if (!has_ime_text()) {
return;
}
// Force apply the current IME text.
String insert_ime_text = ime_text;
cancel_ime();
insert_text_at_caret(insert_ime_text);
}
void TextEdit::set_editable(const bool p_editable) {
if (editable == p_editable) {
return;
@ -3568,16 +3600,8 @@ void TextEdit::insert_text_at_caret(const String &p_text, int p_caret) {
adjust_carets_after_edit(i, new_line, new_column, from_line, from_col);
}
if (!ime_text.is_empty()) {
for (int i = 0; i < carets.size(); i++) {
String t;
if (get_caret_column(i) >= 0) {
t = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length());
} else {
t = ime_text;
}
text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, t, structured_text_parser(st_parser, st_args, t));
}
if (has_ime_text()) {
_update_ime_text();
}
end_complex_operation();
@ -5560,14 +5584,14 @@ void TextEdit::adjust_viewport_to_caret(int p_caret) {
Vector2i caret_pos;
// Get position of the start of caret.
if (ime_text.length() != 0 && ime_selection.x != 0) {
if (has_ime_text() && ime_selection.x != 0) {
caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x, get_caret_line(p_caret), get_caret_column(p_caret));
} else {
caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret), get_caret_line(p_caret), get_caret_column(p_caret));
}
// Get position of the end of caret.
if (ime_text.length() != 0) {
if (has_ime_text()) {
if (ime_selection.y != 0) {
caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret));
} else {
@ -5612,14 +5636,14 @@ void TextEdit::center_viewport_to_caret(int p_caret) {
Vector2i caret_pos;
// Get position of the start of caret.
if (ime_text.length() != 0 && ime_selection.x != 0) {
if (has_ime_text() && ime_selection.x != 0) {
caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x, get_caret_line(p_caret), get_caret_column(p_caret));
} else {
caret_pos.x = _get_column_x_offset_for_line(get_caret_column(p_caret), get_caret_line(p_caret), get_caret_column(p_caret));
}
// Get position of the end of caret.
if (ime_text.length() != 0) {
if (has_ime_text()) {
if (ime_selection.y != 0) {
caret_pos.y = _get_column_x_offset_for_line(get_caret_column(p_caret) + ime_selection.x + ime_selection.y, get_caret_line(p_caret), get_caret_column(p_caret));
} else {
@ -6029,6 +6053,8 @@ void TextEdit::_bind_methods() {
/* Text */
// Text properties
ClassDB::bind_method(D_METHOD("has_ime_text"), &TextEdit::has_ime_text);
ClassDB::bind_method(D_METHOD("cancel_ime"), &TextEdit::cancel_ime);
ClassDB::bind_method(D_METHOD("apply_ime"), &TextEdit::apply_ime);
ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &TextEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &TextEdit::is_editable);

View file

@ -293,6 +293,10 @@ private:
void _clear();
void _update_caches();
void _close_ime_window();
void _update_ime_window_position();
void _update_ime_text();
// User control.
bool overtype_mode = false;
bool context_menu_enabled = true;
@ -696,6 +700,8 @@ public:
/* Text */
// Text properties.
bool has_ime_text() const;
void cancel_ime();
void apply_ime();
void set_editable(const bool p_editable);
bool is_editable() const;