From dc0c1f9528b5e6143e8a45b176075d051dfd8e1c Mon Sep 17 00:00:00 2001 From: Eric M Date: Mon, 18 May 2020 18:25:50 +1000 Subject: [PATCH] Added visual indentation lines guides for text editors --- editor/code_editor.cpp | 2 + editor/editor_settings.cpp | 4 + editor/editor_themes.cpp | 4 + editor/plugins/script_text_editor.cpp | 4 + editor/plugins/shader_editor_plugin.cpp | 4 + editor/plugins/text_editor.cpp | 4 + scene/gui/text_edit.cpp | 101 ++++++++++++++++++++---- scene/gui/text_edit.h | 10 +++ 8 files changed, 119 insertions(+), 14 deletions(-) diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 3ea970a0f00a..f4d2140572b5 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -885,6 +885,8 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_auto_indent(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent")); text_editor->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/indent/draw_tabs")); text_editor->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/indent/draw_spaces")); + text_editor->set_draw_indent_guides(EditorSettings::get_singleton()->get("text_editor/indent/draw_indent_guides")); + text_editor->set_highlight_active_indent_guide(EditorSettings::get_singleton()->get("text_editor/indent/highlight_active_indent_guide")); text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/navigation/smooth_scrolling")); text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/navigation/v_scroll_speed")); text_editor->set_draw_minimap(EditorSettings::get_singleton()->get("text_editor/navigation/show_minimap")); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index a16605ab4481..a8ef1855064c 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -433,6 +433,8 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _initial_set("text_editor/indent/auto_indent", true); _initial_set("text_editor/indent/convert_indent_on_save", true); _initial_set("text_editor/indent/draw_tabs", true); + _initial_set("text_editor/indent/draw_indent_guides", true); + _initial_set("text_editor/indent/highlight_active_indent_guide", true); _initial_set("text_editor/indent/draw_spaces", false); // Navigation @@ -678,6 +680,8 @@ void EditorSettings::_load_default_text_editor_theme() { _initial_set("text_editor/highlighting/brace_mismatch_color", Color(1, 0.2, 0.2)); _initial_set("text_editor/highlighting/current_line_color", Color(0.3, 0.5, 0.8, 0.15)); _initial_set("text_editor/highlighting/line_length_guideline_color", Color(0.3, 0.5, 0.8, 0.1)); + _initial_set("text_editor/highlighting/indent_guide_color", Color(0.8, 0.8, 0.8, 0.1)); + _initial_set("text_editor/highlighting/indent_active_guide_color", Color(0.8, 0.8, 0.8, 0.25)); _initial_set("text_editor/highlighting/word_highlighted_color", Color(0.8, 0.9, 0.9, 0.15)); _initial_set("text_editor/highlighting/number_color", Color(0.92, 0.58, 0.2)); _initial_set("text_editor/highlighting/function_color", Color(0.4, 0.64, 0.81)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index ace106cd3e16..516998bbd2d3 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1179,6 +1179,8 @@ Ref create_editor_theme(const Ref p_theme) { const Color brace_mismatch_color = error_color; const Color current_line_color = alpha1; const Color line_length_guideline_color = dark_theme ? base_color : background_color; + const Color indent_guide_color = mono_color * Color(1, 1, 1, 0.3); + const Color indent_active_guide_color = mono_color * Color(1, 1, 1, 0.6); const Color word_highlighted_color = alpha1; const Color number_color = basetype_color.lerp(mono_color, dark_theme ? 0.5 : 0.3); const Color function_color = main_color; @@ -1217,6 +1219,8 @@ Ref create_editor_theme(const Ref p_theme) { setting->set_initial_value("text_editor/highlighting/brace_mismatch_color", brace_mismatch_color, true); setting->set_initial_value("text_editor/highlighting/current_line_color", current_line_color, true); setting->set_initial_value("text_editor/highlighting/line_length_guideline_color", line_length_guideline_color, true); + setting->set_initial_value("text_editor/highlighting/indent_guide_color", indent_guide_color, true); + setting->set_initial_value("text_editor/highlighting/indent_active_guide_color", indent_active_guide_color, true); setting->set_initial_value("text_editor/highlighting/word_highlighted_color", word_highlighted_color, true); setting->set_initial_value("text_editor/highlighting/number_color", number_color, true); setting->set_initial_value("text_editor/highlighting/function_color", function_color, true); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index e7f8a56e5e0f..1ab78e728a69 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -209,6 +209,8 @@ void ScriptTextEditor::_load_theme_settings() { Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); + Color indent_guide_color = EDITOR_GET("text_editor/highlighting/indent_guide_color"); + Color indent_active_guide_color = EDITOR_GET("text_editor/highlighting/indent_active_guide_color"); Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); Color number_color = EDITOR_GET("text_editor/highlighting/number_color"); Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); @@ -244,6 +246,8 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); text_edit->add_theme_color_override("current_line_color", current_line_color); text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); + text_edit->add_theme_color_override("indent_guide_color", indent_guide_color); + text_edit->add_theme_color_override("indent_active_guide_color", indent_active_guide_color); text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color); text_edit->add_theme_color_override("number_color", number_color); text_edit->add_theme_color_override("function_color", function_color); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 0c3a44e4cde2..fe59afeae721 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -100,6 +100,8 @@ void ShaderTextEditor::_load_theme_settings() { Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); + Color indent_guide_color = EDITOR_GET("text_editor/highlighting/indent_guide_color"); + Color indent_active_guide_color = EDITOR_GET("text_editor/highlighting/indent_active_guide_color"); Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); Color number_color = EDITOR_GET("text_editor/highlighting/number_color"); Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); @@ -130,6 +132,8 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); get_text_edit()->add_theme_color_override("current_line_color", current_line_color); get_text_edit()->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); + get_text_edit()->add_theme_color_override("indent_guide_color", indent_guide_color); + get_text_edit()->add_theme_color_override("indent_active_guide_color", indent_active_guide_color); get_text_edit()->add_theme_color_override("word_highlighted_color", word_highlighted_color); get_text_edit()->add_theme_color_override("number_color", number_color); get_text_edit()->add_theme_color_override("function_color", function_color); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 3ceb9bfd8244..8d8d1bb7fae3 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -88,6 +88,8 @@ void TextEditor::_load_theme_settings() { Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); + Color indent_guide_color = EDITOR_GET("text_editor/highlighting/indent_guide_color"); + Color indent_active_guide_color = EDITOR_GET("text_editor/highlighting/indent_active_guide_color"); Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); Color number_color = EDITOR_GET("text_editor/highlighting/number_color"); Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); @@ -121,6 +123,8 @@ void TextEditor::_load_theme_settings() { text_edit->add_theme_color_override("brace_mismatch_color", brace_mismatch_color); text_edit->add_theme_color_override("current_line_color", current_line_color); text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color); + text_edit->add_theme_color_override("indent_guide_color", indent_guide_color); + text_edit->add_theme_color_override("indent_active_guide_color", indent_active_guide_color); text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color); text_edit->add_theme_color_override("number_color", number_color); text_edit->add_theme_color_override("function_color", function_color); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 932dda2f9da0..0308fdcb5cf7 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1055,8 +1055,13 @@ void TextEdit::_notification(int p_what) { } } - // draw main text + // Find which 'fold' the cursor line is a part of + int selected_line_parent_fold_start_line = get_line_parent_fold(cursor_get_line()); + int selected_line_parent_fold_last_line = get_fold_last_line(selected_line_parent_fold_start_line); + + // Draw main text. int line = first_visible_line; + for (int i = 0; i < draw_amount; i++) { line++; @@ -1123,6 +1128,30 @@ void TextEdit::_notification(int p_what) { ofs_y -= cursor.wrap_ofs * get_row_height(); ofs_y -= get_v_scroll_offset() * get_row_height(); + // Draw indent guides + if (draw_indent_guides && get_indent_level(line) != 0) { + // "Indent Count" is just the number of spaces in the indent (indent level) / the number of spaces per indent (indent size) + int indent_count = get_indent_level(line) / indent_size; + + for (int indent_idx = 0; indent_idx < indent_count; indent_idx++) { + Color line_color = cache.indent_guide_color; + + if (highlight_active_indent_guide && + indent_idx == get_indent_level(cursor_get_line()) / indent_size - 1 && + line >= selected_line_parent_fold_start_line && + line <= selected_line_parent_fold_last_line) { + // Make the line more prominent for the selected code block + line_color = cache.indent_active_guide_color; + } + + draw_line( + Point2(indent_idx * indent_size * cache.font->get_char_size(' ').width + xmargin_beg, ofs_y), + Point2(indent_idx * indent_size * cache.font->get_char_size(' ').width + xmargin_beg, ofs_y + get_row_height()), + line_color, + 1); + } + } + // Check if line contains highlighted word. int highlighted_text_col = -1; int search_text_col = -1; @@ -1482,6 +1511,7 @@ void TextEdit::_notification(int p_what) { } else if (draw_tabs && str[j] == '\t') { int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_color_selected : color); + //draw_line(Point2(char_ofs + char_margin + ofs_x, ofs_y + get_row_height()), Point2(char_ofs + char_margin + ofs_x, ofs_y), Color(1, 1, 1, 0.1), 1); } if (draw_spaces && str[j] == ' ') { @@ -5010,6 +5040,8 @@ void TextEdit::_update_caches() { cache.mark_color = get_theme_color("mark_color"); cache.current_line_color = get_theme_color("current_line_color"); cache.line_length_guideline_color = get_theme_color("line_length_guideline_color"); + cache.indent_guide_color = get_theme_color("indent_guide_color"); + cache.indent_active_guide_color = get_theme_color("indent_active_guide_color"); cache.bookmark_color = get_theme_color("bookmark_color"); cache.breakpoint_color = get_theme_color("breakpoint_color"); cache.executing_line_color = get_theme_color("executing_line_color"); @@ -5930,19 +5962,8 @@ void TextEdit::fold_line(int p_line) { } // Hide lines below this one. - int start_indent = get_indent_level(p_line); - int last_line = start_indent; - for (int i = p_line + 1; i < text.size(); i++) { - if (text[i].strip_edges().size() != 0) { - if (is_line_comment(i)) { - continue; - } else if (get_indent_level(i) > start_indent) { - last_line = i; - } else { - break; - } - } - } + int last_line = get_fold_last_line(p_line); + for (int i = p_line + 1; i <= last_line; i++) { set_line_as_hidden(i, true); } @@ -6002,6 +6023,38 @@ void TextEdit::toggle_fold_line(int p_line) { } } +// Returns the first line above p_line which would 'fold' p_line and hide it +int TextEdit::get_line_parent_fold(int p_line) { + int indent_count = get_indent_level(p_line) / indent_size; + + // Work backwards from the line to find the line that 1) can fold and b) is dedented compared to p_line + for (int line = p_line - 1; line >= 0; line--) { + if (can_fold(line) && get_indent_level(line) / indent_size < indent_count) { + return line; + } + } + return 0; +} + +int TextEdit::get_fold_last_line(int p_start_line) { + int start_indent = get_indent_level(p_start_line); + int last_line = p_start_line; + + for (int i = p_start_line + 1; i < text.size(); i++) { + if (text[i].strip_edges().size() != 0) { + if (is_line_comment(i)) { + continue; + } else if (get_indent_level(i) > start_indent) { + last_line = i; + } else { + break; + } + } + } + + return last_line; +} + int TextEdit::get_line_count() const { return text.size(); } @@ -6207,6 +6260,24 @@ bool TextEdit::is_drawing_spaces() const { return draw_spaces; } +void TextEdit::set_draw_indent_guides(bool p_draw) { + draw_indent_guides = p_draw; + update(); +} + +bool TextEdit::is_drawing_indent_guides(bool p_draw) { + return draw_indent_guides; +} + +void TextEdit::set_highlight_active_indent_guide(bool p_draw) { + highlight_active_indent_guide = p_draw; + update(); +} + +bool TextEdit::get_highlight_active_indent_guide() { + return highlight_active_indent_guide; +} + void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } @@ -7163,6 +7234,8 @@ TextEdit::TextEdit() { setting_row = false; draw_tabs = false; draw_spaces = false; + draw_indent_guides = false; + highlight_active_indent_guide = false; override_selected_font_color = false; draw_caret = true; max_chars = 0; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 689199b6c2de..283e837dec04 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -236,6 +236,8 @@ class TextEdit : public Control { Color code_folding_color; Color current_line_color; Color line_length_guideline_color; + Color indent_guide_color; + Color indent_active_guide_color; Color brace_mismatch_color; Color word_highlighted_color; Color search_result_color; @@ -357,6 +359,8 @@ class TextEdit : public Control { bool setting_row; bool draw_tabs; bool draw_spaces; + bool draw_indent_guides; + bool highlight_active_indent_guide; bool override_selected_font_color; bool cursor_changed_dirty; bool text_changed_dirty; @@ -616,6 +620,8 @@ class TextEdit : public Control { void fold_line(int p_line); void unfold_line(int p_line); void toggle_fold_line(int p_line); + int get_line_parent_fold(int p_line); + int get_fold_last_line(int p_start_line); String get_text(); String get_line(int line) const; @@ -717,6 +723,10 @@ class TextEdit : public Control { bool is_drawing_tabs() const; void set_draw_spaces(bool p_draw); bool is_drawing_spaces() const; + void set_draw_indent_guides(bool p_draw); + bool is_drawing_indent_guides(bool p_draw); + void set_highlight_active_indent_guide(bool p_draw); + bool get_highlight_active_indent_guide(); void set_override_selected_font_color(bool p_override_selected_font_color); bool is_overriding_selected_font_color() const;