Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed completion and hint panel positioning in TextExit #45110

Merged
merged 1 commit into from
Feb 1, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 64 additions & 42 deletions scene/gui/text_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -810,8 +810,8 @@ void TextEdit::_notification(int p_what) {
}
}

bool is_cursor_line_visible = false;
Point2 cursor_pos;
int cursor_insert_offset_y = 0;

// Get the highlighted words.
String highlighted_text = get_selection_text();
Expand Down Expand Up @@ -1238,6 +1238,7 @@ void TextEdit::_notification(int p_what) {
}
}

const int line_top_offset_y = ofs_y;
ofs_y += (row_height - text_height) / 2;

const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
Expand Down Expand Up @@ -1320,7 +1321,9 @@ void TextEdit::_notification(int p_what) {
int caret_width = 1;
#endif
if (cursor.line == line && ((line_wrap_index == line_wrap_amount) || (cursor.column != TS->shaped_text_get_range(rid).y))) {
cursor_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
is_cursor_line_visible = true;
cursor_pos.y = line_top_offset_y;

if (ime_text.length() == 0) {
Rect2 l_caret, t_caret;
TextServer::Direction l_dir, t_dir;
Expand Down Expand Up @@ -1439,78 +1442,97 @@ void TextEdit::_notification(int p_what) {
}

bool completion_below = false;
if (completion_active && completion_options.size() > 0) {
// Code completion box.
Ref<StyleBox> csb = get_theme_stylebox("completion");
int maxlines = get_theme_constant("completion_lines");
int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x', 0, cache.font_size).x;
int scrollw = get_theme_constant("completion_scroll_width");
Color scrollc = get_theme_color("completion_scroll_color");
if (completion_active && is_cursor_line_visible && completion_options.size() > 0) {
// Completion panel

const Ref<StyleBox> csb = get_theme_stylebox("completion");
const int maxlines = get_theme_constant("completion_lines");
const int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x', 0, cache.font_size).x;
const Color scrollc = get_theme_color("completion_scroll_color");

const int completion_options_size = completion_options.size();
int lines = MIN(completion_options_size, maxlines);
int w = 0;
int h = lines * row_height;
int nofs = cache.font->get_string_size(completion_base, cache.font_size).width;
const int row_count = MIN(completion_options_size, maxlines);
const int completion_rows_height = row_count * row_height;
const int completion_base_width = cache.font->get_string_size(completion_base, cache.font_size).width;

int scroll_rectangle_width = get_theme_constant("completion_scroll_width");
int width = 0;

// Compute max width of the panel based on the longest completion option
if (completion_options_size < 50) {
for (int i = 0; i < completion_options_size; i++) {
int w2 = MIN(cache.font->get_string_size(completion_options[i].display, cache.font_size).x, cmax_width);
if (w2 > w) {
w = w2;
int line_width = MIN(cache.font->get_string_size(completion_options[i].display, cache.font_size).x, cmax_width);
if (line_width > width) {
width = line_width;
}
}
} else {
w = cmax_width;
width = cmax_width;
}

// Add space for completion icons.
const int icon_hsep = get_theme_constant("hseparation", "ItemList");
Size2 icon_area_size(row_height, row_height);
w += icon_area_size.width + icon_hsep;
const Size2 icon_area_size(row_height, row_height);
const int icon_area_width = icon_area_size.width + icon_hsep;
width += icon_area_width;

int line_from = CLAMP(completion_index - lines / 2, 0, completion_options_size - lines);
const int line_from = CLAMP(completion_index - row_count / 2, 0, completion_options_size - row_count);

for (int i = 0; i < lines; i++) {
for (int i = 0; i < row_count; i++) {
int l = line_from + i;
ERR_CONTINUE(l < 0 || l >= completion_options_size);
if (completion_options[l].default_value.get_type() == Variant::COLOR) {
w += icon_area_size.width;
width += icon_area_size.width;
break;
}
}

int th = h + csb->get_minimum_size().y;
// Position completion panel
completion_rect.size.width = width + 2;
completion_rect.size.height = completion_rows_height;

if (cursor_pos.y + row_height + th > get_size().height) {
completion_rect.position.y = cursor_pos.y - th - (cache.line_spacing / 2.0f) - cursor_insert_offset_y;
} else {
completion_rect.position.y = cursor_pos.y + cache.font->get_height(cache.font_size) + (cache.line_spacing / 2.0f) + csb->get_offset().y - cursor_insert_offset_y;
completion_below = true;
if (completion_options_size <= maxlines) {
scroll_rectangle_width = 0;
}

if (cursor_pos.x - nofs + w + scrollw > get_size().width) {
completion_rect.position.x = get_size().width - w - scrollw;
const Point2 csb_offset = csb->get_offset();

const int total_width = completion_rect.size.width + csb->get_minimum_size().x + scroll_rectangle_width;
const int total_height = completion_rect.size.height + csb->get_minimum_size().y;

const int rect_left_border_x = cursor_pos.x - completion_base_width - icon_area_width - csb_offset.x;
const int rect_right_border_x = rect_left_border_x + total_width;

if (rect_left_border_x < 0) {
// Anchor the completion panel to the left
completion_rect.position.x = 0;
} else if (rect_right_border_x > get_size().width) {
// Anchor the completion panel to the right
completion_rect.position.x = get_size().width - total_width;
} else {
completion_rect.position.x = cursor_pos.x - nofs;
// Let the completion panel float with the cursor
completion_rect.position.x = rect_left_border_x;
}

completion_rect.size.width = w + 2;
completion_rect.size.height = h;
if (completion_options_size <= maxlines) {
scrollw = 0;
if (cursor_pos.y + row_height + total_height > get_size().height) {
// Completion panel above the cursor line
completion_rect.position.y = cursor_pos.y - total_height;
} else {
// Completion panel below the cursor line
completion_rect.position.y = cursor_pos.y + row_height;
completion_below = true;
}

draw_style_box(csb, Rect2(completion_rect.position - csb->get_offset(), completion_rect.size + csb->get_minimum_size() + Size2(scrollw, 0)));
draw_style_box(csb, Rect2(completion_rect.position - csb_offset, completion_rect.size + csb->get_minimum_size() + Size2(scroll_rectangle_width, 0)));

if (cache.completion_background_color.a > 0.01) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scrollw, 0)), cache.completion_background_color);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scroll_rectangle_width, 0)), cache.completion_background_color);
}
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(completion_rect.position.x, completion_rect.position.y + (completion_index - line_from) * get_row_height()), Size2(completion_rect.size.width, get_row_height())), cache.completion_selected_color);

draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(nofs, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color);
draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(completion_base_width, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color);

for (int i = 0; i < lines; i++) {
for (int i = 0; i < row_count; i++) {
int l = line_from + i;
ERR_CONTINUE(l < 0 || l >= completion_options_size);

Expand Down Expand Up @@ -1550,19 +1572,19 @@ void TextEdit::_notification(int p_what) {
tl->draw(ci, title_pos, completion_options[l].font_color);
}

if (scrollw) {
if (scroll_rectangle_width) {
// Draw a small scroll rectangle to show a position in the options.
float r = (float)maxlines / completion_options_size;
float o = (float)line_from / completion_options_size;
draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scrollw, completion_rect.size.y * r), scrollc);
draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scroll_rectangle_width, completion_rect.size.y * r), scrollc);
}

completion_line_ofs = line_from;
}

// Check to see if the hint should be drawn.
bool show_hint = false;
if (completion_hint != "") {
if (is_cursor_line_visible && completion_hint != "") {
if (completion_active) {
if (completion_below && !callhint_below) {
show_hint = true;
Expand Down