[Complex Text Layouts] Refactor TextEdit and CodeEdit controls.

This commit is contained in:
bruvzg 2020-09-18 10:36:10 +03:00
parent d66eb77c9c
commit 3be31c4960
No known key found for this signature in database
GPG Key ID: FCED35F1CECE0D3A
7 changed files with 1301 additions and 793 deletions

View File

@ -3,6 +3,7 @@
<brief_description>
</brief_description>
<description>
[b]Note[/b]: By default [CodeEdit] always use left-to-right text direction to correcly display source code.
</description>
<tutorials>
</tutorials>

View File

@ -731,20 +731,10 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
if (magnify_gesture.is_valid()) {
/*
Ref<DynamicFont> font = text_editor->get_theme_font("font");
font_size = text_editor->get_theme_font_size("font_size");
font_size *= powf(magnify_gesture->get_factor(), 0.25);
if (font.is_valid()) {
if (font->get_size() != (int)font_size) {
font_size = font->get_size();
}
font_size *= powf(magnify_gesture->get_factor(), 0.25);
_add_font_size((int)font_size - font->get_size());
}
*/
//TODO move size to draw functions
_add_font_size((int)font_size - text_editor->get_theme_font_size("font_size"));
return;
}
@ -767,15 +757,23 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
void CodeTextEditor::_zoom_in() {
font_resize_val += MAX(EDSCALE, 1.0f);
_zoom_changed();
}
void CodeTextEditor::_zoom_out() {
font_resize_val -= MAX(EDSCALE, 1.0f);
_zoom_changed();
}
void CodeTextEditor::_zoom_changed() {
if (font_resize_timer->get_time_left() == 0) {
font_resize_timer->start();
}
}
void CodeTextEditor::_reset_zoom() {
font_resize_val = 1.0f;
//TODO MOVE size to draw functions
EditorSettings::get_singleton()->set("interface/editor/code_font_size", 14);
text_editor->add_theme_font_size_override("font_size", 14 * EDSCALE);
}
void CodeTextEditor::_line_col_changed() {
@ -884,6 +882,24 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptCodeCompletionOp
return tex;
}
void CodeTextEditor::_font_resize_timeout() {
if (_add_font_size(font_resize_val)) {
font_resize_val = 0;
}
}
bool CodeTextEditor::_add_font_size(int p_delta) {
int old_size = text_editor->get_theme_font_size("font_size");
int new_size = CLAMP(old_size + p_delta, 8 * EDSCALE, 96 * EDSCALE);
if (new_size != old_size) {
EditorSettings::get_singleton()->set("interface/editor/code_font_size", new_size / EDSCALE);
text_editor->add_theme_font_size_override("font_size", new_size);
}
return true;
}
void CodeTextEditor::update_editor_settings() {
completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
completion_string_color = EDITOR_GET("text_editor/highlighting/string_color");
@ -1479,6 +1495,31 @@ void CodeTextEditor::_on_settings_change() {
font_size = EditorSettings::get_singleton()->get("interface/editor/code_font_size");
int ot_mode = EditorSettings::get_singleton()->get("interface/editor/code_font_contextual_ligatures");
switch (ot_mode) {
case 1: { // Disable ligatures.
text_editor->clear_opentype_features();
text_editor->set_opentype_feature("calt", 0);
} break;
case 2: { // Custom.
text_editor->clear_opentype_features();
Vector<String> subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_opentype_features")).split(",");
Dictionary ftrs;
for (int i = 0; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=");
if (subtag_a.size() == 2) {
text_editor->set_opentype_feature(subtag_a[0], subtag_a[1].to_int());
} else if (subtag_a.size() == 1) {
text_editor->set_opentype_feature(subtag_a[0], 1);
}
}
} break;
default: { // Default.
text_editor->clear_opentype_features();
text_editor->set_opentype_feature("calt", 1);
} break;
}
// Auto brace completion.
text_editor->set_auto_brace_completion(
EDITOR_GET("text_editor/completion/auto_brace_complete"));
@ -1663,6 +1704,31 @@ CodeTextEditor::CodeTextEditor() {
add_child(text_editor);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
int ot_mode = EditorSettings::get_singleton()->get("interface/editor/code_font_contextual_ligatures");
switch (ot_mode) {
case 1: { // Disable ligatures.
text_editor->clear_opentype_features();
text_editor->set_opentype_feature("calt", 0);
} break;
case 2: { // Custom.
text_editor->clear_opentype_features();
Vector<String> subtag = String(EditorSettings::get_singleton()->get("interface/editor/code_font_custom_opentype_features")).split(",");
Dictionary ftrs;
for (int i = 0; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=");
if (subtag_a.size() == 2) {
text_editor->set_opentype_feature(subtag_a[0], subtag_a[1].to_int());
} else if (subtag_a.size() == 1) {
text_editor->set_opentype_feature(subtag_a[0], 1);
}
}
} break;
default: { // Default.
text_editor->clear_opentype_features();
text_editor->set_opentype_feature("calt", 1);
} break;
}
// Added second so it opens at the bottom, so it won't shift the entire text editor when opening.
find_replace_bar = memnew(FindReplaceBar);
add_child(find_replace_bar);
@ -1764,6 +1830,11 @@ CodeTextEditor::CodeTextEditor() {
font_resize_val = 0;
font_size = EditorSettings::get_singleton()->get("interface/editor/code_font_size");
font_resize_timer = memnew(Timer);
add_child(font_resize_timer);
font_resize_timer->set_one_shot(true);
font_resize_timer->set_wait_time(0.07);
font_resize_timer->connect("timeout", callable_mp(this, &CodeTextEditor::_font_resize_timeout));
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CodeTextEditor::_on_settings_change));
}

View File

@ -151,6 +151,7 @@ class CodeTextEditor : public VBoxContainer {
Timer *idle;
Timer *code_complete_timer;
Timer *font_resize_timer;
int font_resize_val;
real_t font_size;
@ -163,11 +164,14 @@ class CodeTextEditor : public VBoxContainer {
void _update_font();
void _complete_request();
Ref<Texture2D> _get_completion_icon(const ScriptCodeCompletionOption &p_option);
void _font_resize_timeout();
bool _add_font_size(int p_delta);
void _input(const Ref<InputEvent> &event);
void _text_editor_gui_input(const Ref<InputEvent> &p_event);
void _zoom_in();
void _zoom_out();
void _zoom_changed();
void _reset_zoom();
Color completion_font_color;

View File

@ -912,6 +912,7 @@ void ScriptTextEditor::update_toggle_scripts_button() {
void ScriptTextEditor::_update_connected_methods() {
CodeEdit *text_edit = code_editor->get_text_editor();
text_edit->set_gutter_width(connection_gutter, text_edit->get_row_height());
for (int i = 0; i < text_edit->get_line_count(); i++) {
if (text_edit->get_line_gutter_metadata(i, connection_gutter) == "") {
continue;
@ -1352,7 +1353,8 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
void ScriptTextEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_row_height());
} break;
default:

View File

@ -34,9 +34,9 @@ void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
set_gutter_width(main_gutter, cache.row_height);
set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
set_gutter_width(fold_gutter, cache.row_height / 1.2);
set_gutter_width(main_gutter, get_row_height());
set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width);
set_gutter_width(fold_gutter, get_row_height() / 1.2);
breakpoint_color = get_theme_color("breakpoint_color");
breakpoint_icon = get_theme_icon("breakpoint");
@ -234,14 +234,16 @@ bool CodeEdit::is_line_numbers_zero_padded() const {
}
void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
int yofs = p_region.position.y + (cache.row_height - cache.font->get_height()) / 2;
String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding));
Ref<TextLine> tl;
tl.instance();
tl->add_string(fc, cache.font, cache.font_size);
int yofs = p_region.position.y + (get_row_height() - tl->get_size().y) / 2;
Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
if (number_color == Color(1, 1, 1)) {
number_color = line_number_color;
}
cache.font->draw_string(get_canvas_item(), Point2(p_region.position.x, yofs + cache.font->get_ascent()), fc, HALIGN_LEFT, -1, cache.font_size, number_color);
tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color);
}
/* Fold Gutter */
@ -368,7 +370,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
while (lc /= 10) {
line_number_digits++;
}
set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width);
int from_line = MIN(p_from_line, p_to_line);
int line_count = (p_to_line - p_from_line);
@ -410,6 +412,10 @@ void CodeEdit::_update_gutter_indexes() {
}
CodeEdit::CodeEdit() {
/* Text Direction */
set_layout_direction(LAYOUT_DIRECTION_LTR);
set_text_direction(TEXT_DIRECTION_LTR);
/* Gutters */
int gutter_idx = 0;

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
#include "scene/resources/syntax_highlighter.h"
#include "scene/resources/text_paragraph.h"
class TextEdit : public Control {
GDCLASS(TextEdit, Control);
@ -87,47 +88,68 @@ private:
struct Line {
Vector<Gutter> gutters;
int32_t width_cache;
String data;
Vector<Vector2i> bidi_override;
Ref<TextParagraph> data_buf;
bool marked;
bool hidden;
int32_t wrap_amount_cache;
String data;
Line() {
width_cache = 0;
data_buf.instance();
marked = false;
hidden = false;
wrap_amount_cache = 0;
}
};
private:
mutable Vector<Line> text;
Ref<Font> font;
int font_size = -1;
Dictionary opentype_features;
String language;
TextServer::Direction direction = TextServer::DIRECTION_AUTO;
bool draw_control_chars = false;
int width = -1;
int indent_size = 4;
int gutter_count = 0;
void _update_line_cache(int p_line) const;
public:
void set_indent_size(int p_indent_size);
void set_font(const Ref<Font> &p_font);
void set_font_size(int p_font_size);
void set_font_features(const Dictionary &p_features);
void set_direction_and_language(TextServer::Direction p_direction, String p_language);
void set_draw_control_chars(bool p_draw_control_chars);
int get_line_height(int p_line, int p_wrap_index) const;
int get_line_width(int p_line) const;
int get_max_width(bool p_exclude_hidden = false) const;
int get_char_width(char32_t c, char32_t next_c, int px) const;
void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
void set_width(float p_width);
int get_line_wrap_amount(int p_line) const;
void set(int p_line, const String &p_text);
Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
const Ref<TextParagraph> get_line_data(int p_line) const;
void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override);
void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
bool is_marked(int p_line) const { return text[p_line].marked; }
void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
void insert(int p_at, const String &p_text);
void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override);
void remove(int p_at);
int size() const { return text.size(); }
void clear();
void clear_width_cache();
void clear_wrap_cache();
_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Vector<Vector2i> &p_bidi_override = Vector<Vector2i>());
void invalidate_all();
void invalidate_all_lines();
_FORCE_INLINE_ const String &operator[](int p_line) const;
/* Gutters. */
void add_gutter(int p_at);
@ -260,6 +282,14 @@ private:
// data
Text text;
Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextDirection input_direction = TEXT_DIRECTION_LTR;
Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
Array st_args;
bool draw_control_chars = false;
uint32_t version;
uint32_t saved_version;
@ -275,6 +305,7 @@ private:
bool window_has_focus;
bool block_caret;
bool right_click_moves_caret;
bool mid_grapheme_caret_enabled = false;
bool wrap_enabled;
int wrap_at;
@ -356,7 +387,7 @@ private:
int _get_minimap_visible_rows() const;
void update_cursor_wrap_offset();
void _update_wrap_at();
void _update_wrap_at(bool p_force = false);
bool line_wraps(int line) const;
int times_line_wraps(int line) const;
Vector<String> get_wrap_rows_text(int p_line) const;
@ -376,8 +407,6 @@ private:
int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const;
int get_column_x_offset_for_line(int p_char, int p_line) const;
int get_char_pos_for(int p_px, String p_str) const;
int get_column_x_offset(int p_char, String p_str) const;
void adjust_viewport_to_cursor();
double get_scroll_line_diff() const;
@ -405,6 +434,8 @@ private:
Size2 get_minimum_size() const override;
int _get_control_height() const;
Point2 _get_local_mouse_pos() const;
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@ -425,6 +456,8 @@ private:
Dictionary _search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const;
PopupMenu *menu;
PopupMenu *menu_dir;
PopupMenu *menu_ctl;
void _clear();
void _cancel_completion();
@ -465,11 +498,9 @@ protected:
Color search_result_border_color;
Color background_color;
int row_height;
int line_spacing;
int minimap_width;
Cache() {
row_height = 0;
line_spacing = 0;
minimap_width = 0;
}
@ -488,6 +519,10 @@ protected:
static void _bind_methods();
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
/* Syntax Highlighting. */
Ref<SyntaxHighlighter> get_syntax_highlighter();
@ -542,6 +577,27 @@ public:
MENU_SELECT_ALL,
MENU_UNDO,
MENU_REDO,
MENU_DIR_INHERITED,
MENU_DIR_AUTO,
MENU_DIR_LTR,
MENU_DIR_RTL,
MENU_DISPLAY_UCC,
MENU_INSERT_LRM,
MENU_INSERT_RLM,
MENU_INSERT_LRE,
MENU_INSERT_RLE,
MENU_INSERT_LRO,
MENU_INSERT_RLO,
MENU_INSERT_PDF,
MENU_INSERT_ALM,
MENU_INSERT_LRI,
MENU_INSERT_RLI,
MENU_INSERT_FSI,
MENU_INSERT_PDI,
MENU_INSERT_ZWJ,
MENU_INSERT_ZWNJ,
MENU_INSERT_WJ,
MENU_INSERT_SHY,
MENU_MAX
};
@ -565,6 +621,25 @@ public:
bool is_insert_text_operation();
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
void set_opentype_feature(const String &p_name, int p_value);
int get_opentype_feature(const String &p_name) const;
void clear_opentype_features();
void set_language(const String &p_language);
String get_language() const;
void set_draw_control_chars(bool p_draw_control_chars);
bool get_draw_control_chars() const;
void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
Control::StructuredTextParser get_structured_text_bidi_override() const;
void set_structured_text_bidi_override_options(Array p_args);
Array get_structured_text_bidi_override_options() const;
void set_highlighted_word(const String &new_word);
void set_text(String p_text);
void insert_text_at_cursor(const String &p_text);
@ -617,6 +692,9 @@ public:
void center_viewport_to_cursor();
void set_mid_grapheme_caret_enabled(const bool p_enabled);
bool get_mid_grapheme_caret_enabled() const;
void cursor_set_column(int p_col, bool p_adjust_viewport = true);
void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0);