Skip to content

Commit

Permalink
Allow to override editor settings per project
Browse files Browse the repository at this point in the history
  • Loading branch information
KoBeWi committed Dec 25, 2024
1 parent 0f95e9f commit 79caf22
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 11 deletions.
15 changes: 14 additions & 1 deletion core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#endif // TOOLS_ENABLED

const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot";
const String ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX = "editor_overrides/";

ProjectSettings *ProjectSettings::singleton = nullptr;

Expand Down Expand Up @@ -1192,7 +1193,7 @@ bool ProjectSettings::is_project_loaded() const {
}

bool ProjectSettings::_property_can_revert(const StringName &p_name) const {
return props.has(p_name);
return props.has(p_name) && !String(p_name).begins_with(EDITOR_SETTING_OVERRIDE_PREFIX);
}

bool ProjectSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const {
Expand Down Expand Up @@ -1379,6 +1380,18 @@ void ProjectSettings::get_argument_options(const StringName &p_function, int p_i
}
#endif

void ProjectSettings::set_editor_setting_override(const String &p_setting, const Variant &p_value) {
set_setting(EDITOR_SETTING_OVERRIDE_PREFIX + p_setting, p_value);
}

bool ProjectSettings::has_editor_setting_override(const String &p_setting) const {
return has_setting(EDITOR_SETTING_OVERRIDE_PREFIX + p_setting);
}

Variant ProjectSettings::get_editor_setting_override(const String &p_setting) const {
return get_setting(EDITOR_SETTING_OVERRIDE_PREFIX + p_setting);
}

void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
Expand Down
5 changes: 5 additions & 0 deletions core/config/project_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class ProjectSettings : public Object {
public:
typedef HashMap<String, Variant> CustomMap;
static const String PROJECT_DATA_DIR_NAME_SUFFIX;
static const String EDITOR_SETTING_OVERRIDE_PREFIX;

// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
constexpr static const int32_t NO_BUILTIN_ORDER_BASE = 1 << 16;
Expand Down Expand Up @@ -221,6 +222,10 @@ class ProjectSettings : public Object {
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif

void set_editor_setting_override(const String &p_setting, const Variant &p_value);
bool has_editor_setting_override(const String &p_setting) const;
Variant get_editor_setting_override(const String &p_setting) const;

ProjectSettings();
ProjectSettings(const String &p_path);
~ProjectSettings();
Expand Down
14 changes: 14 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,11 @@ void EditorNode::_notification(int p_what) {
EditorFileSystem::get_singleton()->scan();
}
}

if (settings_overrides_changed) {
EditorSettings::get_singleton()->notify_changes();
settings_overrides_changed = false;
}
} break;

case NOTIFICATION_ENTER_TREE: {
Expand Down Expand Up @@ -6734,6 +6739,15 @@ void EditorNode::set_unfocused_low_processor_usage_mode_enabled(bool p_enabled)
unfocused_low_processor_usage_mode_enabled = p_enabled;
}

void EditorNode::open_setting_override(const String &p_property) {
editor_settings_dialog->hide();
project_settings_editor->popup_for_override(p_property);
}

void EditorNode::notify_settings_overrides_changed() {
settings_overrides_changed = true;
}

EditorNode::EditorNode() {
DEV_ASSERT(!singleton);
singleton = this;
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ class EditorNode : public Node {
bool immediate_dialog_confirmed = false;
bool opening_prev = false;
bool restoring_scenes = false;
bool settings_overrides_changed = false;
bool unsaved_cache = true;

bool requested_first_scan = false;
Expand Down Expand Up @@ -937,6 +938,9 @@ class EditorNode : public Node {
void restart_editor(bool p_goto_project_manager = false);
void unload_editor_addons();

void open_setting_override(const String &p_property);
void notify_settings_overrides_changed();

void dim_editor(bool p_dimming);
bool is_editor_dimmed() const;

Expand Down
2 changes: 2 additions & 0 deletions editor/editor_sectioned_inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class SectionedInspectorFilter : public Object {

void SectionedInspector::_bind_methods() {
ClassDB::bind_method("update_category_list", &SectionedInspector::update_category_list);
ADD_SIGNAL(MethodInfo("category_changed", PropertyInfo(Variant::STRING, "new_category")));
}

void SectionedInspector::_section_selected() {
Expand All @@ -150,6 +151,7 @@ void SectionedInspector::_section_selected() {
selected_category = sections->get_selected()->get_metadata(0);
filter->set_section(selected_category, sections->get_selected()->get_first_child() == nullptr);
inspector->set_property_prefix(selected_category + "/");
emit_signal(SNAME("category_changed"), selected_category);
}

void SectionedInspector::set_current_section(const String &p_section) {
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,9 @@ Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_re

Variant _EDITOR_GET(const String &p_setting) {
ERR_FAIL_COND_V(!EditorSettings::get_singleton() || !EditorSettings::get_singleton()->has_setting(p_setting), Variant());
if (ProjectSettings::get_singleton()->has_editor_setting_override(p_setting)) {
return ProjectSettings::get_singleton()->get_editor_setting_override(p_setting);
}
return EditorSettings::get_singleton()->get(p_setting);
}

Expand Down
108 changes: 107 additions & 1 deletion editor/editor_settings_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "editor_settings_dialog.h"

#include "core/config/project_settings.h"
#include "core/input/input_map.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
Expand All @@ -44,6 +45,7 @@
#include "editor/event_listener_line_edit.h"
#include "editor/input_event_configuration_dialog.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/check_button.h"
Expand Down Expand Up @@ -994,7 +996,111 @@ EditorSettingsDialog::EditorSettingsDialog() {
add_child(timer);
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorSettingsDialog::_settings_changed));
set_ok_button_text(TTR("Close"));

Ref<EditorSettingsInspectorPlugin> plugin;
plugin.instantiate();
plugin->inspector = inspector;
EditorInspector::add_inspector_plugin(plugin);
}

void EditorSettingsPropertyWrapper::_update_override() {
const bool has_override = ProjectSettings::get_singleton()->has_editor_setting_override(property);
if (has_override) {
const Variant override_value = EDITOR_GET(property);
override_label->set_text(vformat(TTR("Overridden in project: %s"), override_value));
// In case the text is too long and trimmed.
override_label->set_tooltip_text(override_value);
}
override_button->set_visible(!has_override);
override_info->set_visible(has_override);
container->set_vertical(has_override);
}

void EditorSettingsPropertyWrapper::_create_override() {
ProjectSettings::get_singleton()->set_editor_setting_override(property, EDITOR_GET(property));
ProjectSettings::get_singleton()->save();
_update_override();
}

void EditorSettingsPropertyWrapper::_remove_override() {
ProjectSettings::get_singleton()->set_editor_setting_override(property, Variant());
ProjectSettings::get_singleton()->save();
EditorSettings::get_singleton()->mark_setting_changed(property);
EditorNode::get_singleton()->notify_settings_overrides_changed();
_update_override();
}

void EditorSettingsPropertyWrapper::_notification(int p_what) {
if (p_what == NOTIFICATION_THEME_CHANGED) {
override_button->set_icon(get_editor_theme_icon(SNAME("Override")));

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🤖 Android / Editor (target=editor)

no member named 'set_icon' in 'Button'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🍎 macOS / Editor (target=editor, tests=yes)

no member named 'set_icon' in 'Button'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor w/ Mono (target=editor)

'class Button' has no member named 'set_icon'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold)

'class Button' has no member named 'set_icon'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'set_icon': is not a member of 'Button'

Check failure on line 1035 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)

no member named 'set_icon' in 'Button'
goto_button->set_icon(get_editor_theme_icon(SNAME("MethodOverride")));

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🤖 Android / Editor (target=editor)

no member named 'set_icon' in 'Button'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🍎 macOS / Editor (target=editor, tests=yes)

no member named 'set_icon' in 'Button'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor w/ Mono (target=editor)

'class Button' has no member named 'set_icon'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold)

'class Button' has no member named 'set_icon'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'set_icon': is not a member of 'Button'

Check failure on line 1036 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)

no member named 'set_icon' in 'Button'
remove_button->set_icon(get_editor_theme_icon(SNAME("Remove")));

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🤖 Android / Editor (target=editor)

no member named 'set_icon' in 'Button'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🍎 macOS / Editor (target=editor, tests=yes)

no member named 'set_icon' in 'Button'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor w/ Mono (target=editor)

'class Button' has no member named 'set_icon'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold)

'class Button' has no member named 'set_icon'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🐧 Linux / Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)

no member named 'set_icon' in 'Button'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'set_icon': is not a member of 'Button'

Check failure on line 1037 in editor/editor_settings_dialog.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes)

no member named 'set_icon' in 'Button'
}
}

void EditorSettingsPropertyWrapper::update_property() {
editor_property->update_property();
}

EditorSettingsDialog::~EditorSettingsDialog() {
void EditorSettingsPropertyWrapper::setup(const String &p_property, EditorProperty *p_editor_property) {
property = p_property;
container = memnew(BoxContainer);

override_button = memnew(Button);
override_button->set_tooltip_text(TTR("Override this setting for the current project."));
container->add_child(override_button);
override_button->connect(SNAME("pressed"), callable_mp(this, &EditorSettingsPropertyWrapper::_create_override));

editor_property = p_editor_property;
editor_property->set_h_size_flags(SIZE_EXPAND_FILL);
container->add_child(editor_property);

override_info = memnew(HBoxContainer);
override_info->hide();
container->add_child(override_info);

override_label = memnew(Label);
override_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
override_label->set_mouse_filter(MOUSE_FILTER_STOP); // For tooltip.
override_label->set_h_size_flags(SIZE_EXPAND_FILL);
override_info->add_child(override_label);

goto_button = memnew(Button);
goto_button->set_tooltip_text(TTR("Go to override in Project Settings."));
override_info->add_child(goto_button);
goto_button->connect(SNAME("pressed"), callable_mp(EditorNode::get_singleton(), &EditorNode::open_setting_override).bind(property), CONNECT_DEFERRED);

remove_button = memnew(Button);
remove_button->set_tooltip_text(TTR("Remove this override."));
override_info->add_child(remove_button);
remove_button->connect(SNAME("pressed"), callable_mp(this, &EditorSettingsPropertyWrapper::_remove_override));

add_child(container);
_update_override();
}

bool EditorSettingsInspectorPlugin::can_handle(Object *p_object) {
return p_object->is_class("SectionedInspectorFilter") && p_object != current_object;
}

bool EditorSettingsInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
if (!p_object->is_class("SectionedInspectorFilter")) {
return false;
}

const String property = inspector->get_full_item_path(p_path);
if (EditorSettings::get_singleton()->has_setting(property)) {
current_object = p_object;

EditorSettingsPropertyWrapper *editor = memnew(EditorSettingsPropertyWrapper);
EditorProperty *real_property = inspector->get_inspector()->instantiate_property_editor(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide);
real_property->set_object_and_property(p_object, p_path);
real_property->set_name_split_ratio(0.0);
editor->setup(property, real_property);

add_property_editor(p_path, editor);
current_object = nullptr;
return true;
}
return false;
}
40 changes: 39 additions & 1 deletion editor/editor_settings_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#ifndef EDITOR_SETTINGS_DIALOG_H
#define EDITOR_SETTINGS_DIALOG_H

#include "editor/editor_inspector.h"
#include "scene/gui/dialogs.h"

class CheckButton;
Expand Down Expand Up @@ -137,7 +138,44 @@ class EditorSettingsDialog : public AcceptDialog {
static void update_navigation_preset();

EditorSettingsDialog();
~EditorSettingsDialog();
};

class EditorSettingsPropertyWrapper : public EditorProperty {
GDCLASS(EditorSettingsPropertyWrapper, EditorProperty);

String property;
EditorProperty *editor_property = nullptr;

BoxContainer *container = nullptr;
Button *override_button = nullptr;

HBoxContainer *override_info = nullptr;
Label *override_label = nullptr;
Button *goto_button = nullptr;
Button *remove_button = nullptr;

void _update_override();
void _create_override();
void _remove_override();

protected:
void _notification(int p_what);

public:
virtual void update_property() override;
void setup(const String &p_property, EditorProperty *p_editor_property);
};

class EditorSettingsInspectorPlugin : public EditorInspectorPlugin {
GDCLASS(EditorSettingsInspectorPlugin, EditorInspectorPlugin);

Object *current_object = nullptr;

public:
SectionedInspector *inspector = nullptr;

virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};

#endif // EDITOR_SETTINGS_DIALOG_H
38 changes: 37 additions & 1 deletion editor/project_settings_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
_focus_current_search_box();
}

void ProjectSettingsEditor::popup_for_override(const String &p_override) {
popup_project_settings();
tab_container->set_current_tab(0);
general_settings_inspector->set_current_section("editor_overrides/" + p_override.get_slice("/", 0));
}

void ProjectSettingsEditor::queue_save() {
timer->start();
}
Expand All @@ -97,13 +103,41 @@ void ProjectSettingsEditor::init_autoloads() {
}

void ProjectSettingsEditor::_setting_edited(const String &p_name) {
const String full_name = general_settings_inspector->get_full_item_path(p_name);
if (full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX)) {
EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
pending_override_notify = true;
}
queue_save();
}

void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) {
custom_properties->set_visible(p_is_advanced);
}

void ProjectSettingsEditor::_save_settings() {
ps->save();
if (pending_override_notify) {
pending_override_notify = false;
EditorNode::get_singleton()->notify_settings_overrides_changed();
}
}

void ProjectSettingsEditor::_on_category_changed(const String &p_new_category) {
general_settings_inspector->get_inspector()->set_use_deletable_properties(p_new_category.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
}

void ProjectSettingsEditor::_on_editor_override_deleted(const String &p_setting) {
const String full_name = general_settings_inspector->get_full_item_path(p_setting);
ERR_FAIL_COND(!full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));

ProjectSettings::get_singleton()->set_setting(full_name, Variant());
EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
pending_override_notify = true;
_save_settings();
general_settings_inspector->update_category_list();
}

void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) {
EditorSettings::get_singleton()->set("_project_settings_advanced_mode", p_button_pressed);
EditorSettings::get_singleton()->save();
Expand Down Expand Up @@ -690,9 +724,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
general_settings_inspector->register_search_box(search_box);
general_settings_inspector->register_advanced_toggle(advanced);
general_settings_inspector->connect("category_changed", callable_mp(this, &ProjectSettingsEditor::_on_category_changed));
general_settings_inspector->get_inspector()->set_use_filter(true);
general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));
general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));
general_settings_inspector->get_inspector()->connect("property_deleted", callable_mp(this, &ProjectSettingsEditor::_on_editor_override_deleted));
general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
general_editor->add_child(general_settings_inspector);

Expand Down Expand Up @@ -763,7 +799,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {

timer = memnew(Timer);
timer->set_wait_time(1.5);
timer->connect("timeout", callable_mp(ps, &ProjectSettings::save));
timer->connect("timeout", callable_mp(this, &ProjectSettingsEditor::_save_settings));
timer->set_one_shot(true);
add_child(timer);

Expand Down
Loading

0 comments on commit 79caf22

Please sign in to comment.