From b2528671450068d528f7501c84655cfd51ebdb68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?=
<7645683+bruvzg@users.noreply.github.com>
Date: Thu, 2 Jan 2025 13:27:35 +0200
Subject: [PATCH] [macOS/Windows] Add `Emoji & Symbols` context menu item to
LineEdit/TextEdit to show system character picker.
---
doc/classes/DisplayServer.xml | 10 ++++++
doc/classes/LineEdit.xml | 8 ++++-
doc/classes/TextEdit.xml | 8 ++++-
editor/gui/editor_spin_slider.cpp | 1 +
editor/plugins/script_text_editor.cpp | 7 +++++
editor/plugins/script_text_editor.h | 1 +
editor/plugins/text_editor.cpp | 7 +++++
editor/plugins/text_editor.h | 1 +
editor/plugins/text_shader_editor.cpp | 7 +++++
editor/plugins/text_shader_editor.h | 1 +
platform/macos/display_server_macos.h | 1 +
platform/macos/display_server_macos.mm | 5 +++
platform/windows/display_server_windows.cpp | 23 ++++++++++++++
platform/windows/display_server_windows.h | 1 +
scene/gui/line_edit.cpp | 33 +++++++++++++++++++-
scene/gui/line_edit.h | 7 +++++
scene/gui/spin_box.cpp | 1 +
scene/gui/text_edit.cpp | 34 ++++++++++++++++++++-
scene/gui/text_edit.h | 7 +++++
servers/display_server.cpp | 6 ++++
servers/display_server.h | 2 ++
21 files changed, 167 insertions(+), 4 deletions(-)
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 1f98e3f4867..a607d6b8ff7 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1215,6 +1215,13 @@
[b]Note:[/b] This method is implemented on Android, iOS, macOS, Windows, and Linux (X11/Wayland).
+
+
+
+ Opens system emoji and symbol picker.
+ [b]Note:[/b] This method is implemented on macOS and Windows.
+
+
@@ -1941,6 +1948,9 @@
Native file selection dialog supports MIME types as filters.
+
+ Display server supports system emoji and symbol picker. [b]Windows, macOS[/b]
+
Makes the mouse cursor visible if it is hidden.
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index 6948d7d454d..dd0d6ed4117 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -276,6 +276,9 @@
If [code]false[/code], existing text cannot be modified and new text cannot be added.
+
+ If [code]false[/code], "Emoji and Symbols" menu is enabled.
+
If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened.
@@ -478,7 +481,10 @@
Inserts soft hyphen (SHY) character.
-
+
+ Opens system emoji and symbol picker.
+
+
Represents the size of the [enum MenuItems] enum.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index b884fa59586..89e2a2017af 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -1299,6 +1299,9 @@
If [code]false[/code], existing text cannot be modified and new text cannot be added.
+
+ If [code]false[/code], "Emoji and Symbols" menu is enabled.
+
If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection.
@@ -1519,7 +1522,10 @@
Inserts soft hyphen (SHY) character.
-
+
+ Opens system emoji and symbol picker.
+
+
Represents the size of the [enum MenuItems] enum.
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 35ae1de5c89..cef23fdda1f 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -715,6 +715,7 @@ void EditorSpinSlider::_ensure_input_popup() {
add_child(value_input_popup);
value_input = memnew(LineEdit);
+ value_input->set_emoji_menu_enabled(false);
value_input->set_focus_mode(FOCUS_CLICK);
value_input_popup->add_child(value_input);
value_input->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index c1ef99d6533..fa16aba6b91 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1738,6 +1738,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
_lookup_symbol(text, tx->get_caret_line(0), tx->get_caret_column(0));
}
} break;
+ case EDIT_EMOJI_AND_SYMBOL: {
+ code_editor->get_text_editor()->show_emoji_and_symbol_picker();
+ } break;
default: {
if (p_op >= EditorContextMenuPlugin::BASE_ID) {
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, p_op, tx);
@@ -2294,6 +2297,10 @@ void ScriptTextEditor::_prepare_edit_menu() {
void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, Vector2 p_pos) {
context_menu->clear();
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ context_menu->add_item(TTR("Emoji & Symbols"), EDIT_EMOJI_AND_SYMBOL);
+ context_menu->add_separator();
+ }
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 0b81490d39a..6bf3f9f1ded 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -157,6 +157,7 @@ class ScriptTextEditor : public ScriptEditorBase {
DEBUG_GOTO_PREV_BREAKPOINT,
HELP_CONTEXTUAL,
LOOKUP_SYMBOL,
+ EDIT_EMOJI_AND_SYMBOL,
};
void _enable_code_editor();
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 274f4ee62cb..ee2d592ed3f 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -481,6 +481,9 @@ void TextEditor::_edit_option(int p_op) {
case BOOKMARK_REMOVE_ALL: {
code_editor->remove_all_bookmarks();
} break;
+ case EDIT_EMOJI_AND_SYMBOL: {
+ code_editor->get_text_editor()->show_emoji_and_symbol_picker();
+ } break;
}
}
@@ -560,6 +563,10 @@ void TextEditor::_prepare_edit_menu() {
void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position) {
context_menu->clear();
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ context_menu->add_item(TTR("Emoji & Symbols"), EDIT_EMOJI_AND_SYMBOL);
+ context_menu->add_separator();
+ }
if (p_selection) {
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index badeb91817a..e2e7fada7b9 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -91,6 +91,7 @@ private:
BOOKMARK_GOTO_NEXT,
BOOKMARK_GOTO_PREV,
BOOKMARK_REMOVE_ALL,
+ EDIT_EMOJI_AND_SYMBOL,
};
protected:
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 207a56edbb0..2bf14203e66 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -729,6 +729,9 @@ void TextShaderEditor::_menu_option(int p_option) {
case HELP_DOCS: {
OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL));
} break;
+ case EDIT_EMOJI_AND_SYMBOL: {
+ code_editor->get_text_editor()->show_emoji_and_symbol_picker();
+ } break;
}
if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
callable_mp((Control *)code_editor->get_text_editor(), &Control::grab_focus).call_deferred();
@@ -1087,6 +1090,10 @@ void TextShaderEditor::_bookmark_item_pressed(int p_idx) {
void TextShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
context_menu->clear();
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ context_menu->add_item(TTR("Emoji & Symbols"), EDIT_EMOJI_AND_SYMBOL);
+ context_menu->add_separator();
+ }
if (p_selection) {
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT);
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY);
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
index 33372595549..21d57c5ee6e 100644
--- a/editor/plugins/text_shader_editor.h
+++ b/editor/plugins/text_shader_editor.h
@@ -134,6 +134,7 @@ class TextShaderEditor : public ShaderEditor {
BOOKMARK_GOTO_PREV,
BOOKMARK_REMOVE_ALL,
HELP_DOCS,
+ EDIT_EMOJI_AND_SYMBOL,
};
MenuButton *edit_menu = nullptr;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index cc7ef3507e7..32ab1fc478d 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -432,6 +432,7 @@ public:
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
+ virtual void show_emoji_and_symbol_picker() const override;
virtual void process_events() override;
virtual void force_process_and_drop_events() override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 2e1326ae4f6..174e28310ef 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -789,6 +789,7 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
case FEATURE_NATIVE_HELP:
case FEATURE_WINDOW_DRAG:
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
+ case FEATURE_EMOJI_AND_SYMBOL_PICKER:
return true;
default: {
}
@@ -3134,6 +3135,10 @@ Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers);
}
+void DisplayServerMacOS::show_emoji_and_symbol_picker() const {
+ [[NSApplication sharedApplication] orderFrontCharacterPalette:nil];
+}
+
void DisplayServerMacOS::process_events() {
ERR_FAIL_COND(!Thread::is_main_thread());
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d6e4063d4cd..4d9e7b3965d 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -140,6 +140,8 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_EMBEDDING:
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
return true;
+ case FEATURE_EMOJI_AND_SYMBOL_PICKER:
+ return (os_ver.dwBuildNumber >= 17134); // Windows 10 Redstone 4 (1803)+ only.
default:
return false;
}
@@ -3433,6 +3435,27 @@ Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const
return p_keycode;
}
+void DisplayServerWindows::show_emoji_and_symbol_picker() const {
+ // Send Win + Period shortcut, there's no non-WinRT public API.
+
+ INPUT input[4] = {};
+ input[0].type = INPUT_KEYBOARD; // Win down.
+ input[0].ki.wVk = VK_LWIN;
+
+ input[1].type = INPUT_KEYBOARD; // Period down.
+ input[1].ki.wVk = VK_OEM_PERIOD;
+
+ input[2].type = INPUT_KEYBOARD; // Win up.
+ input[2].ki.wVk = VK_LWIN;
+ input[2].ki.dwFlags = KEYEVENTF_KEYUP;
+
+ input[3].type = INPUT_KEYBOARD; // Period up.
+ input[3].ki.wVk = VK_OEM_PERIOD;
+ input[3].ki.dwFlags = KEYEVENTF_KEYUP;
+
+ SendInput(4, input, sizeof(INPUT));
+}
+
String DisplayServerWindows::_get_keyboard_layout_display_name(const String &p_klid) const {
String ret;
HKEY key;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 5d69e1490d6..b85394366e8 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -842,6 +842,7 @@ public:
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
+ virtual void show_emoji_and_symbol_picker() const override;
virtual int tablet_get_driver_count() const override;
virtual String tablet_get_driver_name(int p_driver) const override;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 77c5b01e925..d54e31b4511 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -2369,7 +2369,10 @@ void LineEdit::menu_option(int p_option) {
if (editable) {
insert_text_at_caret(String::chr(0x00AD));
}
- }
+ } break;
+ case MENU_EMOJI_AND_SYMBOL: {
+ show_emoji_and_symbol_picker();
+ } break;
}
}
@@ -2381,6 +2384,22 @@ bool LineEdit::is_context_menu_enabled() {
return context_menu_enabled;
}
+void LineEdit::show_emoji_and_symbol_picker() {
+ _update_ime_window_position();
+ DisplayServer::get_singleton()->show_emoji_and_symbol_picker();
+}
+
+void LineEdit::set_emoji_menu_enabled(bool p_enabled) {
+ if (emoji_menu_enabled != p_enabled) {
+ emoji_menu_enabled = p_enabled;
+ _update_context_menu();
+ }
+}
+
+bool LineEdit::is_emoji_menu_enabled() const {
+ return emoji_menu_enabled;
+}
+
bool LineEdit::is_menu_visible() const {
return menu && menu->is_visible();
}
@@ -2709,6 +2728,11 @@ void LineEdit::_generate_context_menu() {
menu_ctl->add_item(ETR("Word Joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(ETR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ menu->add_item(ETR("Emoji & Symbols"), MENU_EMOJI_AND_SYMBOL);
+ menu->add_separator();
+ }
+
menu->add_item(ETR("Cut"), MENU_CUT);
menu->add_item(ETR("Copy"), MENU_COPY);
menu->add_item(ETR("Paste"), MENU_PASTE);
@@ -2764,6 +2788,9 @@ void LineEdit::_update_context_menu() {
m_menu->set_item_checked(idx, m_checked); \
}
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ MENU_ITEM_DISABLED(menu, MENU_EMOJI_AND_SYMBOL, !editable || !emoji_menu_enabled)
+ }
MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable)
MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy")
MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable)
@@ -2859,6 +2886,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_menu_visible"), &LineEdit::is_menu_visible);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_emoji_menu_enabled", "enable"), &LineEdit::set_emoji_menu_enabled);
+ ClassDB::bind_method(D_METHOD("is_emoji_menu_enabled"), &LineEdit::is_emoji_menu_enabled);
ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &LineEdit::set_virtual_keyboard_enabled);
ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &LineEdit::is_virtual_keyboard_enabled);
ClassDB::bind_method(D_METHOD("set_virtual_keyboard_type", "type"), &LineEdit::set_virtual_keyboard_type);
@@ -2917,6 +2946,7 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ);
BIND_ENUM_CONSTANT(MENU_INSERT_WJ);
BIND_ENUM_CONSTANT(MENU_INSERT_SHY);
+ BIND_ENUM_CONSTANT(MENU_EMOJI_AND_SYMBOL);
BIND_ENUM_CONSTANT(MENU_MAX);
BIND_ENUM_CONSTANT(KEYBOARD_TYPE_DEFAULT);
@@ -2936,6 +2966,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_editing_on_text_submit"), "set_keep_editing_on_text_submit", "is_editing_kept_on_text_submit");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emoji_menu_enabled"), "set_emoji_menu_enabled", "is_emoji_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "virtual_keyboard_type", PROPERTY_HINT_ENUM, "Default,Multiline,Number,Decimal,Phone,Email,Password,URL"), "set_virtual_keyboard_type", "get_virtual_keyboard_type");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index b60493b32dc..6f6b49307c9 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -69,6 +69,7 @@ public:
MENU_INSERT_ZWNJ,
MENU_INSERT_WJ,
MENU_INSERT_SHY,
+ MENU_EMOJI_AND_SYMBOL,
MENU_MAX
};
@@ -112,6 +113,7 @@ private:
bool drag_and_drop_selection_enabled = true;
bool context_menu_enabled = true;
+ bool emoji_menu_enabled = true;
PopupMenu *menu = nullptr;
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
@@ -289,6 +291,11 @@ public:
PopupMenu *get_menu() const;
bool is_menu_visible() const;
+ void show_emoji_and_symbol_picker();
+
+ void set_emoji_menu_enabled(bool p_enabled);
+ bool is_emoji_menu_enabled() const;
+
void select(int p_from = 0, int p_to = -1);
void select_all();
void selection_delete();
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index ffe6e2bb978..785ffa14e92 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -622,6 +622,7 @@ void SpinBox::_bind_methods() {
SpinBox::SpinBox() {
line_edit = memnew(LineEdit);
+ line_edit->set_emoji_menu_enabled(false);
add_child(line_edit, false, INTERNAL_MODE_FRONT);
line_edit->set_theme_type_variation("SpinBoxInnerLineEdit");
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index e4469167a74..85a6dce028e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3357,6 +3357,22 @@ bool TextEdit::is_context_menu_enabled() const {
return context_menu_enabled;
}
+void TextEdit::show_emoji_and_symbol_picker() {
+ _update_ime_window_position();
+ DisplayServer::get_singleton()->show_emoji_and_symbol_picker();
+}
+
+void TextEdit::set_emoji_menu_enabled(bool p_enabled) {
+ if (emoji_menu_enabled != p_enabled) {
+ emoji_menu_enabled = p_enabled;
+ _update_context_menu();
+ }
+}
+
+bool TextEdit::is_emoji_menu_enabled() const {
+ return emoji_menu_enabled;
+}
+
void TextEdit::set_shortcut_keys_enabled(bool p_enabled) {
shortcut_keys_enabled = p_enabled;
}
@@ -4031,7 +4047,10 @@ void TextEdit::menu_option(int p_option) {
if (editable) {
insert_text_at_caret(String::chr(0x00AD));
}
- }
+ } break;
+ case MENU_EMOJI_AND_SYMBOL: {
+ show_emoji_and_symbol_picker();
+ } break;
}
}
@@ -6583,6 +6602,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &TextEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_emoji_menu_enabled", "enable"), &TextEdit::set_emoji_menu_enabled);
+ ClassDB::bind_method(D_METHOD("is_emoji_menu_enabled"), &TextEdit::is_emoji_menu_enabled);
+
ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &TextEdit::set_shortcut_keys_enabled);
ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled);
@@ -6674,6 +6696,7 @@ void TextEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ);
BIND_ENUM_CONSTANT(MENU_INSERT_WJ);
BIND_ENUM_CONSTANT(MENU_INSERT_SHY);
+ BIND_ENUM_CONSTANT(MENU_EMOJI_AND_SYMBOL);
BIND_ENUM_CONSTANT(MENU_MAX);
/* Versioning */
@@ -6982,6 +7005,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emoji_menu_enabled"), "set_emoji_menu_enabled", "is_emoji_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
@@ -7433,6 +7457,11 @@ void TextEdit::_generate_context_menu() {
menu_ctl->add_item(ETR("Word Joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(ETR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ menu->add_item(ETR("Emoji & Symbols"), MENU_EMOJI_AND_SYMBOL);
+ menu->add_separator();
+ }
+
menu->add_item(ETR("Cut"), MENU_CUT);
menu->add_item(ETR("Copy"), MENU_COPY);
menu->add_item(ETR("Paste"), MENU_PASTE);
@@ -7485,6 +7514,9 @@ void TextEdit::_update_context_menu() {
m_menu->set_item_checked(idx, m_checked); \
}
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
+ MENU_ITEM_DISABLED(menu, MENU_EMOJI_AND_SYMBOL, !editable || !emoji_menu_enabled)
+ }
MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable)
MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy")
MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable)
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 04ddb1895fc..ef511096302 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -110,6 +110,7 @@ public:
MENU_INSERT_ZWNJ,
MENU_INSERT_WJ,
MENU_INSERT_SHY,
+ MENU_EMOJI_AND_SYMBOL,
MENU_MAX
};
@@ -316,6 +317,7 @@ private:
// User control.
bool overtype_mode = false;
bool context_menu_enabled = true;
+ bool emoji_menu_enabled = true;
bool shortcut_keys_enabled = true;
bool virtual_keyboard_enabled = true;
bool middle_mouse_paste_enabled = true;
@@ -762,6 +764,11 @@ public:
void set_context_menu_enabled(bool p_enabled);
bool is_context_menu_enabled() const;
+ void show_emoji_and_symbol_picker();
+
+ void set_emoji_menu_enabled(bool p_enabled);
+ bool is_emoji_menu_enabled() const;
+
void set_shortcut_keys_enabled(bool p_enabled);
bool is_shortcut_keys_enabled() const;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index c30a619d8cf..bf3dacc7f7d 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -723,6 +723,9 @@ Key DisplayServer::keyboard_get_label_from_physical(Key p_keycode) const {
ERR_FAIL_V_MSG(p_keycode, "Not supported by this display server.");
}
+void DisplayServer::show_emoji_and_symbol_picker() const {
+}
+
void DisplayServer::force_process_and_drop_events() {
}
@@ -1029,6 +1032,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("keyboard_get_keycode_from_physical", "keycode"), &DisplayServer::keyboard_get_keycode_from_physical);
ClassDB::bind_method(D_METHOD("keyboard_get_label_from_physical", "keycode"), &DisplayServer::keyboard_get_label_from_physical);
+ ClassDB::bind_method(D_METHOD("show_emoji_and_symbol_picker"), &DisplayServer::show_emoji_and_symbol_picker);
+
ClassDB::bind_method(D_METHOD("process_events"), &DisplayServer::process_events);
ClassDB::bind_method(D_METHOD("force_process_and_drop_events"), &DisplayServer::force_process_and_drop_events);
@@ -1086,6 +1091,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE);
BIND_ENUM_CONSTANT(FEATURE_WINDOW_EMBEDDING);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE_MIME);
+ BIND_ENUM_CONSTANT(FEATURE_EMOJI_AND_SYMBOL_PICKER);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/servers/display_server.h b/servers/display_server.h
index 460c1610ec7..562c2bf5abe 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -157,6 +157,7 @@ public:
FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE,
FEATURE_WINDOW_EMBEDDING,
FEATURE_NATIVE_DIALOG_FILE_MIME,
+ FEATURE_EMOJI_AND_SYMBOL_PICKER,
};
virtual bool has_feature(Feature p_feature) const = 0;
@@ -593,6 +594,7 @@ public:
virtual String keyboard_get_layout_name(int p_index) const;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const;
virtual Key keyboard_get_label_from_physical(Key p_keycode) const;
+ virtual void show_emoji_and_symbol_picker() const;
virtual int tablet_get_driver_count() const { return 1; }
virtual String tablet_get_driver_name(int p_driver) const { return "default"; }