Show icons for code completion options

This commit is contained in:
Geequlim 2019-06-13 17:32:03 +08:00
parent 1ddb610255
commit 10cfd87414
5 changed files with 98 additions and 35 deletions

View file

@ -216,6 +216,7 @@ struct ScriptCodeCompletionOption {
Kind kind; Kind kind;
String display; String display;
String insert_text; String insert_text;
RES icon;
ScriptCodeCompletionOption() { ScriptCodeCompletionOption() {
kind = KIND_PLAIN_TEXT; kind = KIND_PLAIN_TEXT;

View file

@ -734,15 +734,54 @@ void CodeTextEditor::_complete_request() {
if (entries.size() == 0) if (entries.size() == 0)
return; return;
Vector<String> options;
options.resize(entries.size());
size_t i = 0;
for (List<ScriptCodeCompletionOption>::Element *E = entries.front(); E; E = E->next()) { for (List<ScriptCodeCompletionOption>::Element *E = entries.front(); E; E = E->next()) {
options.write[i] = E->get().insert_text; E->get().icon = _get_completion_icon(E->get());
i++; }
text_editor->code_complete(entries, forced);
} }
text_editor->code_complete(options, forced); Ref<Texture> CodeTextEditor::_get_completion_icon(const ScriptCodeCompletionOption &p_option) {
Ref<Texture> tex;
switch (p_option.kind) {
case ScriptCodeCompletionOption::KIND_CLASS: {
if (has_icon(p_option.display, "EditorIcons")) {
tex = get_icon(p_option.display, "EditorIcons");
} else {
tex = get_icon("Object", "EditorIcons");
}
} break;
case ScriptCodeCompletionOption::KIND_ENUM:
tex = get_icon("Enum", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_FILE_PATH:
tex = get_icon("File", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_NODE_PATH:
tex = get_icon("NodePath", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_VARIABLE:
tex = get_icon("Variant", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_CONSTANT:
tex = get_icon("MemberConstant", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_MEMBER:
tex = get_icon("MemberProperty", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_SIGNAL:
tex = get_icon("MemberSignal", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_FUNCTION:
tex = get_icon("MemberMethod", "EditorIcons");
break;
case ScriptCodeCompletionOption::KIND_PLAIN_TEXT:
tex = get_icon("CubeMesh", "EditorIcons");
break;
default:
tex = get_icon("String", "EditorIcons");
break;
}
return tex;
} }
void CodeTextEditor::_font_resize_timeout() { void CodeTextEditor::_font_resize_timeout() {

View file

@ -162,6 +162,7 @@ class CodeTextEditor : public VBoxContainer {
void _update_font(); void _update_font();
void _complete_request(); void _complete_request();
Ref<Texture> _get_completion_icon(const ScriptCodeCompletionOption &p_option);
void _font_resize_timeout(); void _font_resize_timeout();
bool _add_font_size(int p_delta); bool _add_font_size(int p_delta);

View file

@ -35,6 +35,7 @@
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/project_settings.h" #include "core/project_settings.h"
#include "core/script_language.h"
#include "scene/main/viewport.h" #include "scene/main/viewport.h"
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
@ -1362,7 +1363,7 @@ void TextEdit::_notification(int p_what) {
if (completion_options.size() < 50) { if (completion_options.size() < 50) {
for (int i = 0; i < completion_options.size(); i++) { for (int i = 0; i < completion_options.size(); i++) {
int w2 = MIN(cache.font->get_string_size(completion_options[i]).x, cmax_width); int w2 = MIN(cache.font->get_string_size(completion_options[i].display).x, cmax_width);
if (w2 > w) if (w2 > w)
w = w2; w = w2;
} }
@ -1370,6 +1371,11 @@ void TextEdit::_notification(int p_what) {
w = cmax_width; w = cmax_width;
} }
// Add space for completion icons
const int icon_hsep = get_constant("hseparation", "ItemList");
Size2 icon_area_size(get_row_height(), get_row_height());
w += icon_area_size.width + icon_hsep;
int th = h + csb->get_minimum_size().y; int th = h + csb->get_minimum_size().y;
if (cursor_pos.y + get_row_height() + th > get_size().height) { if (cursor_pos.y + get_row_height() + th > get_size().height) {
@ -1405,12 +1411,26 @@ void TextEdit::_notification(int p_what) {
ERR_CONTINUE(l < 0 || l >= completion_options.size()); ERR_CONTINUE(l < 0 || l >= completion_options.size());
Color text_color = cache.completion_font_color; Color text_color = cache.completion_font_color;
for (int j = 0; j < color_regions.size(); j++) { for (int j = 0; j < color_regions.size(); j++) {
if (completion_options[l].begins_with(color_regions[j].begin_key)) { if (completion_options[l].insert_text.begins_with(color_regions[j].begin_key)) {
text_color = color_regions[j].color; text_color = color_regions[j].color;
} }
} }
int yofs = (get_row_height() - cache.font->get_height()) / 2; int yofs = (get_row_height() - cache.font->get_height()) / 2;
draw_string(cache.font, Point2(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs), completion_options[l], text_color, completion_rect.size.width); Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs);
//draw completion icon if it is valid
Ref<Texture> icon = completion_options[l].icon;
Rect2 icon_area(completion_rect.position.x, completion_rect.position.y + i * get_row_height(), icon_area_size.width, icon_area_size.height);
if (icon.is_valid()) {
const real_t max_scale = 0.7f;
const real_t side = max_scale * icon_area.size.width;
real_t scale = MIN(side / icon->get_width(), side / icon->get_height());
Size2 icon_size = icon->get_size() * scale;
draw_texture_rect(icon, Rect2(icon_area.position + (icon_area.size - icon_size) / 2, icon_size));
}
title_pos.x = icon_area.position.x + icon_area.size.width + icon_hsep;
draw_string(cache.font, title_pos, completion_options[l].display, text_color, completion_rect.size.width);
} }
if (scrollw) { if (scrollw) {
@ -5925,12 +5945,12 @@ void TextEdit::_confirm_completion() {
_remove_text(cursor.line, cursor.column - completion_base.length(), cursor.line, cursor.column); _remove_text(cursor.line, cursor.column - completion_base.length(), cursor.line, cursor.column);
cursor_set_column(cursor.column - completion_base.length(), false); cursor_set_column(cursor.column - completion_base.length(), false);
insert_text_at_cursor(completion_current); insert_text_at_cursor(completion_current.insert_text);
// When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket. // When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
String line = text[cursor.line]; String line = text[cursor.line];
CharType next_char = line[cursor.column]; CharType next_char = line[cursor.column];
CharType last_completion_char = completion_current[completion_current.length() - 1]; CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) { if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) {
_base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); _base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
@ -6066,39 +6086,41 @@ void TextEdit::_update_completion_candidates() {
completion_base = s; completion_base = s;
Vector<float> sim_cache; Vector<float> sim_cache;
bool single_quote = s.begins_with("'"); bool single_quote = s.begins_with("'");
Vector<String> completion_options_casei; Vector<ScriptCodeCompletionOption> completion_options_casei;
for (int i = 0; i < completion_strings.size(); i++) { for (List<ScriptCodeCompletionOption>::Element *E = completion_sources.front(); E; E = E->next()) {
if (single_quote && completion_strings[i].is_quoted()) { ScriptCodeCompletionOption &option = E->get();
completion_strings.write[i] = completion_strings[i].unquote().quote("'");
if (single_quote && option.display.is_quoted()) {
option.display = option.display.unquote().quote("'");
} }
if (inquote && restore_quotes == 1 && !completion_strings[i].is_quoted()) { if (inquote && restore_quotes == 1 && !option.display.is_quoted()) {
String quote = single_quote ? "'" : "\""; String quote = single_quote ? "'" : "\"";
completion_strings.write[i] = completion_strings[i].quote(quote); option.display = option.display.quote(quote);
} }
if (completion_strings[i].begins_with(s)) { if (option.display.begins_with(s)) {
completion_options.push_back(completion_strings[i]); completion_options.push_back(option);
} else if (completion_strings[i].to_lower().begins_with(s.to_lower())) { } else if (option.display.to_lower().begins_with(s.to_lower())) {
completion_options_casei.push_back(completion_strings[i]); completion_options_casei.push_back(option);
} }
} }
completion_options.append_array(completion_options_casei); completion_options.append_array(completion_options_casei);
if (completion_options.size() == 0) { if (completion_options.size() == 0) {
for (int i = 0; i < completion_strings.size(); i++) { for (int i = 0; i < completion_sources.size(); i++) {
if (s.is_subsequence_of(completion_strings[i])) { if (s.is_subsequence_of(completion_sources[i].display)) {
completion_options.push_back(completion_strings[i]); completion_options.push_back(completion_sources[i]);
} }
} }
} }
if (completion_options.size() == 0) { if (completion_options.size() == 0) {
for (int i = 0; i < completion_strings.size(); i++) { for (int i = 0; i < completion_sources.size(); i++) {
if (s.is_subsequence_ofi(completion_strings[i])) { if (s.is_subsequence_ofi(completion_sources[i].display)) {
completion_options.push_back(completion_strings[i]); completion_options.push_back(completion_sources[i]);
} }
} }
} }
@ -6109,7 +6131,7 @@ void TextEdit::_update_completion_candidates() {
return; return;
} }
if (completion_options.size() == 1 && s == completion_options[0]) { if (completion_options.size() == 1 && s == completion_options[0].display) {
// A perfect match, stop completion // A perfect match, stop completion
_cancel_completion(); _cancel_completion();
return; return;
@ -6147,12 +6169,12 @@ void TextEdit::set_code_hint(const String &p_hint) {
update(); update();
} }
void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) { void TextEdit::code_complete(const List<ScriptCodeCompletionOption> &p_strings, bool p_forced) {
completion_strings = p_strings; completion_sources = p_strings;
completion_active = true; completion_active = true;
completion_forced = p_forced; completion_forced = p_forced;
completion_current = ""; completion_current = ScriptCodeCompletionOption();
completion_index = 0; completion_index = 0;
_update_completion_candidates(); _update_completion_candidates();
} }

View file

@ -255,11 +255,11 @@ private:
Set<String> completion_prefixes; Set<String> completion_prefixes;
bool completion_enabled; bool completion_enabled;
Vector<String> completion_strings; List<ScriptCodeCompletionOption> completion_sources;
Vector<String> completion_options; Vector<ScriptCodeCompletionOption> completion_options;
bool completion_active; bool completion_active;
bool completion_forced; bool completion_forced;
String completion_current; ScriptCodeCompletionOption completion_current;
String completion_base; String completion_base;
int completion_index; int completion_index;
Rect2i completion_rect; Rect2i completion_rect;
@ -704,7 +704,7 @@ public:
void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
void set_completion(bool p_enabled, const Vector<String> &p_prefixes); void set_completion(bool p_enabled, const Vector<String> &p_prefixes);
void code_complete(const Vector<String> &p_strings, bool p_forced = false); void code_complete(const List<ScriptCodeCompletionOption> &p_strings, bool p_forced = false);
void set_code_hint(const String &p_hint); void set_code_hint(const String &p_hint);
void query_code_comple(); void query_code_comple();