propagate parent input change to nested input

We have decided to add a new attribute to our code generator that will allow to overwrite the behavior of property setters for the c++ runtime.
For the editor, the solution is more complex because we still need to reflect the nested artboard value in the inspector so users can follow changes both at the parent and the child level.

Diffs=
fee67f16d propagate parent input change to nested input (#6878)
ff2c0cbf6 apply final update if it has not been applied before (#6930)

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index cc1de3e..292ff7c 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-f3739347cf81c6c35d445da3678f987626ce7df3
+fee67f16df7648b5c9bc100b6221c7ba5baca0af
diff --git a/dev/core_generator/lib/src/definition.dart b/dev/core_generator/lib/src/definition.dart
index 6515f77..4e02bf6 100644
--- a/dev/core_generator/lib/src/definition.dart
+++ b/dev/core_generator/lib/src/definition.dart
@@ -220,20 +220,26 @@
               (property.isSetOverride ? 'override' : '') +
               '= 0;');
         } else {
-          code.writeln((property.isVirtual ? 'virtual' : 'inline') +
+          code.writeln(((property.isVirtual || property.isPureVirtual)
+                  ? 'virtual'
+                  : 'inline') +
               ' ${property.type.cppGetterName} ${property.name}() const ' +
               (property.isGetOverride ? 'override' : '') +
               '{ return m_${property.capitalizedName}; }');
-
-          code.writeln(
-              'void ${property.name}(${property.type.cppName} value) ' +
-                  (property.isSetOverride ? 'override' : '') +
-                  '{'
-                      'if(m_${property.capitalizedName} == value)'
-                      '{return;}'
-                      'm_${property.capitalizedName} = value;'
-                      '${property.name}Changed();'
-                      '}');
+          if (!property.isPureVirtual) {
+            code.writeln(
+                'void ${property.name}(${property.type.cppName} value) ' +
+                    (property.isSetOverride ? 'override' : '') +
+                    '{'
+                        'if(m_${property.capitalizedName} == value)'
+                        '{return;}'
+                        'm_${property.capitalizedName} = value;'
+                        '${property.name}Changed();'
+                        '}');
+          } else {
+            code.writeln(
+                'virtual void ${property.name}(${property.type.cppName} value) = 0;');
+          }
         }
 
         code.writeln();
diff --git a/dev/core_generator/lib/src/property.dart b/dev/core_generator/lib/src/property.dart
index e13ae67..e147e81 100644
--- a/dev/core_generator/lib/src/property.dart
+++ b/dev/core_generator/lib/src/property.dart
@@ -20,6 +20,7 @@
   bool isSetOverride = false;
   bool isGetOverride = false;
   bool isEncoded = false;
+  bool isPureVirtual = false;
   FieldType? typeRuntime;
 
   static Property? make(
@@ -93,6 +94,10 @@
     if (rt is String) {
       typeRuntime = FieldType.find(rt);
     }
+    dynamic pv = data['pureVirtual'];
+    if (pv is bool) {
+      isPureVirtual = pv;
+    }
     key = Key.fromJSON(data['key']) ?? Key.forProperty(this);
   }
 
diff --git a/dev/defs/animation/nested_bool.json b/dev/defs/animation/nested_bool.json
index b55c59a..31aa257 100644
--- a/dev/defs/animation/nested_bool.json
+++ b/dev/defs/animation/nested_bool.json
@@ -10,6 +10,7 @@
       "type": "bool",
       "initialValue": "false",
       "animates": true,
+      "pureVirtual": true,
       "key": {
         "int": 238,
         "string": "value"
diff --git a/dev/defs/animation/nested_number.json b/dev/defs/animation/nested_number.json
index 7315cf1..c845ea4 100644
--- a/dev/defs/animation/nested_number.json
+++ b/dev/defs/animation/nested_number.json
@@ -10,6 +10,7 @@
       "type": "double",
       "initialValue": "0",
       "animates": true,
+      "pureVirtual": true,
       "key": {
         "int": 239,
         "string": "nestedValue"
diff --git a/include/rive/animation/nested_bool.hpp b/include/rive/animation/nested_bool.hpp
index 813ece2..6421069 100644
--- a/include/rive/animation/nested_bool.hpp
+++ b/include/rive/animation/nested_bool.hpp
@@ -7,10 +7,12 @@
 class NestedBool : public NestedBoolBase
 {
 public:
+    void nestedValue(bool value) override;
+    bool nestedValue() const override;
+
     void applyValue() override;
 
 protected:
-    void nestedValueChanged() override;
 };
 } // namespace rive
 
diff --git a/include/rive/animation/nested_input.hpp b/include/rive/animation/nested_input.hpp
index 16d5bf9..0db46df 100644
--- a/include/rive/animation/nested_input.hpp
+++ b/include/rive/animation/nested_input.hpp
@@ -22,7 +22,7 @@
     virtual void applyValue() {}
 
 protected:
-    SMIInput* input()
+    SMIInput* input() const
     {
         auto parent = this->parent();
         if (parent != nullptr && parent->is<NestedStateMachine>())
diff --git a/include/rive/animation/nested_number.hpp b/include/rive/animation/nested_number.hpp
index b21dfc3..f965126 100644
--- a/include/rive/animation/nested_number.hpp
+++ b/include/rive/animation/nested_number.hpp
@@ -7,10 +7,11 @@
 class NestedNumber : public NestedNumberBase
 {
 public:
+    void nestedValue(float value) override;
+    float nestedValue() const override;
     void applyValue() override;
 
 protected:
-    void nestedValueChanged() override;
 };
 } // namespace rive
 
diff --git a/include/rive/generated/animation/nested_bool_base.hpp b/include/rive/generated/animation/nested_bool_base.hpp
index d756d95..8c015f5 100644
--- a/include/rive/generated/animation/nested_bool_base.hpp
+++ b/include/rive/generated/animation/nested_bool_base.hpp
@@ -35,16 +35,8 @@
     bool m_NestedValue = false;
 
 public:
-    inline bool nestedValue() const { return m_NestedValue; }
-    void nestedValue(bool value)
-    {
-        if (m_NestedValue == value)
-        {
-            return;
-        }
-        m_NestedValue = value;
-        nestedValueChanged();
-    }
+    virtual bool nestedValue() const { return m_NestedValue; }
+    virtual void nestedValue(bool value) = 0;
 
     Core* clone() const override;
     void copy(const NestedBoolBase& object)
diff --git a/include/rive/generated/animation/nested_number_base.hpp b/include/rive/generated/animation/nested_number_base.hpp
index e50ecac..82bc0c9 100644
--- a/include/rive/generated/animation/nested_number_base.hpp
+++ b/include/rive/generated/animation/nested_number_base.hpp
@@ -35,16 +35,8 @@
     float m_NestedValue = 0.0f;
 
 public:
-    inline float nestedValue() const { return m_NestedValue; }
-    void nestedValue(float value)
-    {
-        if (m_NestedValue == value)
-        {
-            return;
-        }
-        m_NestedValue = value;
-        nestedValueChanged();
-    }
+    virtual float nestedValue() const { return m_NestedValue; }
+    virtual void nestedValue(float value) = 0;
 
     Core* clone() const override;
     void copy(const NestedNumberBase& object)
diff --git a/src/animation/nested_bool.cpp b/src/animation/nested_bool.cpp
index 29cd646..e575022 100644
--- a/src/animation/nested_bool.cpp
+++ b/src/animation/nested_bool.cpp
@@ -6,8 +6,8 @@
 using namespace rive;
 class StateMachineInstance;
 
-void NestedBool::nestedValueChanged() { this->applyValue(); }
-
+// Use the NestedBoolBase m_NestedValue on initialization but then it won't
+// be used anymore and interface directly with the nested input value.
 void NestedBool::applyValue()
 {
     auto inputInstance = input();
@@ -16,7 +16,34 @@
         auto boolInput = static_cast<SMIBool*>(inputInstance);
         if (boolInput != nullptr)
         {
-            boolInput->value(nestedValue());
+            boolInput->value(NestedBoolBase::nestedValue());
         }
     }
+}
+
+void NestedBool::nestedValue(bool value)
+{
+    auto inputInstance = input();
+    if (inputInstance != nullptr)
+    {
+        auto boolInput = static_cast<SMIBool*>(inputInstance);
+        if (boolInput != nullptr && boolInput->value() != value)
+        {
+            boolInput->value(value);
+        }
+    }
+}
+
+bool NestedBool::nestedValue() const
+{
+    auto inputInstance = input();
+    if (inputInstance != nullptr)
+    {
+        auto boolInput = static_cast<SMIBool*>(inputInstance);
+        if (boolInput != nullptr)
+        {
+            return boolInput->value();
+        }
+    }
+    return false;
 }
\ No newline at end of file
diff --git a/src/animation/nested_number.cpp b/src/animation/nested_number.cpp
index a81236f..a3d8829 100644
--- a/src/animation/nested_number.cpp
+++ b/src/animation/nested_number.cpp
@@ -6,8 +6,8 @@
 using namespace rive;
 class StateMachineInstance;
 
-void NestedNumber::nestedValueChanged() { this->applyValue(); }
-
+// Use the NestedNumberBase m_NestedValue on initialization but then it won't
+// be used anymore and interface directly with the nested input value.
 void NestedNumber::applyValue()
 {
     auto inputInstance = input();
@@ -16,7 +16,34 @@
         auto numInput = static_cast<SMINumber*>(inputInstance);
         if (numInput != nullptr)
         {
-            numInput->value(nestedValue());
+            numInput->value(NestedNumberBase::nestedValue());
         }
     }
+}
+
+void NestedNumber::nestedValue(float value)
+{
+    auto inputInstance = input();
+    if (inputInstance != nullptr)
+    {
+        auto numInput = static_cast<SMINumber*>(inputInstance);
+        if (numInput != nullptr && numInput->value() != value)
+        {
+            numInput->value(value);
+        }
+    }
+}
+
+float NestedNumber::nestedValue() const
+{
+    auto inputInstance = input();
+    if (inputInstance != nullptr)
+    {
+        auto numInput = static_cast<SMINumber*>(inputInstance);
+        if (numInput != nullptr)
+        {
+            return numInput->value();
+        }
+    }
+    return 0.0;
 }
\ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 65fc319..0348c6e 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -83,10 +83,7 @@
             m_stateFrom->advance(seconds, m_stateMachineInstance);
         }
 
-        if (isTransitionEnded())
-        {
-            apply();
-        }
+        apply();
 
         for (int i = 0; updateState(i != 0); i++)
         {
@@ -99,8 +96,6 @@
             }
         }
 
-        apply();
-
         m_currentState->clearSpilledTime();
 
         return m_mix != 1.0f || m_waitingForExit ||
@@ -113,12 +108,6 @@
                m_mix < 1.0f;
     }
 
-    bool isTransitionEnded()
-    {
-        return m_transition != nullptr && m_stateFrom != nullptr && m_transition->duration() != 0 &&
-               m_mix == 1.0f;
-    }
-
     bool updateState(bool ignoreTriggers)
     {
         // Don't allow changing state while a transition is taking place