mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 04:06:14 +00:00
[Complex Text Layouts] Refactor TextEdit and CodeEdit controls.
This commit is contained in:
parent
d66eb77c9c
commit
3be31c4960
@ -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>
|
||||
|
@ -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");
|
||||
|
||||
if (font.is_valid()) {
|
||||
if (font->get_size() != (int)font_size) {
|
||||
font_size = font->get_size();
|
||||
}
|
||||
|
||||
font_size = text_editor->get_theme_font_size("font_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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user