Changes to a component during PostEditChangeChainProperty() do not take effect, though they appear to in the editor

After a several day battle, I finally have hard evidence that PostEditChangeChainProperty() isn’t working correctly. (Though the similar PostEditChangeProperty() does, PostEditChangeProperty() doesn’t work for structs.)

When a property is edited on a component in the editor, it appears that the component is copied to an identical component which has the same name, but suffixed with “_GEN_VARIABLE”. PostEditChangeProperty() will fire on both the actual component, as well as the _GEN_VARIABLE version. HOWEVER, PostEditChangeChainProperty() ONLY fires on the “_GEN_VARIABLE” version. The _GEN_VARIABLE data then appears to be used to update the Editor UI (so the properties will appear to change correctly), however, it does not actually propagate. If more direct methods are used to the actual data, it will reveal that it has not actually changed, resulting in a disagreement between what the editor is displaying and what’s really happened to the component’s data.

I’ve uploaded a zip of the relavent components and uasset [here][1]
You’ll need to get those components to compile and then import the UAsset.
I can provide a full uproject if need be, but arrangements would have to be made since even an almost empty project is a few hundred MB and this place only allows 5.2MB files.

Repro steps:

  1. Open the repro case above.
  2. Open the “BP_PostEditPropTest” Blueprint CDO in the content directory
  3. Open an output window so you can see log output

First, we’ll test the working PostEditChangeProperty to show the test case functions.

Select the “PostEditPropertyChange” component.

Confirm that “Event To Test” is set to “PostEditPropertyChange”, “Foo” is TRUE, and “Bar” is FALSE

Set “Foo” to FALSE.

Observe that “Bar” automatically becomes TRUE. (This is done in PostEditPropertyChange)

Check the log. Observe the following output:

LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: PostEditChangeProperty
LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: Property change: Foo
LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: Foo changed to 0. Bar set to 1.
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: PostEditChangeProperty
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: Property change: Foo
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: Foo changed to 0. Bar set to 1.

Note that there are TWO components firing events here. The “real” one, and the orphaned “GEN_VARIABLE” copy.

Select the “Property Outputter” component

Click the “Click Me To Log Values” bool

Check the log. Observe

LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: Actual values are currently Foo: 0 and Bar: 1.

This correctly reflects the change we made, as well as the change made by the PostEditPropertyChange.

Everything worked properly, yay!

Now we’ll try the same thing, except using the (broken) PostEditChangeChainProperty() version

Select the “PostEditPropertyChange” component again.

Set “Event To Test” to “PostEditChangeChainProperty”

Set “Foo” back to TRUE.

Observe that “Foo” changes to TRUE, and “Bar” automatically changes to “FALSE.” It looks like everything worked again, right? Well, here’s where things get weird.

Look at the log. It will :

LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: PostEditChangeProperty
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: PostEditChangeProperty
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: PostEditChangeChainProperty
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: Property change: Foo
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: Foo changed to 1. Bar set to 0.
LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: PostEditChangeProperty
LogTemp:Warning: NO_PARENT_ACTOR/PostEditPropertyChange_GEN_VARIABLE: PostEditChangeProperty

Note that this time, while the GEN_VARIABLE version toggled the value of Bar when Foo changed, the “real” version of the component DIDN’T. But yet, the editor is displaying values of Foo and Bar as if it HAD. We can verify this by accessing the values for Foo and Bar directly via code.

Select the “Property Outputter” component

Click the “Click Me To Log Values” bool

Check the log. Observe

LogTemp:Warning: BP_PostEditPropTest_C_0/PostEditPropertyChange: Actual values are currently Foo: 1 and Bar: 1.

So, while “Foo” changed correctly (it’s the one we set manually) Bar is still TRUE because the “real” version of the component never executed the PostEditChangeChainProperty event.
[1]: 121976-chainpropertybroken.zip (9.35 KB)

Hey -

The PostEditChangeChainProperty() appears to be working as intended. As you mentioned, this function is specifically for structs. Since the sample code does not contain a struct, PostEditChangeChainProperty() is not being called. To test this I made the following edits to the provided code:

  • Added Struct to PostEditPropertyChangeComponent.h

    USTRUCT()
    struct FMyStruct
    {
    GENERATED_BODY()

     UPROPERTY()
     bool Struct_Foo;
     UPROPERTY()
     bool Struct_Bar;
    
     FMyStruct()
     {
     	Struct_Foo;
     	Struct_Bar;
     }
    

    };

  • Added Struct variable to class declaration

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Test)
    FMyStruct TestStruct;

  • Set Struct_Foo & Struct_Bar to Foo & Bar (respectively) in constructor

    TestStruct.Struct_Foo = Foo = true;
    TestStruct.Struct_Bar = Bar = false;

  • In DoOnPropertyChangedStruff() I updated the struct variables when Foo and Bar are updated

  • (in if statement)

    TestStruct.Struct_Foo = Foo;
    TestStruct.Struct_Bar = Bar = !Foo;

  • In else if statement

    TestStruct.Struct_Bar = Bar;
    TestStruct.Struct_Foo = Foo = !Bar;

  • Added additional logging for Struct_Foo & Struct_Bar in LogCurrentValues()

    UE_LOG(LogTemp, Error, TEXT(“%s/%s: Actual values are currently Struct_Foo: %d and Struct_Bar: %d.”), *ActorName, *GetFName().ToString(), (int)TestStruct.Struct_Foo, (int)TestStruct.Struct_Bar);
    With this added code, changing either your Foo or Bar variable in the editor will also update both struct variables in the same way as the class variables. When EventToTest is set to PostEditChangeChainProperty, you’ll find that Struct_Foo and Struct_Bar are being updated as expected. Let me know if you have any further questions about the use of these functions.

Cheers

I’ll have to take a look with your changes, but this is an isolated test case which originally was dealing with properties of a struct changing (An FIntVector) and the behavior was the same.

Either way, though, there’s still a bug here. If PostEditChangeChainProperty is supposed to fire for non-structs, then the bug is as described. But even if PostEditChangeChainProperty isn’t supposed to fire when a non-struct changes, then the bug is that it is firing on the _GEN_VARIABLE version, and that the editor’s property grid is changing to reflect a change in data which never occurred. Either way, PostEditChangeChainProperty is causing the editor’s property grid to get out of synch with the object it’s displaying the properties of.

1 Like

The GEN_VARIABLE is a temporary actor that is separate from the actual actor that is part of the viewport. If you are able to reproduce a case where the struct variables are not updating we will reinvestigate the PostEditChangeChainProperty() function, however this appears to be working as intended.