Merge pull request #52944 from RandomShaper/property_pin_control_natural_3.x

This commit is contained in:
Rémi Verschelde 2021-11-08 18:53:25 +01:00 committed by GitHub
commit 4b4e92b3d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 626 additions and 343 deletions

View file

@ -102,7 +102,7 @@
</signal>
<signal name="property_checked">
<argument index="0" name="property" type="String" />
<argument index="1" name="bool" type="String" />
<argument index="1" name="checked" type="bool" />
<description>
Emitted when a property was checked. Used internally.
</description>
@ -120,6 +120,14 @@
Emit it if you want to key a property with a single value.
</description>
</signal>
<signal name="property_pinned">
<argument index="0" name="property" type="String" />
<argument index="1" name="pinned" type="bool" />
<description>
Emit it if you want to mark (or unmark) the value of a property for being saved regardless of being equal to the default value.
The default value is the one the property will get when the node is just instantiated and can come from an ancestor scene in the inheritance/instancing chain, a script or a builtin class.
</description>
</signal>
<signal name="resource_selected">
<argument index="0" name="path" type="String" />
<argument index="1" name="resource" type="Resource" />

View file

@ -86,5 +86,9 @@
If passed to [method instance], provides local scene resources to the local scene. Only the main scene should receive the main edit state.
[b]Note:[/b] Only available in editor builds.
</constant>
<constant name="GEN_EDIT_STATE_MAIN_INHERITED" value="3" enum="GenEditState">
It's similar to [constant GEN_EDIT_STATE_MAIN], but for the case where the scene is being instantiated to be the base of another one.
[b]Note:[/b] Only available in editor builds.
</constant>
</constants>
</class>

View file

@ -168,5 +168,9 @@
If passed to [method PackedScene.instance], provides local scene resources to the local scene. Only the main scene should receive the main edit state.
[b]Note:[/b] Only available in editor builds.
</constant>
<constant name="GEN_EDIT_STATE_MAIN_INHERITED" value="3" enum="GenEditState">
If passed to [method PackedScene.instance], it's similar to [constant GEN_EDIT_STATE_MAIN], but for the case where the scene is being instantiated to be the base of another one.
[b]Note:[/b] Only available in editor builds.
</constant>
</constants>
</class>

View file

@ -29,12 +29,14 @@
/*************************************************************************/
#include "editor_inspector.h"
#include "array_property_edit.h"
#include "dictionary_property_edit.h"
#include "editor_feature_profile.h"
#include "editor_node.h"
#include "editor_scale.h"
#include "multi_node_edit.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
Size2 EditorProperty::get_minimum_size() const {
@ -259,6 +261,14 @@ void EditorProperty::_notification(int p_what) {
revert_rect = Rect2();
}
if (!pin_hidden && is_pinned) {
Ref<Texture> pinned_icon = get_icon("Pin", "EditorIcons");
int margin_w = get_constant("hseparator", "Tree") * 2;
text_limit -= margin_w + pinned_icon->get_width();
int text_w = MIN(font->get_string_size(label).x, text_limit);
draw_texture(pinned_icon, Vector2(ofs + text_w + margin_w, (size.height - pinned_icon->get_height()) / 2), color);
}
int v_ofs = (size.height - font->get_height()) / 2;
draw_string(font, Point2(ofs, v_ofs + font->get_ascent()), label, color, text_limit);
@ -318,177 +328,12 @@ bool EditorProperty::is_read_only() const {
return read_only;
}
bool EditorPropertyRevert::may_node_be_in_instance(Node *p_node) {
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool might_be = false;
Node *node = p_node;
while (node) {
if (node == edited_scene) {
if (node->get_scene_inherited_state().is_valid()) {
might_be = true;
break;
}
might_be = false;
break;
}
if (node->get_scene_instance_state().is_valid()) {
might_be = true;
break;
}
node = node->get_owner();
}
return might_be; // or might not be
}
bool EditorPropertyRevert::get_instanced_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default) {
Node *node = p_node;
Node *orig = node;
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool found = false;
while (node) {
Ref<SceneState> ss;
if (node == edited_scene) {
ss = node->get_scene_inherited_state();
} else {
ss = node->get_scene_instance_state();
}
if (ss.is_valid()) {
NodePath np = node->get_path_to(orig);
int node_idx = ss->find_node_by_path(np);
if (node_idx >= 0) {
bool lfound = false;
Variant lvar;
lvar = ss->get_property_value(node_idx, p_prop, lfound);
if (lfound) {
found = true;
value = lvar;
}
}
}
if (node == edited_scene) {
//just in case
break;
}
node = node->get_owner();
}
if (p_check_class_default && !found && p_node) {
//if not found, try default class value
Variant attempt = ClassDB::class_get_default_property_value(p_node->get_class_name(), p_prop);
if (attempt.get_type() != Variant::NIL) {
found = true;
value = attempt;
}
}
return found;
}
bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig) {
// this is a pretty difficult function, because a property may not be saved but may have
// the flag to not save if one or if zero
//make sure there is an actual state
{
Node *node = p_node;
if (!node) {
return false;
}
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool found_state = false;
while (node) {
Ref<SceneState> ss;
if (node == edited_scene) {
ss = node->get_scene_inherited_state();
} else {
ss = node->get_scene_instance_state();
}
if (ss.is_valid()) {
found_state = true;
break;
}
if (node == edited_scene) {
//just in case
break;
}
node = node->get_owner();
}
if (!found_state) {
return false; //pointless to check if we are not comparing against anything.
}
}
return is_property_value_different(p_current, p_orig);
}
bool EditorPropertyRevert::is_property_value_different(const Variant &p_a, const Variant &p_b) {
if (p_a.get_type() == Variant::REAL && p_b.get_type() == Variant::REAL) {
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
return !Math::is_equal_approx((float)p_a, (float)p_b);
} else {
return p_a != p_b;
}
}
Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property) {
// If the object implements property_can_revert, rely on that completely
// (i.e. don't then try to revert to default value - the property_get_revert implementation
// can do that if so desired)
if (p_object->has_method("property_can_revert") && p_object->call("property_can_revert", p_property)) {
return p_object->call("property_get_revert", p_property);
}
Ref<Script> scr = p_object->get_script();
Node *node = Object::cast_to<Node>(p_object);
if (node && EditorPropertyRevert::may_node_be_in_instance(node)) {
//if this node is an instance or inherits, but it has a script attached which is unrelated
//to the one set for the parent and also has a default value for the property, consider that
//has precedence over the value from the parent, because that is an explicit source of defaults
//closer in the tree to the current node
bool ignore_parent = false;
if (scr.is_valid()) {
Variant sorig;
if (EditorPropertyRevert::get_instanced_node_original_property(node, "script", sorig) && !scr->inherits_script(sorig)) {
Variant dummy;
if (scr->get_property_default_value(p_property, dummy)) {
ignore_parent = true;
}
}
}
if (!ignore_parent) {
//check for difference including instantiation
Variant vorig;
if (EditorPropertyRevert::get_instanced_node_original_property(node, p_property, vorig, false)) {
return vorig;
}
}
}
if (scr.is_valid()) {
Variant orig_value;
if (scr->get_property_default_value(p_property, orig_value)) {
return orig_value;
}
}
//report default class value instead
return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
return PropertyUtils::get_property_default_value(p_object, p_property);
}
bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringName &p_property) {
@ -497,18 +342,25 @@ bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringNam
return false;
}
Variant current_value = p_object->get(p_property);
return EditorPropertyRevert::is_property_value_different(current_value, revert_value);
return PropertyUtils::is_property_value_different(current_value, revert_value);
}
void EditorProperty::update_reload_status() {
void EditorProperty::update_revert_and_pin_status() {
if (property == StringName()) {
return; //no property, so nothing to do
}
bool has_reload = EditorPropertyRevert::can_property_revert(object, property);
bool new_is_pinned = false;
if (can_pin) {
Node *node = Object::cast_to<Node>(object);
CRASH_COND(!node);
new_is_pinned = node->is_property_pinned(property);
}
bool new_can_revert = EditorPropertyRevert::can_property_revert(object, property);
if (has_reload != can_revert) {
can_revert = has_reload;
if (new_can_revert != can_revert || new_is_pinned != is_pinned) {
can_revert = new_can_revert;
is_pinned = new_is_pinned;
update();
}
}
@ -679,6 +531,14 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
update();
emit_signal("property_checked", property, checked);
}
} else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
_update_popup();
if (menu->get_item_count()) {
menu->set_position(get_global_mouse_position());
menu->set_as_minsize();
menu->popup();
select();
}
}
}
@ -738,6 +598,56 @@ float EditorProperty::get_name_split_ratio() const {
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
_update_pin_flags();
}
static bool _is_value_potential_override(Node *p_node, const String &p_property) {
// Consider a value is potentially overriding another if either of the following is true:
// a) The node is foreign (inheriting or an instance), so the original value may come from another scene.
// b) The node belongs to the scene, but the original value comes from somewhere but the builtin class (i.e., a script).
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, edited_scene);
if (states_stack.size()) {
return true;
} else {
bool is_class_default = false;
PropertyUtils::get_property_default_value(p_node, p_property, &states_stack, false, nullptr, &is_class_default);
return !is_class_default;
}
}
void EditorProperty::_update_pin_flags() {
can_pin = false;
pin_hidden = true;
if (read_only) {
return;
}
if (Node *node = Object::cast_to<Node>(object)) {
// Avoid errors down the road by ignoring nodes which are not part of a scene
if (!node->get_owner()) {
bool is_scene_root = false;
for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_edited_scene_count(); ++i) {
if (EditorNode::get_singleton()->get_editor_data().get_edited_scene_root(i) == node) {
is_scene_root = true;
break;
}
}
if (!is_scene_root) {
return;
}
}
if (!_is_value_potential_override(node, property)) {
return;
}
pin_hidden = false;
{
Set<StringName> storable_properties;
node->get_storable_properties(storable_properties);
if (storable_properties.has(node->get_property_store_alias(property))) {
can_pin = true;
}
}
}
}
Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
@ -790,6 +700,7 @@ void EditorProperty::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object);
ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input);
ClassDB::bind_method(D_METHOD("_menu_option"), &EditorProperty::_menu_option);
ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused);
ClassDB::bind_method(D_METHOD("get_tooltip_text"), &EditorProperty::get_tooltip_text);
@ -809,7 +720,8 @@ void EditorProperty::_bind_methods() {
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::STRING, "bool")));
ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked")));
ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "pinned")));
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx")));
@ -817,6 +729,8 @@ void EditorProperty::_bind_methods() {
MethodInfo vm;
vm.name = "update_property";
BIND_VMETHOD(vm);
ClassDB::bind_method(D_METHOD("_update_revert_and_pin_status"), &EditorProperty::update_revert_and_pin_status);
}
EditorProperty::EditorProperty() {
@ -834,13 +748,47 @@ EditorProperty::EditorProperty() {
revert_hover = false;
check_hover = false;
can_revert = false;
can_pin = false;
pin_hidden = false;
is_pinned = false;
use_folding = false;
property_usage = 0;
selected = false;
selected_focusable = -1;
label_reference = nullptr;
bottom_editor = nullptr;
menu = nullptr;
}
void EditorProperty::_update_popup() {
if (menu) {
menu->clear();
} else {
menu = memnew(PopupMenu);
add_child(menu);
menu->connect("id_pressed", this, "_menu_option");
}
if (!pin_hidden) {
if (can_pin) {
menu->add_check_item(TTR("Pin value"), MENU_PIN_VALUE);
menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), is_pinned);
menu->set_item_tooltip(menu->get_item_index(MENU_PIN_VALUE), TTR("Pinning a value forces it to be saved even if it's equal to the default."));
} else {
menu->add_check_item(vformat(TTR("Pin value [Disabled because '%s' is editor-only]"), property), MENU_PIN_VALUE);
menu->set_item_disabled(menu->get_item_index(MENU_PIN_VALUE), true);
}
}
}
void EditorProperty::_menu_option(int p_option) {
switch (p_option) {
case MENU_PIN_VALUE: {
emit_signal("property_pinned", property, !is_pinned);
update();
} break;
}
}
////////////////////////////////////////////////
////////////////////////////////////////////////
@ -1305,6 +1253,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit
ep->connect("property_keyed", this, "_property_keyed");
ep->connect("property_keyed_with_value", this, "_property_keyed_with_value");
ep->connect("property_checked", this, "_property_checked");
ep->connect("property_pinned", this, "_property_pinned");
ep->connect("selected", this, "_property_selected");
ep->connect("multiple_properties_changed", this, "_multiple_properties_changed");
ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
@ -1333,7 +1282,8 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit
ep->set_read_only(read_only);
ep->update_property();
ep->update_reload_status();
ep->_update_pin_flags();
ep->update_revert_and_pin_status();
}
}
ped->added_editors.clear();
@ -1729,6 +1679,7 @@ void EditorInspector::update_tree() {
ep->connect("property_keyed", this, "_property_keyed");
ep->connect("property_keyed_with_value", this, "_property_keyed_with_value");
ep->connect("property_checked", this, "_property_checked");
ep->connect("property_pinned", this, "_property_pinned");
ep->connect("selected", this, "_property_selected");
ep->connect("multiple_properties_changed", this, "_multiple_properties_changed");
ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
@ -1739,7 +1690,8 @@ void EditorInspector::update_tree() {
ep->set_tooltip(property_prefix + p.name);
}
ep->update_property();
ep->update_reload_status();
ep->_update_pin_flags();
ep->update_revert_and_pin_status();
if (current_selected && ep->property == current_selected) {
ep->select(current_focusable);
@ -1768,7 +1720,7 @@ void EditorInspector::update_property(const String &p_prop) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_prop].front(); E; E = E->next()) {
E->get()->update_property();
E->get()->update_reload_status();
E->get()->update_revert_and_pin_status();
}
}
@ -2007,7 +1959,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
if (editor_property_map.has(p_name)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) {
E->get()->update_reload_status();
E->get()->update_revert_and_pin_status();
}
}
}
@ -2098,7 +2050,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
if (editor_property_map.has(p_path)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
E->get()->update_property();
E->get()->update_reload_status();
E->get()->update_revert_and_pin_status();
}
}
@ -2107,6 +2059,35 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
}
}
void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) {
if (!object) {
return;
}
Node *node = Object::cast_to<Node>(object);
ERR_FAIL_COND(!node);
if (undo_redo) {
undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
if (editor_property_map.has(p_path)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
undo_redo->add_do_method(E->get(), "_update_revert_and_pin_status");
undo_redo->add_undo_method(E->get(), "_update_revert_and_pin_status");
}
}
undo_redo->commit_action();
} else {
node->set_property_pinned(p_path, p_pinned);
if (editor_property_map.has(p_path)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
E->get()->update_revert_and_pin_status();
}
}
}
}
void EditorInspector::_property_selected(const String &p_path, int p_focusable) {
property_selected = p_path;
property_focusable = p_focusable;
@ -2171,7 +2152,7 @@ void EditorInspector::_notification(int p_what) {
for (Map<StringName, List<EditorProperty *>>::Element *F = editor_property_map.front(); F; F = F->next()) {
for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
E->get()->update_property();
E->get()->update_reload_status();
E->get()->update_revert_and_pin_status();
}
}
}
@ -2190,7 +2171,7 @@ void EditorInspector::_notification(int p_what) {
if (editor_property_map.has(prop)) {
for (List<EditorProperty *>::Element *E = editor_property_map[prop].front(); E; E = E->next()) {
E->get()->update_property();
E->get()->update_reload_status();
E->get()->update_revert_and_pin_status();
}
}
pending.erase(pending.front());
@ -2253,6 +2234,7 @@ void EditorInspector::_bind_methods() {
ClassDB::bind_method("_property_keyed", &EditorInspector::_property_keyed);
ClassDB::bind_method("_property_keyed_with_value", &EditorInspector::_property_keyed_with_value);
ClassDB::bind_method("_property_checked", &EditorInspector::_property_checked);
ClassDB::bind_method("_property_pinned", &EditorInspector::_property_pinned);
ClassDB::bind_method("_property_selected", &EditorInspector::_property_selected);
ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected);
ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected);

View file

@ -39,7 +39,6 @@ class UndoRedo;
class EditorPropertyRevert {
public:
static bool may_node_be_in_instance(Node *p_node);
static bool get_instanced_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true);
static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig);
static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
@ -51,6 +50,11 @@ public:
class EditorProperty : public Container {
GDCLASS(EditorProperty, Container);
public:
enum MenuItems {
MENU_PIN_VALUE,
};
private:
String label;
int text_size;
@ -77,12 +81,15 @@ private:
bool check_hover;
bool can_revert;
bool can_pin;
bool pin_hidden;
bool is_pinned;
bool use_folding;
bool draw_top_bg;
bool _is_property_different(const Variant &p_current, const Variant &p_orig);
bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value);
void _update_popup();
void _menu_option(int p_option);
void _focusable_focused(int p_index);
bool selectable;
@ -94,9 +101,12 @@ private:
Vector<Control *> focusables;
Control *label_reference;
Control *bottom_editor;
PopupMenu *menu;
mutable String tooltip_text;
void _update_pin_flags();
protected:
void _notification(int p_what);
static void _bind_methods();
@ -118,7 +128,7 @@ public:
StringName get_edited_property();
virtual void update_property();
void update_reload_status();
void update_revert_and_pin_status();
virtual bool use_keying_next() const;
@ -308,8 +318,8 @@ class EditorInspector : public ScrollContainer {
void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
void _property_keyed(const String &p_path, bool p_advance);
void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance);
void _property_checked(const String &p_path, bool p_checked);
void _property_pinned(const String &p_path, bool p_pinned);
void _resource_selected(const String &p_path, RES p_resource);
void _property_selected(const String &p_path, int p_focusable);

View file

@ -3588,7 +3588,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
sdata->set_path(lpath, true); //take over path
}
Node *new_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_MAIN);
Node *new_scene = sdata->instance(p_set_inherited ? PackedScene::GEN_EDIT_STATE_MAIN_INHERITED : PackedScene::GEN_EDIT_STATE_MAIN);
if (!new_scene) {
sdata.unref();

View file

@ -34,7 +34,6 @@
#include "core/os/input.h"
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
@ -46,6 +45,7 @@
#include "editor/plugins/spatial_editor_plugin.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
void SceneTreeDock::_nodes_drag_begin() {
@ -3086,7 +3086,9 @@ void SceneTreeDock::_clear_clipboard() {
void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap) {
List<PropertyInfo> props;
p_node->get_property_list(&props);
bool is_instanced = EditorPropertyRevert::may_node_be_in_instance(p_node);
Vector<SceneState::PackState> states_stack;
bool states_stack_ready = false;
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
@ -3097,13 +3099,14 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap)
if (v.is_ref()) {
RES res = v;
if (res.is_valid()) {
if (is_instanced) {
Variant orig;
if (EditorPropertyRevert::get_instanced_node_original_property(p_node, E->get().name, orig)) {
if (!EditorPropertyRevert::is_node_property_different(p_node, v, orig)) {
continue;
}
}
if (!states_stack_ready) {
states_stack = PropertyUtils::get_node_states_stack(p_node);
states_stack_ready = true;
}
Variant orig = PropertyUtils::get_property_default_value(p_node, E->get().name, &states_stack);
if (!PropertyUtils::is_property_value_different(v, orig)) {
continue;
}
if ((res->get_path() == "" || res->get_path().find("::") > -1) && !r_remap.has(res)) {

View file

@ -439,6 +439,16 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
}
#ifdef TOOLS_ENABLED
StringName Node2D::get_property_store_alias(const StringName &p_property) const {
if (p_property == "rotation_degrees") {
return "rotation";
} else {
return Node::get_property_store_alias(p_property);
}
}
#endif
Node2D::Node2D() {
angle = 0;
_scale = Vector2(1, 1);

View file

@ -116,6 +116,10 @@ public:
Transform2D get_transform() const;
#ifdef TOOLS_ENABLED
virtual StringName get_property_store_alias(const StringName &p_property) const;
#endif
Node2D();
};

View file

@ -306,6 +306,16 @@ void CanvasLayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale");
}
#ifdef TOOLS_ENABLED
StringName CanvasLayer::get_property_store_alias(const StringName &p_property) const {
if (p_property == "rotation_degrees") {
return "rotation";
} else {
return Node::get_property_store_alias(p_property);
}
}
#endif
CanvasLayer::CanvasLayer() {
vp = nullptr;
scale = Vector2(1, 1);

View file

@ -102,6 +102,10 @@ public:
RID get_canvas() const;
#ifdef TOOLS_ENABLED
StringName get_property_store_alias(const StringName &p_property) const;
#endif
CanvasLayer();
~CanvasLayer();
};

View file

@ -1866,6 +1866,56 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const {
return node;
}
#ifdef TOOLS_ENABLED
void Node::set_property_pinned(const StringName &p_property, bool p_pinned) {
bool current_pinned = false;
bool has_pinned = has_meta("_edit_pinned_properties_");
Array pinned;
String psa = get_property_store_alias(p_property);
if (has_pinned) {
pinned = get_meta("_edit_pinned_properties_");
current_pinned = pinned.has(psa);
}
if (current_pinned != p_pinned) {
if (p_pinned) {
pinned.append(psa);
if (!has_pinned) {
set_meta("_edit_pinned_properties_", pinned);
}
} else {
pinned.erase(psa);
if (pinned.empty()) {
remove_meta("_edit_pinned_properties_");
}
}
}
}
bool Node::is_property_pinned(const StringName &p_property) const {
if (!has_meta("_edit_pinned_properties_")) {
return false;
}
Array pinned = get_meta("_edit_pinned_properties_");
String psa = get_property_store_alias(p_property);
return pinned.has(psa);
}
StringName Node::get_property_store_alias(const StringName &p_property) const {
return p_property;
}
#endif
void Node::get_storable_properties(Set<StringName> &r_storable_properties) const {
List<PropertyInfo> pi;
get_property_list(&pi);
for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
if ((E->get().usage & PROPERTY_USAGE_STORAGE)) {
r_storable_properties.insert(E->get().name);
}
}
}
String Node::to_string() {
if (get_script_instance()) {
bool valid;
@ -2811,6 +2861,10 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
#endif
{
MethodInfo mi;

View file

@ -314,6 +314,13 @@ public:
bool is_editable_instance(const Node *p_node) const;
Node *get_deepest_editable_node(Node *start_node) const;
#ifdef TOOLS_ENABLED
void set_property_pinned(const StringName &p_property, bool p_pinned);
bool is_property_pinned(const StringName &p_property) const;
virtual StringName get_property_store_alias(const StringName &p_property) const;
#endif
void get_storable_properties(Set<StringName> &r_storable_properties) const;
virtual String to_string();
/* NOTIFICATIONS */

186
scene/property_utils.cpp Normal file
View file

@ -0,0 +1,186 @@
/*************************************************************************/
/* property_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "property_utils.h"
#include "core/core_string_names.h"
#include "core/engine.h"
#include "core/local_vector.h"
#include "editor/editor_node.h"
#include "scene/resources/packed_scene.h"
bool PropertyUtils::is_property_value_different(const Variant &p_a, const Variant &p_b) {
if (p_a.get_type() == Variant::REAL && p_b.get_type() == Variant::REAL) {
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
return !Math::is_equal_approx((float)p_a, (float)p_b);
} else {
// For our purposes, treating null object as NIL is the right thing to do
const Variant &a = p_a.get_type() == Variant::OBJECT && (Object *)p_a == nullptr ? Variant() : p_a;
const Variant &b = p_b.get_type() == Variant::OBJECT && (Object *)p_b == nullptr ? Variant() : p_b;
return a != b;
}
}
Variant PropertyUtils::get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache, bool p_update_exports, const Node *p_owner, bool *r_is_class_default) {
// This function obeys the way property values are set when an object is instantiated,
// which is the following (the latter wins):
// 1. Default value from builtin class
// 2. Default value from script exported variable (from the topmost script)
// 3. Value overrides from the instantiation/inheritance stack
if (r_is_class_default) {
*r_is_class_default = false;
}
Ref<Script> topmost_script;
if (const Node *node = Object::cast_to<Node>(p_object)) {
// Check inheritance/instantiation ancestors
const Vector<SceneState::PackState> &states_stack = p_states_stack_cache ? *p_states_stack_cache : PropertyUtils::get_node_states_stack(node, p_owner);
for (int i = 0; i < states_stack.size(); ++i) {
const SceneState::PackState &ia = states_stack[i];
bool found = false;
Variant value_in_ancestor = ia.state->get_property_value(ia.node, p_property, found);
if (found) {
return value_in_ancestor;
}
// Save script for later
bool has_script = false;
Variant script = ia.state->get_property_value(ia.node, CoreStringNames::get_singleton()->_script, has_script);
if (has_script) {
Ref<Script> scr = script;
if (scr.is_valid()) {
topmost_script = scr;
}
}
}
}
// Let's see what default is set by the topmost script having a default, if any
if (topmost_script.is_null()) {
topmost_script = p_object->get_script();
}
if (topmost_script.is_valid()) {
Variant default_value;
// Should be called in the editor only and not at runtime,
// otherwise it can cause problems because of missing instance state support.
if (p_update_exports && Engine::get_singleton()->is_editor_hint()) {
topmost_script->update_exports();
}
if (topmost_script->get_property_default_value(p_property, default_value)) {
return default_value;
}
}
// Fall back to the default from the native class
if (r_is_class_default) {
*r_is_class_default = true;
}
return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
}
// Like SceneState::PackState, but using a raw pointer to avoid the cost of
// updating the reference count during the internal work of the functions below
namespace {
struct _FastPackState {
SceneState *state = nullptr;
int node = -1;
};
} // namespace
static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const NodePath &p_path, LocalVector<_FastPackState> &r_states_stack) {
bool found = false;
LocalVector<_FastPackState> inheritance_states;
Ref<SceneState> state = p_state;
while (state.is_valid()) {
int node = state->find_node_by_path(p_path);
if (node >= 0) {
// This one has state for this node
inheritance_states.push_back({ state.ptr(), node });
found = true;
}
state = state->get_base_scene_state();
}
for (int i = inheritance_states.size() - 1; i >= 0; --i) {
r_states_stack.push_back(inheritance_states[i]);
}
return found;
}
Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p_node, const Node *p_owner, bool *r_instanced_by_owner) {
if (r_instanced_by_owner) {
*r_instanced_by_owner = true;
}
LocalVector<_FastPackState> states_stack;
{
const Node *owner = p_owner;
#ifdef TOOLS_ENABLED
if (!p_owner && Engine::get_singleton()->is_editor_hint()) {
owner = EditorNode::get_singleton()->get_edited_scene();
}
#endif
const Node *n = p_node;
while (n) {
if (n == owner) {
const Ref<SceneState> &state = n->get_scene_inherited_state();
if (_collect_inheritance_chain(state, n->get_path_to(p_node), states_stack)) {
if (r_instanced_by_owner) {
*r_instanced_by_owner = false;
}
}
break;
} else if (n->get_filename() != String()) {
const Ref<SceneState> &state = n->get_scene_instance_state();
_collect_inheritance_chain(state, n->get_path_to(p_node), states_stack);
}
n = n->get_owner();
}
}
// Convert to the proper type for returning, inverting the vector on the go
// (it was more convenient to fill the vector in reverse order)
Vector<SceneState::PackState> states_stack_ret;
{
states_stack_ret.resize(states_stack.size());
_FastPackState *ps = states_stack.ptr();
for (int i = states_stack.size() - 1; i >= 0; --i) {
states_stack_ret.write[i].state.reference_ptr(ps->state);
states_stack_ret.write[i].node = ps->node;
++ps;
}
}
return states_stack_ret;
}

51
scene/property_utils.h Normal file
View file

@ -0,0 +1,51 @@
/*************************************************************************/
/* property_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PROPERTY_UTILS_H
#define PROPERTY_UTILS_H
#include "scene/main/node.h"
#include "scene/resources/packed_scene.h"
class PropertyUtils {
public:
static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
// Gets the most pure default value, the one that would be set when the node has just been instantiated
static Variant get_property_default_value(const Object *p_object, const StringName &p_property, const Vector<SceneState::PackState> *p_states_stack_cache = nullptr, bool p_update_exports = false, const Node *p_owner = nullptr, bool *r_is_class_default = nullptr);
// Gets the instance/inheritance states of this node, in order of precedence,
// that is, from the topmost (the most able to override values) to the lowermost
// (Note that in nested instancing the one with the greatest precedence is the furthest
// in the tree, since every owner found while traversing towards the root gets a chance
// to override property values.)
static Vector<SceneState::PackState> get_node_states_stack(const Node *p_node, const Node *p_owner = nullptr, bool *r_instanced_by_owner = nullptr);
};
#endif // PROPERTY_UTILS_H

View file

@ -34,10 +34,12 @@
#include "core/engine.h"
#include "core/io/resource_loader.h"
#include "core/project_settings.h"
#include "editor/editor_inspector.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/spatial.h"
#include "scene/gui/control.h"
#include "scene/main/instance_placeholder.h"
#include "scene/property_utils.h"
#define PACKED_SCENE_VERSION 2
@ -45,6 +47,30 @@ bool SceneState::can_instance() const {
return nodes.size() > 0;
}
static Array _sanitize_node_pinned_properties(Node *p_node) {
if (!p_node->has_meta("_edit_pinned_properties_")) {
return Array();
}
Array pinned = p_node->get_meta("_edit_pinned_properties_");
if (pinned.empty()) {
return Array();
}
Set<StringName> storable_properties;
p_node->get_storable_properties(storable_properties);
int i = 0;
do {
if (storable_properties.has(pinned[i])) {
i++;
} else {
pinned.remove(i);
}
} while (i < pinned.size());
if (pinned.empty()) {
p_node->remove_meta("_edit_pinned_properties_");
}
return pinned;
}
Node *SceneState::instance(GenEditState p_edit_state) const {
// nodes where instancing failed (because something is missing)
List<Node *> stray_instances;
@ -225,7 +251,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
} else {
Node *base = i == 0 ? node : ret_nodes[0];
if (p_edit_state == GEN_EDIT_STATE_MAIN) {
if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) {
//for the main scene, use the resource as is
res->configure_for_local_scene(base, resources_local_to_scene);
resources_local_to_scene[res] = res;
@ -289,6 +315,13 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
}
}
// we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting)
if (p_edit_state == GEN_EDIT_STATE_MAIN) {
_sanitize_node_pinned_properties(node);
} else {
node->remove_meta("_edit_pinned_properties_");
}
ret_nodes[i] = node;
if (node && gen_node_path_cache && ret_nodes[0]) {
@ -413,61 +446,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// with the instance states, we can query for identical properties/groups
// and only save what has changed
List<PackState> pack_state_stack;
bool instanced_by_owner = false;
Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instanced_by_owner);
bool instanced_by_owner = true;
{
Node *n = p_node;
while (n) {
if (n == p_owner) {
Ref<SceneState> state = n->get_scene_inherited_state();
if (state.is_valid()) {
int node = state->find_node_by_path(n->get_path_to(p_node));
if (node >= 0) {
//this one has state for this node, save
PackState ps;
ps.node = node;
ps.state = state;
pack_state_stack.push_back(ps);
instanced_by_owner = false;
}
}
if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instanced_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
nd.instance = _vm_get_variant(p_node->get_filename(), variant_map);
nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
} else {
//must instance ourselves
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
nd.instance = _vm_get_variant(instance, variant_map);
}
}
n = nullptr;
} else {
if (n->get_filename() != String()) {
//is an instance
Ref<SceneState> state = n->get_scene_instance_state();
if (state.is_valid()) {
int node = state->find_node_by_path(n->get_path_to(p_node));
if (node >= 0) {
//this one has state for this node, save
PackState ps;
ps.node = node;
ps.state = state;
pack_state_stack.push_back(ps);
}
}
}
n = n->get_owner();
if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instanced_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
nd.instance = _vm_get_variant(p_node->get_filename(), variant_map);
nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
} else {
//must instance ourselves
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
nd.instance = _vm_get_variant(instance, variant_map);
}
}
@ -476,88 +470,37 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
StringName type = p_node->get_class();
Ref<Script> script = p_node->get_script();
if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
// Should be called in the editor only and not at runtime,
// otherwise it can cause problems because of missing instance state support.
script->update_exports();
}
Array pinned_props = _sanitize_node_pinned_properties(p_node);
for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
Variant forced_value;
// If instance or inheriting, not saving if property requested so, or it's meta
if (states_stack.size()) {
if ((E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) {
continue;
}
// Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list
if (E->get().name == "__meta__") {
if (pinned_props.size()) {
Dictionary meta_override;
meta_override["_edit_pinned_properties_"] = pinned_props;
forced_value = meta_override;
}
}
}
String name = E->get().name;
Variant value = p_node->get(E->get().name);
Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value;
bool isdefault = false;
Variant default_value = ClassDB::class_get_default_property_value(type, name);
if (default_value.get_type() != Variant::NIL) {
isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
}
if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) {
isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
}
// the version above makes more sense, because it does not rely on placeholder or usage flag
// in the script, just the default value function.
// if (E->get().usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
// isdefault = true; //is script default value
// }
if (pack_state_stack.size()) {
// we are on part of an instanced subscene
// or part of instanced scene.
// only save what has been changed
// only save changed properties in instance
if ((E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E->get().name == "__meta__") {
//property has requested that no instance state is saved, sorry
//also, meta won't be overridden or saved
continue;
}
bool exists = false;
Variant original;
for (List<PackState>::Element *F = pack_state_stack.back(); F; F = F->prev()) {
//check all levels of pack to see if the property exists somewhere
const PackState &ps = F->get();
original = ps.state->get_property_value(ps.node, E->get().name, exists);
if (exists) {
break;
}
}
if (exists) {
//check if already exists and did not change
if (value.get_type() == Variant::REAL && original.get_type() == Variant::REAL) {
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
float a = value;
float b = original;
if (Math::is_equal_approx(a, b)) {
continue;
}
} else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
continue;
}
}
if (!exists && isdefault) {
//does not exist in original node, but it's the default value
//so safe to skip too.
continue;
}
} else {
if (isdefault) {
//it's the default value, no point in saving it
if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) {
Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &states_stack, true);
if (!PropertyUtils::is_property_value_different(value, default_value)) {
continue;
}
}
@ -585,10 +528,10 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
*/
bool skip = false;
for (List<PackState>::Element *F = pack_state_stack.front(); F; F = F->next()) {
for (int i = 0; i < states_stack.size(); ++i) {
const auto &ia = states_stack[i];
//check all levels of pack to see if the group was added somewhere
const PackState &ps = F->get();
if (ps.state->is_node_in_group(ps.node, gi.name)) {
if (ia.state->is_node_in_group(ia.node, gi.name)) {
skip = true;
break;
}
@ -618,7 +561,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
if (pack_state_stack.empty() && !is_editable_instance) {
if (states_stack.empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map);
} else {
@ -932,7 +875,7 @@ void SceneState::clear() {
base_scene_idx = -1;
}
Ref<SceneState> SceneState::_get_base_scene_state() const {
Ref<SceneState> SceneState::get_base_scene_state() const {
if (base_scene_idx >= 0) {
Ref<PackedScene> ps = variants[base_scene_idx];
if (ps.is_valid()) {
@ -947,8 +890,8 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
if (!node_path_cache.has(p_node)) {
if (_get_base_scene_state().is_valid()) {
int idx = _get_base_scene_state()->find_node_by_path(p_node);
if (get_base_scene_state().is_valid()) {
int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
int rkey = _find_base_scene_node_remap_key(idx);
if (rkey == -1) {
@ -963,11 +906,11 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
int nid = node_path_cache[p_node];
if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
if (get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
//for nodes that _do_ exist in current scene, still try to look for
//the node in the instanced scene, as a property may be missing
//from the local one
int idx = _get_base_scene_state()->find_node_by_path(p_node);
int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
base_scene_node_remap[nid] = idx;
}
@ -1007,7 +950,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property,
//property not found, try on instance
if (base_scene_node_remap.has(p_node)) {
return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
return get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
}
return Variant();
@ -1026,7 +969,7 @@ bool SceneState::is_node_in_group(int p_node, const StringName &p_group) const {
}
if (base_scene_node_remap.has(p_node)) {
return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
return get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
}
return false;
@ -1065,7 +1008,7 @@ bool SceneState::is_connection(int p_node, const StringName &p_signal, int p_to_
}
if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
return get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
}
return false;
@ -1483,7 +1426,7 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p
}
}
ss = ss->_get_base_scene_state();
ss = ss->get_base_scene_state();
} while (ss.is_valid());
return false;
@ -1608,6 +1551,7 @@ void SceneState::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
SceneState::SceneState() {
@ -1698,6 +1642,7 @@ void PackedScene::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
PackedScene::PackedScene() {

View file

@ -69,12 +69,6 @@ class SceneState : public Reference {
Vector<int> groups;
};
struct PackState {
Ref<SceneState> state;
int node;
PackState() { node = -1; }
};
Vector<NodeData> nodes;
struct ConnectionData {
@ -95,8 +89,6 @@ class SceneState : public Reference {
uint64_t last_modified_time;
_FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
static bool disable_placeholders;
PoolVector<String> _get_node_groups(int p_idx) const;
@ -118,6 +110,12 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
GEN_EDIT_STATE_MAIN_INHERITED,
};
struct PackState {
Ref<SceneState> state;
int node = -1;
};
static void set_disable_placeholders(bool p_disable);
@ -140,6 +138,8 @@ public:
bool can_instance() const;
Node *instance(GenEditState p_edit_state) const;
Ref<SceneState> get_base_scene_state() const;
//unbuild API
int get_node_count() const;
@ -208,6 +208,7 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
GEN_EDIT_STATE_MAIN_INHERITED,
};
Error pack(Node *p_scene);