Godot version: 3.0.2
Issue description:
Calling append, insert, erase, push_*, pop_*, sort*, clear, resize or remove on an array with a setter doesn't trigger its setter. Calling clear and erase on dictionaries doesn't trigger it either. It works for dictionaries if you add new entries so it should work for arrays too.
Steps to reproduce:
-Add the scripts to nodes
Array:
extends Node
func _ready():
var a = A.new()
a.b.append(3) #Or the other methods
class A:
var b = [1,2] setget setter
func setter(value):
print(value) #Never called
Dictionary:
extends Node
func _ready():
var a = A.new()
a.b.orange = 4 #erase() and clear() don't trigger it
class A:
var b = {} setget setter
func setter(value):
print(value) #Prints '(orange:4)'
Actually the setter getting called for modifying a dictionary seems to be a bug?
A setter should only be called, if you set the variable to a different value / object.
If you call append on an object, you are actually calling the getter to get the object and then work on the object. The getter does not care what you do with the object. Its only job is to give you the object.
The setter is never called since you are not setting the variable to a different object. You are just modifying values on the object which is not the same as setting it.
If there's a need for functions to be called when an array or a dictionary is modified then the container classes should get signals, so you can connect a function to the change.
I agree with ShyRed, calling a setter or getter when methods of a container are called is too inconsistent with the usual meaning of setters and getters.
But what should happen now? Should the setter not be called anymore for dictionary modifications? Is there somebody who depends on this behavior? Should somebody open a new issue for container signals? Should I close the issue?
Would need a Godot dev / team member to get an official statement on this.
My 2 cents:
The fact that the setter is called when doing an "indexed set" on the dictionary is because of how GDScript does indexing internally.
Suppose there is a property called transform of type Transform2D.
Some code does transform.o.x = 6. (o is origin in this case)
But, Transform2D is somewhat immutable, as is Vector2, and if the code just naively sets the x of the o, it won't change anything.
So, it would do something like this:
var local_transform = transform
var local_o = transform.o
local_o.x = 6 # Those are vars, so we can change their properties
local_transform.o = local_o
transform = local_transform
Not sure how this should be fixed, since the compiler does not know anything about whether the value is shared (Dictionary, Array, Object) or unshared (Vector2, etc.) at compile time.
Here are some relevant lines in the code:
Can anyone still reproduce this bug in Godot 3.2.1 or 3.2.2beta4?
Note that GDScript is being rewritten for Godot 4.0.
Should still be valid on 3.2.x.
Confirmed on 3.2.1 and 3.2.2beta4 (and also on master).
Most helpful comment
Actually the setter getting called for modifying a dictionary seems to be a bug?
A setter should only be called, if you set the variable to a different value / object.
If you call
appendon an object, you are actually calling the getter to get the object and then work on the object. The getter does not care what you do with the object. Its only job is to give you the object.The setter is never called since you are not setting the variable to a different object. You are just modifying values on the object which is not the same as setting it.