diff options
Diffstat (limited to 'scene/gui/code_edit.cpp')
-rw-r--r-- | scene/gui/code_edit.cpp | 203 |
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 |