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

Add Trait System to GDScript #97657

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ void Object::set_script(const Variant &p_script) {
if (!p_script.is_null()) {
ERR_FAIL_COND_MSG(s.is_null(), "Cannot set object script. Parameter should be null or a reference to a valid script.");
ERR_FAIL_COND_MSG(s->is_abstract(), vformat("Cannot set object script. Script '%s' should not be abstract.", s->get_path()));
ERR_FAIL_COND_MSG(!s->is_attachable(), vformat("Cannot set object script. Script '%s' is not attachable.", s->get_path()));
}

script = p_script;
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ void Script::_bind_methods() {

ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
ClassDB::bind_method(D_METHOD("is_abstract"), &Script::is_abstract);
ClassDB::bind_method(D_METHOD("is_attachable"), &Script::is_attachable);

ClassDB::bind_method(D_METHOD("get_rpc_config"), &Script::get_rpc_config);

Expand Down
2 changes: 2 additions & 0 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class Script : public Resource {
virtual bool is_tool() const = 0;
virtual bool is_valid() const = 0;
virtual bool is_abstract() const = 0;
virtual bool is_attachable() const { return true; }

virtual ScriptLanguage *get_language() const = 0;

Expand Down Expand Up @@ -214,6 +215,7 @@ class ScriptLanguage : public Object {
virtual void init() = 0;
virtual String get_type() const = 0;
virtual String get_extension() const = 0;
virtual bool is_language_script_attachable() const { return true; }
virtual void finish() = 0;

/* EDITOR FUNCTIONS */
Expand Down
8 changes: 7 additions & 1 deletion doc/classes/Script.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<description>
A class stored as a resource. A script extends the functionality of all objects that instantiate it.
This is the base class for all scripts and should not be used directly. Trying to create a new script with this class will result in an error.
The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes and if script is attachable check by [method is_attachable].
</description>
<tutorials>
<link title="Scripting documentation index">$DOCS_URL/tutorials/scripting/index.html</link>
Expand Down Expand Up @@ -115,6 +115,12 @@
Returns [code]true[/code] if the script is an abstract script. An abstract script does not have a constructor and cannot be instantiated.
</description>
</method>
<method name="is_attachable" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the script is able to be attached to [Object].
</description>
</method>
<method name="is_tool" qualifiers="const">
<return type="bool" />
<description>
Expand Down
5 changes: 4 additions & 1 deletion editor/debugger/script_editor_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
stack_dump_info.push_back(d);
s->set_metadata(0, d);

String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]);
String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]);
if (!String(d["function"]).is_empty()) {
line += " - at function: " + String(d["function"]);
}
s->set_text(0, line);

if (i == 0) {
Expand Down
1 change: 1 addition & 0 deletions editor/filesystem_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4266,6 +4266,7 @@ FileSystemDock::FileSystemDock() {
make_scene_dialog->connect(SceneStringName(confirmed), callable_mp(this, &FileSystemDock::_make_scene_confirm));

make_script_dialog = memnew(ScriptCreateDialog);
make_script_dialog->set_languages_list(false);
make_script_dialog->set_title(TTR("Create Script"));
add_child(make_script_dialog);

Expand Down
106 changes: 68 additions & 38 deletions editor/script_create_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,32 +129,16 @@ void ScriptCreateDialog::_notification(int p_what) {
} break;

case NOTIFICATION_THEME_CHANGED: {
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));

EditorData &ed = EditorNode::get_editor_data();

for (int i = 0; i < ScriptServer::get_language_count(); i++) {
// Check if the extension has an icon first.
String script_type = ScriptServer::get_language(i)->get_type();
Ref<Texture2D> language_icon = get_editor_theme_icon(script_type);
if (language_icon.is_null() || language_icon == ThemeDB::get_singleton()->get_fallback_icon()) {
// The theme doesn't have an icon for this language, ask the extensions.
Ref<Texture2D> extension_language_icon = ed.extension_class_get_icon(script_type);
if (extension_language_icon.is_valid()) {
language_menu->get_popup()->set_item_icon_max_width(i, icon_size);
language_icon = extension_language_icon;
}
}

if (language_icon.is_valid()) {
language_menu->set_item_icon(i, language_icon);
}
}
set_languages_list(is_languages_list_only_attachable);

path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
parent_browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
parent_search_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList")));
} break;

case NOTIFICATION_POSTINITIALIZE: {
set_languages_list(is_languages_list_only_attachable);
} break;
}
}

Expand Down Expand Up @@ -210,6 +194,51 @@ void ScriptCreateDialog::set_inheritance_base_type(const String &p_base) {
base_type = p_base;
}

void ScriptCreateDialog::set_languages_list(const bool p_only_attachable) {
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
EditorData &ed = EditorNode::get_editor_data();
is_languages_list_only_attachable = p_only_attachable;
String previous_default;
if (language_menu->get_selected_id() > -1) {
previous_default = language_menu->get_item_text(language_menu->get_selected_id());
}
default_language = -1;
language_menu->clear();
language_list.clear();
int menu_location = 0;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
if (p_only_attachable && !lang->is_language_script_attachable()) {
continue;
}
language_menu->add_item(lang->get_name());
String script_type = lang->get_type();
Ref<Texture2D> language_icon = get_editor_theme_icon(script_type);
if (language_icon.is_null() || language_icon == ThemeDB::get_singleton()->get_fallback_icon()) {
// The theme doesn't have an icon for this language, ask the extensions.
Ref<Texture2D> extension_language_icon = ed.extension_class_get_icon(script_type);
if (extension_language_icon.is_valid()) {
language_menu->get_popup()->set_item_icon_max_width(menu_location, icon_size);
language_icon = extension_language_icon;
}
}
if (language_icon.is_valid()) {
language_menu->set_item_icon(menu_location, language_icon);
}
if (lang->get_name() == previous_default) {
default_language = menu_location;
}
if (default_language == -1 && lang->get_name() == "GDScript") {
default_language = menu_location;
}
language_list.append(lang);
menu_location++;
}
if (default_language >= 0) {
language_menu->select(default_language);
}
}

bool ScriptCreateDialog::_validate_parent(const String &p_string) {
if (p_string.length() == 0) {
return false;
Expand Down Expand Up @@ -278,7 +307,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
for (const String &E : extensions) {
if (E.nocasecmp_to(extension) == 0) {
found = true;
if (E == ScriptServer::get_language(language_menu->get_selected())->get_extension()) {
if (E == language->get_extension()) {
match = true;
}
break;
Expand All @@ -288,12 +317,25 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must
if (!found) {
return TTR("Invalid extension.");
}
if (!match) {
if (!match && _extention_update_selected_language(p.get_extension()) != OK) {
return TTR("Extension doesn't match chosen language.");
}

// Let ScriptLanguage do custom validation.
return ScriptServer::get_language(language_menu->get_selected())->validate_path(p);
return language->validate_path(p);
}

Error ScriptCreateDialog::_extention_update_selected_language(const String &p_extention) {
for (int i = 0; i < language_list.size(); i++) {
ScriptLanguage *lang = language_list[i];
if (p_extention == lang->get_extension()) {
language_menu->select(i);
_language_changed(i);
file_path->set_caret_column(file_path->get_text().length());
return OK;
}
}
return FAILED;
}

void ScriptCreateDialog::_parent_name_changed(const String &p_parent) {
Expand Down Expand Up @@ -360,7 +402,7 @@ void ScriptCreateDialog::_create_new() {
}

String class_name = file_path->get_text().get_file().get_basename();
scr = ScriptServer::get_language(language_menu->get_selected())->make_template(sinfo.content, class_name, parent_class);
scr = language->make_template(sinfo.content, class_name, parent_class);

if (is_built_in) {
scr->set_name(built_in_name->get_text());
Expand Down Expand Up @@ -396,7 +438,7 @@ void ScriptCreateDialog::_load_exist() {
}

void ScriptCreateDialog::_language_changed(int l) {
language = ScriptServer::get_language(l);
language = language_list[l];

can_inherit_from_file = language->can_inherit_from_file();
supports_built_in = language->supports_builtin_mode();
Expand Down Expand Up @@ -872,18 +914,6 @@ ScriptCreateDialog::ScriptCreateDialog() {
gc->add_child(memnew(Label(TTR("Language:"))));
gc->add_child(language_menu);

default_language = -1;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
String lang = ScriptServer::get_language(i)->get_name();
language_menu->add_item(lang);
if (lang == "GDScript") {
default_language = i;
}
}
if (default_language >= 0) {
language_menu->select(default_language);
}

language_menu->connect(SceneStringName(item_selected), callable_mp(this, &ScriptCreateDialog::_language_changed));

/* Inherits */
Expand Down
4 changes: 4 additions & 0 deletions editor/script_create_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
AcceptDialog *alert = nullptr;
CreateDialog *select_class = nullptr;

bool is_languages_list_only_attachable = true;
bool is_browsing_parent = false;
String path_error;
String template_inactive_message;
Expand All @@ -89,6 +90,7 @@ class ScriptCreateDialog : public ConfirmationDialog {

Vector<ScriptLanguage::ScriptTemplate> template_list;
ScriptLanguage *language = nullptr;
Vector<ScriptLanguage *> language_list;

String base_type;

Expand All @@ -100,6 +102,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
void _use_template_pressed();
bool _validate_parent(const String &p_string);
String _validate_path(const String &p_path, bool p_file_must_exist);
Error _extention_update_selected_language(const String &p_extention);
void _parent_name_changed(const String &p_parent);
void _template_changed(int p_template = 0);
void _browse_path(bool browse_parent, bool p_save);
Expand All @@ -124,6 +127,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
public:
void config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled = true, bool p_load_enabled = true);
void set_inheritance_base_type(const String &p_base);
void set_languages_list(const bool p_only_attachable);
ScriptCreateDialog();
};

Expand Down
4 changes: 4 additions & 0 deletions modules/gdscript/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[*.gd]
indent_size = 4
trim_trailing_whitespace = true

[*.gdt]
indent_size = 4
trim_trailing_whitespace = true
2 changes: 2 additions & 0 deletions modules/gdscript/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ def configure(env):
def get_doc_classes():
return [
"@GDScript",
"@GDTrait",
"GDScript",
"GDTrait",
"GDScriptSyntaxHighlighter",
]

Expand Down
Loading
Loading