diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 3e3d2205f2f0..70beb973b4ad 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1044,6 +1044,9 @@ The indentation style to use (tabs or spaces). [b]Note:[/b] The [url=$DOCS_URL/tutorials/scripting/gdscript/gdscript_styleguide.html]GDScript style guide[/url] recommends using tabs for indentation. It is advised to change this setting only if you need to work on a project that currently uses spaces for indentation. + + The characters to consider as word delimiters if [member text_editor/behavior/navigation/use_custom_word_separators] is [code]true[/code]. The characters should be defined without separation, for example [code]#_![/code]. + If [code]true[/code], allows drag-and-dropping text in the script editor to move text. Disable this if you find yourself accidentally drag-and-dropping text in the script editor. @@ -1063,6 +1066,12 @@ If [code]true[/code], prevents automatically switching between the Script and 2D/3D screens when selecting a node in the Scene tree dock. + + If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will use the behavior of [member text_editor/behavior/navigation/use_default_word_separators]. If [code]true[/code], it will also stop the caret if a character within [member text_editor/behavior/navigation/custom_word_separators] is detected. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + + + If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will stop moving caret only if a space or punctuation is detected. If [code]true[/code], it will also stop the caret if a character is [code]´`~$^=+|<>[/code], a General Punctuation, or CJK Punctuation. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + The number of pixels to scroll with every mouse wheel increment. Higher values make the script scroll by faster when using the mouse wheel. [b]Note:[/b] You can hold down [kbd]Alt[/kbd] while using the mouse wheel to temporarily scroll 5 times faster. diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 75cad4d08b57..f82671b533d2 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1278,6 +1278,9 @@ If [code]true[/code], a right-click displays the context menu. + + The characters to consider as word delimiters if [member use_custom_word_separators] is [code]true[/code]. The characters should be defined without separation, for example [code]#_![/code]. + If [code]true[/code], the selected text will be deselected when focus is lost. @@ -1363,6 +1366,12 @@ Base text writing direction. + + If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will use the behavior of [member use_default_word_separators]. If [code]true[/code], it will also stop the caret if a character within [member custom_word_separators] is detected. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + + + If [code]false[/code], using [kbd]Ctrl + Left[/kbd] or [kbd]Ctrl + Right[/kbd] ([kbd]Cmd + Left[/kbd] or [kbd]Cmd + Right[/kbd] on macOS) bindings will stop moving caret only if a space or punctuation is detected. If [code]true[/code], it will also stop the caret if a character is [code]´`~$^=+|<>[/code], a General Punctuation, or CJK Punctuation. Useful for subword moving. This behavior also will be applied to the behavior of text selection. + If [code]true[/code], the native virtual keyboard is shown when focused on platforms that support it. diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index ee0108df8ecd..4772b476e8b4 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1049,6 +1049,9 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_smooth_scroll_enabled(EDITOR_GET("text_editor/behavior/navigation/smooth_scrolling")); text_editor->set_v_scroll_speed(EDITOR_GET("text_editor/behavior/navigation/v_scroll_speed")); text_editor->set_drag_and_drop_selection_enabled(EDITOR_GET("text_editor/behavior/navigation/drag_and_drop_selection")); + text_editor->set_use_default_word_separators(EDITOR_GET("text_editor/behavior/navigation/use_default_word_separators")); + text_editor->set_use_custom_word_separators(EDITOR_GET("text_editor/behavior/navigation/use_custom_word_separators")); + text_editor->set_custom_word_separators(EDITOR_GET("text_editor/behavior/navigation/custom_word_separators")); // Behavior: Indent set_indent_using_spaces(EDITOR_GET("text_editor/behavior/indent/type")); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index d7bc3502cef6..e49dd49d466d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -642,6 +642,9 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _initial_set("text_editor/behavior/navigation/drag_and_drop_selection", true); _initial_set("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected", true); _initial_set("text_editor/behavior/navigation/open_script_when_connecting_signal_to_existing_method", true); + _initial_set("text_editor/behavior/navigation/use_default_word_separators", true); // Includes ´`~$^=+|<> General punctuation and CJK punctuation. + _initial_set("text_editor/behavior/navigation/use_custom_word_separators", false); + _initial_set("text_editor/behavior/navigation/custom_word_separators", ""); // Custom word separators. // Behavior: Indent EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/behavior/indent/type", 0, "Tabs,Spaces") diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a36eb0652ee0..62f4344c1e6e 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -207,6 +207,8 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan text.write[p_line].data_buf->set_direction((TextServer::Direction)direction); text.write[p_line].data_buf->set_break_flags(flags); text.write[p_line].data_buf->set_preserve_control(draw_control_chars); + text.write[p_line].data_buf->set_custom_punctuation(get_enabled_word_separators()); + if (p_ime_text.length() > 0) { if (p_text_changed) { text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, language); @@ -275,6 +277,8 @@ void TextEdit::Text::invalidate_all_lines() { } text.write[i].data_buf->set_width(width); text.write[i].data_buf->set_break_flags(flags); + text.write[i].data_buf->set_custom_punctuation(get_enabled_word_separators()); + if (tab_size_dirty) { if (tab_size > 0) { Vector tabs; @@ -436,6 +440,57 @@ void TextEdit::Text::move_gutters(int p_from_line, int p_to_line) { text.write[p_from_line].gutters.resize(gutter_count); } +void TextEdit::Text::set_use_default_word_separators(bool p_enabled) { + if (use_default_word_separators == p_enabled) { + return; + } + use_default_word_separators = p_enabled; + invalidate_all_lines(); +} + +void TextEdit::Text::set_use_custom_word_separators(bool p_enabled) { + if (use_custom_word_separators == p_enabled) { + return; + } + use_custom_word_separators = p_enabled; + invalidate_all_lines(); +} + +bool TextEdit::Text::is_default_word_separators_enabled() const { + return use_default_word_separators; +} + +bool TextEdit::Text::is_custom_word_separators_enabled() const { + return use_custom_word_separators; +} + +String TextEdit::Text::get_custom_word_separators() const { + return custom_word_separators; +} + +String TextEdit::Text::get_default_word_separators() const { + String concat_separators = "´`~$^=+|<>"; + for (char32_t ch = 0x2000; ch <= 0x206F; ++ch) { // General punctuation block. + concat_separators += ch; + } + for (char32_t ch = 0x3000; ch <= 0x303F; ++ch) { // CJK punctuation block. + concat_separators += ch; + } + return concat_separators; +} + +// Get default and/or custom word separators depending on the option enabled. +String TextEdit::Text::get_enabled_word_separators() const { + String all_separators; + if (use_default_word_separators) { + all_separators += get_default_word_separators(); + } + if (use_custom_word_separators) { + all_separators += get_custom_word_separators(); + } + return all_separators; +} + /////////////////////////////////////////////////////////////////////////////// /// TEXT EDIT /// /////////////////////////////////////////////////////////////////////////////// @@ -6267,6 +6322,44 @@ bool TextEdit::is_highlight_all_occurrences_enabled() const { return highlight_all_occurrences; } +void TextEdit::set_use_default_word_separators(bool p_enabled) { + text.set_use_default_word_separators(p_enabled); +} + +bool TextEdit::is_default_word_separators_enabled() const { + return text.is_default_word_separators_enabled(); +} + +// Set word separators. Combine default separators with custom separators if those options are enabled. +void TextEdit::set_custom_word_separators(const String &p_separators) { + text.set_custom_word_separators(p_separators); +} + +void TextEdit::Text::set_custom_word_separators(const String &p_separators) { + if (custom_word_separators == p_separators) { + return; + } + custom_word_separators = p_separators; + invalidate_all_lines(); +} + +bool TextEdit::is_custom_word_separators_enabled() const { + return text.is_custom_word_separators_enabled(); +} + +String TextEdit::get_custom_word_separators() const { + return text.get_custom_word_separators(); +} + +// Enable or disable custom word separators. +void TextEdit::set_use_custom_word_separators(bool p_enabled) { + text.set_use_custom_word_separators(p_enabled); +} + +String TextEdit::get_default_word_separators() const { + return text.get_default_word_separators(); +} + void TextEdit::set_draw_control_chars(bool p_enabled) { if (draw_control_chars != p_enabled) { draw_control_chars = p_enabled; @@ -6541,6 +6634,14 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_word_under_caret", "caret_index"), &TextEdit::get_word_under_caret, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("set_use_default_word_separators", "enabled"), &TextEdit::set_use_default_word_separators); + ClassDB::bind_method(D_METHOD("is_default_word_separators_enabled"), &TextEdit::is_default_word_separators_enabled); + + ClassDB::bind_method(D_METHOD("set_use_custom_word_separators", "enabled"), &TextEdit::set_use_custom_word_separators); + ClassDB::bind_method(D_METHOD("is_custom_word_separators_enabled"), &TextEdit::is_custom_word_separators_enabled); + ClassDB::bind_method(D_METHOD("set_custom_word_separators", "custom_word_separators"), &TextEdit::set_custom_word_separators); + ClassDB::bind_method(D_METHOD("get_custom_word_separators"), &TextEdit::get_custom_word_separators); + /* Selection. */ BIND_ENUM_CONSTANT(SELECTION_MODE_NONE); BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT); @@ -6764,6 +6865,10 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_multiple"), "set_multiple_carets_enabled", "is_multiple_carets_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_default_word_separators"), "set_use_default_word_separators", "is_default_word_separators_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_word_separators"), "set_use_custom_word_separators", "is_custom_word_separators_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_word_separators"), "set_custom_word_separators", "get_custom_word_separators"); + ADD_GROUP("Highlighting", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index efade3987673..6ed5cf4bdc00 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -174,6 +174,9 @@ private: TextServer::Direction direction = TextServer::DIRECTION_AUTO; BitField brk_flags = TextServer::BREAK_MANDATORY; bool draw_control_chars = false; + String custom_word_separators; + bool use_default_word_separators = true; + bool use_custom_word_separators = false; int line_height = -1; int max_width = -1; @@ -201,6 +204,18 @@ private: int get_line_width(int p_line, int p_wrap_index = -1) const; int get_max_width() const; + void set_use_default_word_separators(bool p_enabled); + bool is_default_word_separators_enabled() const; + + void set_use_custom_word_separators(bool p_enabled); + bool is_custom_word_separators_enabled() const; + + void set_word_separators(const String &p_separators); + void set_custom_word_separators(const String &p_separators); + String get_enabled_word_separators() const; + String get_custom_word_separators() const; + String get_default_word_separators() const; + void set_width(float p_width); float get_width() const; void set_brk_flags(BitField p_flags); @@ -1068,6 +1083,19 @@ public: Color get_font_color() const; + /* Behavior */ + + String get_default_word_separators() const; + + void set_use_default_word_separators(bool p_enabled); + bool is_default_word_separators_enabled() const; + + void set_custom_word_separators(const String &p_separators); + void set_use_custom_word_separators(bool p_enabled); + bool is_custom_word_separators_enabled() const; + + String get_custom_word_separators() const; + /* Deprecated. */ #ifndef DISABLE_DEPRECATED Vector get_caret_index_edit_order();