summaryrefslogtreecommitdiffstats
path: root/scene/gui/code_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/code_edit.cpp')
-rw-r--r--scene/gui/code_edit.cpp203
1 files changed, 156 insertions, 47 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 8131fe7aaa..635228670d 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -40,8 +40,18 @@ void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
set_gutter_width(main_gutter, get_line_height());
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
+ _update_line_number_gutter_width();
set_gutter_width(fold_gutter, get_line_height() / 1.2);
+ _clear_line_number_text_cache();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ [[fallthrough]];
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ [[fallthrough]];
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ // Avoid having many hidden text editors with unused cache filling up memory.
+ _clear_line_number_text_cache();
} break;
case NOTIFICATION_DRAW: {
@@ -211,7 +221,13 @@ void CodeEdit::_notification(int p_what) {
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
- draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
+ const Color color = code_completion_options[l].default_value;
+ const Rect2 rect = Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size);
+ if (color.a < 1.0) {
+ draw_texture_rect(theme_cache.completion_color_bg, rect, true);
+ }
+
+ draw_rect(rect, color);
}
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
@@ -251,6 +267,27 @@ void CodeEdit::_notification(int p_what) {
}
void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
+ Ref<InputEventPanGesture> pan_gesture = p_gui_input;
+ if (pan_gesture.is_valid() && code_completion_active && code_completion_rect.has_point(pan_gesture->get_position())) {
+ const real_t delta = pan_gesture->get_delta().y;
+ code_completion_pan_offset += delta;
+ if (code_completion_pan_offset <= -1.0) {
+ if (code_completion_current_selected > 0) {
+ code_completion_current_selected--;
+ code_completion_force_item_center = -1;
+ queue_redraw();
+ }
+ code_completion_pan_offset += 1.0f;
+ } else if (code_completion_pan_offset >= +1.0) {
+ if (code_completion_current_selected < code_completion_options.size() - 1) {
+ code_completion_current_selected++;
+ code_completion_force_item_center = -1;
+ queue_redraw();
+ }
+ code_completion_pan_offset -= 1.0f;
+ }
+ }
+
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
// Ignore mouse clicks in IME input mode, let TextEdit handle it.
@@ -285,6 +322,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (code_completion_current_selected > 0) {
code_completion_current_selected--;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
} break;
@@ -292,6 +330,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (code_completion_current_selected < code_completion_options.size() - 1) {
code_completion_current_selected++;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
} break;
@@ -301,6 +340,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1);
+ code_completion_pan_offset = 0.0f;
if (mb->is_double_click()) {
confirm_code_completion();
}
@@ -472,6 +512,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
code_completion_current_selected = code_completion_options.size() - 1;
}
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -483,6 +524,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
code_completion_current_selected = 0;
}
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -490,6 +532,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->is_action("ui_page_up", true)) {
code_completion_current_selected = MAX(0, code_completion_current_selected - theme_cache.code_completion_max_lines);
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -497,6 +540,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->is_action("ui_page_down", true)) {
code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + theme_cache.code_completion_max_lines);
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
accept_event();
return;
@@ -737,7 +781,7 @@ void CodeEdit::_backspace_internal(int p_caret) {
continue;
}
- if (to_line > 0 && _is_line_hidden(to_line - 1)) {
+ if (to_line > 0 && to_column == 0 && _is_line_hidden(to_line - 1)) {
unfold_line(to_line - 1);
}
@@ -787,6 +831,9 @@ void CodeEdit::_cut_internal(int p_caret) {
delete_selection(p_caret);
return;
}
+ if (!is_empty_selection_clipboard_enabled()) {
+ return;
+ }
if (p_caret == -1) {
delete_lines();
} else {
@@ -1253,9 +1300,9 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const {
}
void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+ bool hovering = get_hovered_gutter() == Vector2i(main_gutter, p_line);
if (draw_breakpoints && theme_cache.breakpoint_icon.is_valid()) {
bool breakpointed = is_line_breakpointed(p_line);
- bool hovering = p_region.has_point(get_local_mouse_pos());
bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT);
if (breakpointed || (hovering && !is_dragging_cursor() && !shift_pressed)) {
@@ -1274,7 +1321,6 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2
if (draw_bookmarks && theme_cache.bookmark_icon.is_valid()) {
bool bookmarked = is_line_bookmarked(p_line);
- bool hovering = p_region.has_point(get_local_mouse_pos());
bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT);
if (bookmarked || (hovering && !is_dragging_cursor() && shift_pressed)) {
@@ -1408,7 +1454,13 @@ bool CodeEdit::is_draw_line_numbers_enabled() const {
}
void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
- p_zero_padded ? line_number_padding = "0" : line_number_padding = " ";
+ String new_line_number_padding = p_zero_padded ? "0" : " ";
+ if (line_number_padding == new_line_number_padding) {
+ return;
+ }
+
+ line_number_padding = new_line_number_padding;
+ _clear_line_number_text_cache();
queue_redraw();
}
@@ -1417,19 +1469,55 @@ 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);
- if (is_localizing_numeral_system()) {
- fc = TS->format_number(fc);
- }
- Ref<TextLine> tl;
- tl.instantiate();
- tl->add_string(fc, theme_cache.font, theme_cache.font_size);
- int yofs = p_region.position.y + (get_line_height() - tl->get_size().y) / 2;
+ if (!Rect2(Vector2(0, 0), get_size()).intersects(p_region)) {
+ return;
+ }
+
+ bool rtl = is_layout_rtl();
+ HashMap<int, RID>::Iterator E = line_number_text_cache.find(p_line);
+ RID text_rid;
+ if (E) {
+ text_rid = E->value;
+ } else {
+ String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
+ if (is_localizing_numeral_system()) {
+ fc = TS->format_number(fc);
+ }
+
+ text_rid = TS->create_shaped_text();
+ if (theme_cache.font.is_valid()) {
+ TS->shaped_text_add_string(text_rid, fc, theme_cache.font->get_rids(), theme_cache.font_size, theme_cache.font->get_opentype_features());
+ }
+ line_number_text_cache.insert(p_line, text_rid);
+ }
+
+ Size2 text_size = TS->shaped_text_get_size(text_rid);
+ Point2 ofs = p_region.get_center() - text_size / 2;
+ ofs.y += TS->shaped_text_get_ascent(text_rid);
+
+ if (rtl) {
+ ofs.x = p_region.position.x;
+ } else {
+ ofs.x = p_region.get_end().x - text_size.width;
+ }
+
Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
if (number_color == Color(1, 1, 1)) {
number_color = theme_cache.line_number_color;
}
- tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color);
+
+ TS->shaped_text_draw(text_rid, get_canvas_item(), ofs, -1, -1, number_color);
+}
+
+void CodeEdit::_clear_line_number_text_cache() {
+ for (const KeyValue<int, RID> &KV : line_number_text_cache) {
+ TS->free_rid(KV.value);
+ }
+ line_number_text_cache.clear();
+}
+
+void CodeEdit::_update_line_number_gutter_width() {
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
}
/* Fold Gutter */
@@ -1949,12 +2037,18 @@ Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const {
/* Code hint */
void CodeEdit::set_code_hint(const String &p_hint) {
+ if (code_hint == p_hint) {
+ return;
+ }
code_hint = p_hint;
code_hint_xpos = -0xFFFF;
queue_redraw();
}
void CodeEdit::set_code_hint_draw_below(bool p_below) {
+ if (code_hint_draw_below == p_below) {
+ return;
+ }
code_hint_draw_below = p_below;
queue_redraw();
}
@@ -2119,6 +2213,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) {
ERR_FAIL_INDEX(p_index, code_completion_options.size());
code_completion_current_selected = p_index;
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
queue_redraw();
}
@@ -2331,18 +2426,19 @@ void CodeEdit::move_lines_up() {
unfold_line(line);
swap_lines(line - 1, line);
}
- }
-
- // Fix selection if it ended at column 0, since it wasn't moved.
- for (int i = 0; i < get_caret_count(); i++) {
- if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) != 0) {
- if (is_caret_after_selection_origin(i)) {
- set_caret_line(get_caret_line(i) - 1, false, true, -1, i);
- } else {
- set_selection_origin_line(get_selection_origin_line(i) - 1, true, -1, i);
+ // Fix selection if the last one ends at column 0, since it wasn't moved.
+ for (int i = 0; i < get_caret_count(); i++) {
+ if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) == line_range.y + 1) {
+ if (is_caret_after_selection_origin(i)) {
+ set_caret_line(get_caret_line(i) - 1, false, true, -1, i);
+ } else {
+ set_selection_origin_line(get_selection_origin_line(i) - 1, true, -1, i);
+ }
+ break;
}
}
}
+ adjust_viewport_to_caret();
end_multicaret_edit();
end_complex_operation();
@@ -2352,30 +2448,41 @@ void CodeEdit::move_lines_down() {
begin_complex_operation();
begin_multicaret_edit();
- Vector<Point2i> line_ranges = get_line_ranges_from_carets();
-
- // Fix selection if it ended at column 0, since it won't be moved.
- for (int i = 0; i < get_caret_count(); i++) {
- if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) != get_line_count() - 1) {
- if (is_caret_after_selection_origin(i)) {
- set_caret_line(get_caret_line(i) + 1, false, true, -1, i);
- } else {
- set_selection_origin_line(get_selection_origin_line(i) + 1, true, -1, i);
- }
- }
- }
-
// Move lines down by swapping each line with the one below it.
+ Vector<Point2i> line_ranges = get_line_ranges_from_carets();
+ // Reverse in case line ranges are adjacent, if the first ends at column 0.
+ line_ranges.reverse();
for (Point2i line_range : line_ranges) {
if (line_range.y == get_line_count() - 1) {
continue;
}
+ // Fix selection if the last one ends at column 0, since it won't be moved.
+ bool selection_to_line_at_end = false;
+ for (int i = 0; i < get_caret_count(); i++) {
+ if (has_selection(i) && get_selection_to_column(i) == 0 && get_selection_to_line(i) == line_range.y + 1) {
+ selection_to_line_at_end = get_selection_to_line(i) == get_line_count() - 1;
+ if (selection_to_line_at_end) {
+ break;
+ }
+ if (is_caret_after_selection_origin(i)) {
+ set_caret_line(get_caret_line(i) + 1, false, true, -1, i);
+ } else {
+ set_selection_origin_line(get_selection_origin_line(i) + 1, true, -1, i);
+ }
+ break;
+ }
+ }
+ if (selection_to_line_at_end) {
+ continue;
+ }
+
unfold_line(line_range.y + 1);
for (int line = line_range.y; line >= line_range.x; line--) {
unfold_line(line);
swap_lines(line + 1, line);
}
}
+ adjust_viewport_to_caret();
end_multicaret_edit();
end_complex_operation();
@@ -2730,6 +2837,7 @@ void CodeEdit::_bind_methods() {
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, can_fold_code_region_icon, "can_fold_code_region");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, folded_code_region_icon, "folded_code_region");
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, folded_eol_icon);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, completion_color_bg);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_color);
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, breakpoint_icon, "breakpoint");
@@ -3050,7 +3158,7 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
}
int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const {
- if (delimiters.size() == 0) {
+ if (delimiters.size() == 0 || p_line >= delimiter_cache.size()) {
return -1;
}
ERR_FAIL_INDEX_V(p_line, get_line_count(), 0);
@@ -3232,6 +3340,7 @@ void CodeEdit::_update_scroll_selected_line(float p_mouse_y) {
code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1));
code_completion_force_item_center = -1;
+ code_completion_pan_offset = 0.0f;
}
void CodeEdit::_filter_code_completion_candidates_impl() {
@@ -3293,6 +3402,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
code_completion_current_selected = 0;
+ code_completion_pan_offset = 0.0f;
}
code_completion_options = code_completion_options_new;
@@ -3508,6 +3618,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_options_new.sort_custom<CodeCompletionOptionCompare>();
if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
code_completion_current_selected = 0;
+ code_completion_pan_offset = 0.0f;
}
code_completion_options = code_completion_options_new;
@@ -3558,16 +3669,13 @@ void CodeEdit::_text_changed() {
}
int lc = get_line_count();
- line_number_digits = 1;
- while (lc /= 10) {
- line_number_digits++;
- }
-
- if (theme_cache.font.is_valid()) {
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
+ int new_line_number_digits = log10l(lc) + 1;
+ if (line_number_digits != new_line_number_digits) {
+ _clear_line_number_text_cache();
}
+ line_number_digits = new_line_number_digits;
+ _update_line_number_gutter_width();
- lc = get_line_count();
List<int> breakpoints;
for (const KeyValue<int, bool> &E : breakpointed_lines) {
breakpoints.push_back(E.key);
@@ -3645,7 +3753,7 @@ CodeEdit::CodeEdit() {
connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
connect("text_set", callable_mp(this, &CodeEdit::_text_set));
- connect("text_changed", callable_mp(this, &CodeEdit::_text_changed));
+ connect(SceneStringName(text_changed), callable_mp(this, &CodeEdit::_text_changed));
connect("gutter_clicked", callable_mp(this, &CodeEdit::_gutter_clicked));
connect("gutter_added", callable_mp(this, &CodeEdit::_update_gutter_indexes));
@@ -3654,6 +3762,7 @@ CodeEdit::CodeEdit() {
}
CodeEdit::~CodeEdit() {
+ _clear_line_number_text_cache();
}
// Return true if l should come before r