Path: blob/master/editor/plugins/editor_plugin_settings.cpp
9896 views
/**************************************************************************/1/* editor_plugin_settings.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "editor_plugin_settings.h"3132#include "core/config/project_settings.h"33#include "core/io/config_file.h"34#include "core/io/dir_access.h"35#include "core/io/file_access.h"36#include "editor/editor_node.h"37#include "editor/editor_string_names.h"38#include "editor/themes/editor_scale.h"39#include "scene/gui/margin_container.h"40#include "scene/gui/separator.h"41#include "scene/gui/texture_rect.h"42#include "scene/gui/tree.h"4344void EditorPluginSettings::_notification(int p_what) {45switch (p_what) {46case NOTIFICATION_WM_WINDOW_FOCUS_IN: {47update_plugins();48} break;4950case NOTIFICATION_READY: {51plugin_config_dialog->connect("plugin_ready", callable_mp(EditorNode::get_singleton(), &EditorNode::_on_plugin_ready));52plugin_list->connect("button_clicked", callable_mp(this, &EditorPluginSettings::_cell_button_pressed));53} break;5455case NOTIFICATION_TRANSLATION_CHANGED: {56if (plugin_list->get_root()) {57update_plugins();58}59} break;6061case NOTIFICATION_THEME_CHANGED: {62if (Engine::get_singleton()->is_recovery_mode_hint()) {63recovery_mode_icon->set_texture(get_editor_theme_icon(SNAME("NodeWarning")));64}65} break;66}67}6869void EditorPluginSettings::update_plugins() {70plugin_list->clear();71updating = true;72TreeItem *root = plugin_list->create_item();7374Vector<String> plugins = _get_plugins("res://addons");75plugins.sort();7677for (int i = 0; i < plugins.size(); i++) {78Ref<ConfigFile> cfg;79cfg.instantiate();80const String &path = plugins[i];8182Error err = cfg->load(path);8384if (err != OK) {85WARN_PRINT("Can't load plugin config at: " + path);86} else {87Vector<String> missing_keys;88for (const String required_key : { "name", "author", "version", "description", "script" }) {89if (!cfg->has_section_key("plugin", required_key)) {90missing_keys.append("\"plugin/" + required_key + "\"");91}92}9394if (!missing_keys.is_empty()) {95WARN_PRINT(vformat("Plugin config at \"%s\" is missing the following keys: %s", path, String(",").join(missing_keys)));96} else {97String name = cfg->get_value("plugin", "name");98String author = cfg->get_value("plugin", "author");99String version = cfg->get_value("plugin", "version");100String description = cfg->get_value("plugin", "description");101String scr = cfg->get_value("plugin", "script");102103bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(path);104Color disabled_color = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));105106const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);107String wrapped_description;108109for (int j = 0; j < boundaries.size(); j += 2) {110const int start = boundaries[j];111const int end = boundaries[j + 1];112wrapped_description += "\n" + description.substr(start, end - start + 1).rstrip("\n");113}114115TreeItem *item = plugin_list->create_item(root);116item->set_text(COLUMN_NAME, name);117if (!is_enabled) {118item->set_custom_color(COLUMN_NAME, disabled_color);119}120item->set_tooltip_text(COLUMN_NAME, vformat(TTR("Name: %s\nPath: %s\nMain Script: %s\n\n%s"), name, path, scr, wrapped_description));121item->set_metadata(COLUMN_NAME, path);122item->set_text(COLUMN_VERSION, version);123item->set_custom_font(COLUMN_VERSION, get_theme_font("source", EditorStringName(EditorFonts)));124item->set_metadata(COLUMN_VERSION, scr);125item->set_text(COLUMN_AUTHOR, author);126item->set_metadata(COLUMN_AUTHOR, description);127item->set_cell_mode(COLUMN_STATUS, TreeItem::CELL_MODE_CHECK);128item->set_text(COLUMN_STATUS, TTRC("On"));129item->set_checked(COLUMN_STATUS, is_enabled);130item->set_editable(COLUMN_STATUS, true);131item->add_button(COLUMN_EDIT, get_editor_theme_icon(SNAME("Edit")), BUTTON_PLUGIN_EDIT, false, TTRC("Edit Plugin"));132}133}134}135136updating = false;137}138139void EditorPluginSettings::_plugin_activity_changed() {140if (updating) {141return;142}143144TreeItem *ti = plugin_list->get_edited();145ERR_FAIL_NULL(ti);146bool checked = ti->is_checked(COLUMN_STATUS);147String name = ti->get_metadata(COLUMN_NAME);148149EditorNode::get_singleton()->set_addon_plugin_enabled(name, checked, true);150151bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(name);152153if (is_enabled != checked) {154updating = true;155ti->set_checked(COLUMN_STATUS, is_enabled);156updating = false;157}158if (is_enabled) {159ti->clear_custom_color(COLUMN_NAME);160} else {161ti->set_custom_color(COLUMN_NAME, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));162}163}164165void EditorPluginSettings::_create_clicked() {166plugin_config_dialog->config("");167plugin_config_dialog->popup_centered();168}169170void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {171if (p_button != MouseButton::LEFT) {172return;173}174TreeItem *item = Object::cast_to<TreeItem>(p_item);175if (!item) {176return;177}178if (p_id == BUTTON_PLUGIN_EDIT) {179if (p_column == COLUMN_EDIT) {180String dir = item->get_metadata(COLUMN_NAME);181plugin_config_dialog->config(dir);182plugin_config_dialog->popup_centered();183}184}185}186187Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) {188Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);189Error err = da->change_dir(p_dir);190if (err != OK) {191return Vector<String>();192}193194Vector<String> plugins;195da->list_dir_begin();196for (String path = da->get_next(); !path.is_empty(); path = da->get_next()) {197if (path[0] == '.' || !da->current_is_dir()) {198continue;199}200201const String full_path = p_dir.path_join(path);202const String plugin_config = full_path.path_join("plugin.cfg");203if (FileAccess::exists(plugin_config)) {204plugins.push_back(plugin_config);205} else {206plugins.append_array(_get_plugins(full_path));207}208}209210da->list_dir_end();211return plugins;212}213214EditorPluginSettings::EditorPluginSettings() {215ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/");216217plugin_config_dialog = memnew(PluginConfigDialog);218plugin_config_dialog->config("");219add_child(plugin_config_dialog);220221if (Engine::get_singleton()->is_recovery_mode_hint()) {222HBoxContainer *c = memnew(HBoxContainer);223add_child(c);224225recovery_mode_icon = memnew(TextureRect);226recovery_mode_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);227c->add_child(recovery_mode_icon);228229Label *recovery_mode_label = memnew(Label(TTRC("Recovery mode is enabled. Enabled plugins will not run while this mode is active.")));230recovery_mode_label->set_theme_type_variation("HeaderSmall");231recovery_mode_label->set_h_size_flags(SIZE_EXPAND_FILL);232c->add_child(recovery_mode_label);233234HSeparator *sep = memnew(HSeparator);235add_child(sep);236}237238HBoxContainer *title_hb = memnew(HBoxContainer);239Label *label = memnew(Label(TTRC("Installed Plugins:")));240label->set_theme_type_variation("HeaderSmall");241title_hb->add_child(label);242title_hb->add_spacer();243Button *create_plugin_button = memnew(Button(TTRC("Create New Plugin")));244create_plugin_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPluginSettings::_create_clicked));245title_hb->add_child(create_plugin_button);246add_child(title_hb);247248plugin_list = memnew(Tree);249plugin_list->set_v_size_flags(SIZE_EXPAND_FILL);250plugin_list->set_columns(COLUMN_MAX);251plugin_list->set_column_titles_visible(true);252plugin_list->set_column_title(COLUMN_STATUS, TTRC("Enabled"));253plugin_list->set_column_title(COLUMN_NAME, TTRC("Name"));254plugin_list->set_column_title(COLUMN_VERSION, TTRC("Version"));255plugin_list->set_column_title(COLUMN_AUTHOR, TTRC("Author"));256plugin_list->set_column_title(COLUMN_EDIT, TTRC("Edit"));257plugin_list->set_column_title_alignment(COLUMN_STATUS, HORIZONTAL_ALIGNMENT_LEFT);258plugin_list->set_column_title_alignment(COLUMN_NAME, HORIZONTAL_ALIGNMENT_LEFT);259plugin_list->set_column_title_alignment(COLUMN_VERSION, HORIZONTAL_ALIGNMENT_LEFT);260plugin_list->set_column_title_alignment(COLUMN_AUTHOR, HORIZONTAL_ALIGNMENT_LEFT);261plugin_list->set_column_title_alignment(COLUMN_EDIT, HORIZONTAL_ALIGNMENT_LEFT);262plugin_list->set_column_expand(COLUMN_PADDING_LEFT, false);263plugin_list->set_column_expand(COLUMN_STATUS, false);264plugin_list->set_column_expand(COLUMN_NAME, true);265plugin_list->set_column_expand(COLUMN_VERSION, false);266plugin_list->set_column_expand(COLUMN_AUTHOR, false);267plugin_list->set_column_expand(COLUMN_EDIT, false);268plugin_list->set_column_expand(COLUMN_PADDING_RIGHT, false);269plugin_list->set_column_clip_content(COLUMN_STATUS, true);270plugin_list->set_column_clip_content(COLUMN_NAME, true);271plugin_list->set_column_clip_content(COLUMN_VERSION, true);272plugin_list->set_column_clip_content(COLUMN_AUTHOR, true);273plugin_list->set_column_clip_content(COLUMN_EDIT, true);274plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_LEFT, 10 * EDSCALE);275plugin_list->set_column_custom_minimum_width(COLUMN_STATUS, 80 * EDSCALE);276plugin_list->set_column_custom_minimum_width(COLUMN_VERSION, 100 * EDSCALE);277plugin_list->set_column_custom_minimum_width(COLUMN_AUTHOR, 250 * EDSCALE);278plugin_list->set_column_custom_minimum_width(COLUMN_EDIT, 40 * EDSCALE);279plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_RIGHT, 10 * EDSCALE);280plugin_list->set_hide_root(true);281plugin_list->connect("item_edited", callable_mp(this, &EditorPluginSettings::_plugin_activity_changed), CONNECT_DEFERRED);282283VBoxContainer *mc = memnew(VBoxContainer);284mc->add_child(plugin_list);285mc->set_v_size_flags(SIZE_EXPAND_FILL);286mc->set_h_size_flags(SIZE_EXPAND_FILL);287288add_child(mc);289}290291292