Merge pull request #29744 from GodotExplorer/gdscript-completion-icons
Show icons for code completion options
This commit is contained in:
commit
259a3fb1e9
5 changed files with 98 additions and 35 deletions
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue