| #include "rive/animation/transition_viewmodel_condition.hpp" |
| #include "rive/animation/state_transition.hpp" |
| #include "rive/animation/transition_property_component_comparator.hpp" |
| #include "rive/artboard.hpp" |
| #include "rive/core/field_types/core_bool_type.hpp" |
| #include "rive/core/field_types/core_color_type.hpp" |
| #include "rive/core/field_types/core_double_type.hpp" |
| #include "rive/core/field_types/core_string_type.hpp" |
| #include "rive/core/field_types/core_uint_type.hpp" |
| #include "rive/generated/core_registry.hpp" |
| #include "rive/generated/custom_property_enum_base.hpp" |
| #include "rive/generated/custom_property_trigger_base.hpp" |
| #include "rive/generated/viewmodel/viewmodel_instance_artboard_base.hpp" |
| #include "rive/generated/viewmodel/viewmodel_instance_asset_base.hpp" |
| #include "rive/generated/viewmodel/viewmodel_instance_enum_base.hpp" |
| #include "rive/generated/viewmodel/viewmodel_instance_trigger_base.hpp" |
| #include "rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp" |
| #include "rive/importers/state_transition_importer.hpp" |
| #include "rive/importers/state_machine_importer.hpp" |
| #include "rive/animation/state_machine.hpp" |
| #include "rive/animation/state_machine_instance.hpp" |
| #include "rive/component_dirt.hpp" |
| #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" |
| |
| #include <vector> |
| |
| using namespace rive; |
| |
| class ConditionComparisonNone : public ConditionComparison |
| { |
| public: |
| ConditionComparisonNone() : ConditionComparison(nullptr) {} |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return false; |
| } |
| }; |
| |
| class ConditionComparisonSelf : public ConditionComparison |
| { |
| public: |
| ConditionComparisonSelf(BindableProperty* property) : |
| ConditionComparison(nullptr), m_bindableProperty(property) |
| {} |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| auto bindableInstance = |
| stateMachineInstance->bindablePropertyInstance(m_bindableProperty); |
| auto dataBind = |
| stateMachineInstance->bindableDataBindToTarget(bindableInstance); |
| if (dataBind != nullptr) |
| { |
| auto source = dataBind->source(); |
| if (source != nullptr && source->hasChanged() && |
| !source->isUsedInLayer(layerInstance)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private: |
| BindableProperty* m_bindableProperty; |
| }; |
| class ConditionComparisonNumber : public ConditionComparison |
| { |
| public: |
| ConditionComparisonNumber(ConditionComparandNumber* left, |
| ConditionComparandNumber* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonNumber() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareNumbers(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandNumber* m_leftComparand = nullptr; |
| ConditionComparandNumber* m_rightComparand = nullptr; |
| }; |
| class ConditionComparisonBoolean : public ConditionComparison |
| { |
| public: |
| ConditionComparisonBoolean(ConditionComparandBoolean* left, |
| ConditionComparandBoolean* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonBoolean() override |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareBooleans(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandBoolean* m_leftComparand = nullptr; |
| ConditionComparandBoolean* m_rightComparand = nullptr; |
| }; |
| |
| class ConditionComparisonString : public ConditionComparison |
| { |
| public: |
| ConditionComparisonString(ConditionComparandString* left, |
| ConditionComparandString* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonString() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareStrings(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandString* m_leftComparand = nullptr; |
| ConditionComparandString* m_rightComparand = nullptr; |
| }; |
| |
| class ConditionComparisonColor : public ConditionComparison |
| { |
| public: |
| ConditionComparisonColor(ConditionComparandColor* left, |
| ConditionComparandColor* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonColor() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareColors(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandColor* m_leftComparand = nullptr; |
| ConditionComparandColor* m_rightComparand = nullptr; |
| }; |
| |
| class ConditionComparisonEnum : public ConditionComparison |
| { |
| public: |
| ConditionComparisonEnum(ConditionComparandUint32* left, |
| ConditionComparandUint32* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonEnum() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareUints32(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandUint32* m_leftComparand = nullptr; |
| ConditionComparandUint32* m_rightComparand = nullptr; |
| }; |
| |
| class ConditionComparisonUint32 : public ConditionComparison |
| { |
| public: |
| ConditionComparisonUint32(ConditionComparandUint32* left, |
| ConditionComparandUint32* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonUint32() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return compareUints32(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandUint32* m_leftComparand = nullptr; |
| ConditionComparandUint32* m_rightComparand = nullptr; |
| }; |
| |
| class ConditionComparisonViewModel : public ConditionComparison |
| { |
| public: |
| ConditionComparisonViewModel(ConditionComparandViewModel* left, |
| ConditionComparandViewModel* right, |
| ConditionOperation* operation) : |
| ConditionComparison(operation), |
| m_leftComparand(left), |
| m_rightComparand(right) |
| {} |
| ~ConditionComparisonViewModel() |
| { |
| delete m_leftComparand; |
| delete m_rightComparand; |
| } |
| bool compare(const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) override |
| { |
| return comparePointers(m_leftComparand->value(stateMachineInstance), |
| m_rightComparand->value(stateMachineInstance)); |
| } |
| |
| private: |
| ConditionComparandViewModel* m_leftComparand = nullptr; |
| ConditionComparandViewModel* m_rightComparand = nullptr; |
| }; |
| |
| namespace |
| { |
| |
| Core* resolveComponentTarget(const StateMachineInstance* smi, |
| const TransitionPropertyComponentComparator* comp) |
| { |
| if (smi == nullptr || comp == nullptr) |
| { |
| return nullptr; |
| } |
| Artboard* artboard = smi->artboard(); |
| if (artboard == nullptr) |
| { |
| return nullptr; |
| } |
| Core* target = artboard->resolve(comp->objectId()); |
| if (target == nullptr || |
| !CoreRegistry::objectSupportsProperty(target, comp->propertyKey())) |
| { |
| return nullptr; |
| } |
| return target; |
| } |
| |
| enum class ComponentComparandKind |
| { |
| NumberDouble, |
| NumberFromUint, |
| Boolean, |
| String, |
| Color, |
| Enum, |
| Trigger, |
| Asset, |
| Artboard, |
| ViewModel, |
| }; |
| |
| bool describeComponentSide(const TransitionPropertyComponentComparator* comp, |
| ComponentComparandKind* outKind) |
| { |
| int fieldId = CoreRegistry::propertyFieldId((int)comp->propertyKey()); |
| if (fieldId < 0) |
| { |
| return false; |
| } |
| const uint32_t pk = comp->propertyKey(); |
| switch (fieldId) |
| { |
| case CoreDoubleType::id: |
| *outKind = ComponentComparandKind::NumberDouble; |
| return true; |
| case CoreBoolType::id: |
| *outKind = ComponentComparandKind::Boolean; |
| return true; |
| case CoreStringType::id: |
| *outKind = ComponentComparandKind::String; |
| return true; |
| case CoreColorType::id: |
| *outKind = ComponentComparandKind::Color; |
| return true; |
| case CoreUintType::id: |
| if (pk == CustomPropertyEnumBase::propertyValuePropertyKey || |
| pk == ViewModelInstanceEnumBase::propertyValuePropertyKey) |
| { |
| *outKind = ComponentComparandKind::Enum; |
| return true; |
| } |
| if (pk == CustomPropertyTriggerBase::propertyValuePropertyKey || |
| pk == ViewModelInstanceTriggerBase::propertyValuePropertyKey) |
| { |
| *outKind = ComponentComparandKind::Trigger; |
| return true; |
| } |
| if (pk == ViewModelInstanceAssetBase::propertyValuePropertyKey) |
| { |
| *outKind = ComponentComparandKind::Asset; |
| return true; |
| } |
| if (pk == ViewModelInstanceArtboardBase::propertyValuePropertyKey) |
| { |
| *outKind = ComponentComparandKind::Artboard; |
| return true; |
| } |
| if (pk == ViewModelInstanceViewModelBase::propertyValuePropertyKey) |
| { |
| *outKind = ComponentComparandKind::ViewModel; |
| return true; |
| } |
| *outKind = ComponentComparandKind::NumberFromUint; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool componentKindsCompatible(ComponentComparandKind a, |
| ComponentComparandKind b) |
| { |
| auto isNumberKind = [](ComponentComparandKind k) { |
| return k == ComponentComparandKind::NumberDouble || |
| k == ComponentComparandKind::NumberFromUint; |
| }; |
| if (isNumberKind(a) && isNumberKind(b)) |
| { |
| return true; |
| } |
| return a == b; |
| } |
| |
| ConditionComparandNumber* makeComponentNumberComparand( |
| TransitionPropertyComponentComparator* comp, |
| ComponentComparandKind kind) |
| { |
| if (kind == ComponentComparandKind::NumberDouble) |
| { |
| return new ConditionComparandComponentCoreNumber(comp); |
| } |
| return new ConditionComparandComponentCoreUintAsNumber(comp); |
| } |
| |
| bool describeViewModelBindableKind(BindableProperty* bindable, |
| ComponentComparandKind* outKind) |
| { |
| if (bindable == nullptr) |
| { |
| return false; |
| } |
| switch (bindable->coreType()) |
| { |
| case BindablePropertyNumber::typeKey: |
| *outKind = ComponentComparandKind::NumberDouble; |
| return true; |
| case BindablePropertyInteger::typeKey: |
| *outKind = ComponentComparandKind::NumberFromUint; |
| return true; |
| case BindablePropertyBoolean::typeKey: |
| *outKind = ComponentComparandKind::Boolean; |
| return true; |
| case BindablePropertyString::typeKey: |
| *outKind = ComponentComparandKind::String; |
| return true; |
| case BindablePropertyColor::typeKey: |
| *outKind = ComponentComparandKind::Color; |
| return true; |
| case BindablePropertyEnum::typeKey: |
| *outKind = ComponentComparandKind::Enum; |
| return true; |
| case BindablePropertyTrigger::typeKey: |
| *outKind = ComponentComparandKind::Trigger; |
| return true; |
| case BindablePropertyAsset::typeKey: |
| *outKind = ComponentComparandKind::Asset; |
| return true; |
| case BindablePropertyArtboard::typeKey: |
| *outKind = ComponentComparandKind::Artboard; |
| return true; |
| case BindablePropertyViewModel::typeKey: |
| *outKind = ComponentComparandKind::ViewModel; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| enum class ComparatorSide |
| { |
| Left, |
| Right, |
| }; |
| |
| // Artboard properties and literal values only participate on the left / right |
| // respectively (matches historical initialize() support). |
| void appendComparableKinds(TransitionComparator* comparator, |
| ComparatorSide side, |
| std::vector<ComponentComparandKind>& out) |
| { |
| if (comparator->is<TransitionPropertyArtboardComparator>()) |
| { |
| if (side == ComparatorSide::Left) |
| { |
| out.push_back(ComponentComparandKind::NumberDouble); |
| } |
| return; |
| } |
| if (comparator->is<TransitionPropertyComponentComparator>()) |
| { |
| ComponentComparandKind k; |
| if (describeComponentSide( |
| comparator->as<TransitionPropertyComponentComparator>(), |
| &k)) |
| { |
| out.push_back(k); |
| } |
| return; |
| } |
| if (comparator->is<TransitionPropertyViewModelComparator>()) |
| { |
| ComponentComparandKind k; |
| if (describeViewModelBindableKind( |
| comparator->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(), |
| &k)) |
| { |
| out.push_back(k); |
| } |
| return; |
| } |
| if (side != ComparatorSide::Right) |
| { |
| return; |
| } |
| if (comparator->is<TransitionValueNumberComparator>()) |
| { |
| out.push_back(ComponentComparandKind::NumberDouble); |
| return; |
| } |
| if (comparator->is<TransitionValueBooleanComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Boolean); |
| return; |
| } |
| if (comparator->is<TransitionValueStringComparator>()) |
| { |
| out.push_back(ComponentComparandKind::String); |
| return; |
| } |
| if (comparator->is<TransitionValueColorComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Color); |
| return; |
| } |
| if (comparator->is<TransitionValueEnumComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Enum); |
| return; |
| } |
| if (comparator->is<TransitionValueAssetComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Asset); |
| return; |
| } |
| if (comparator->is<TransitionValueArtboardComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Artboard); |
| return; |
| } |
| // Uint32 comparison vs component triggers; VM trigger + value trigger uses |
| // ConditionComparisonSelf and is handled before kind intersection. |
| if (comparator->is<TransitionValueTriggerComparator>()) |
| { |
| out.push_back(ComponentComparandKind::Trigger); |
| return; |
| } |
| } |
| |
| // Picks the first (lk, rk) pair such that componentKindsCompatible(lk, rk), |
| // scanning leftKinds in order then rightKinds (deterministic tie-break). |
| bool intersectCompatibleKinds( |
| const std::vector<ComponentComparandKind>& leftKinds, |
| const std::vector<ComponentComparandKind>& rightKinds, |
| ComponentComparandKind* outLeftKind, |
| ComponentComparandKind* outRightKind) |
| { |
| for (ComponentComparandKind lk : leftKinds) |
| { |
| for (ComponentComparandKind rk : rightKinds) |
| { |
| if (componentKindsCompatible(lk, rk)) |
| { |
| *outLeftKind = lk; |
| *outRightKind = rk; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| enum class ComparisonShape |
| { |
| Number, |
| Boolean, |
| String, |
| Color, |
| Enum, |
| Uint32, |
| ViewModel, |
| }; |
| |
| // Maps compatible kinds to ConditionComparison wrapper. Numeric: any |
| // NumberDouble in the pair uses float compare (Number); both NumberFromUint |
| // uses exact uint32 compare (Uint32), including component+component generic |
| // uint fields and VM integer pairs. |
| bool resolveComparisonShape(ComponentComparandKind lk, |
| ComponentComparandKind rk, |
| ComparisonShape* outShape) |
| { |
| if (!componentKindsCompatible(lk, rk)) |
| { |
| return false; |
| } |
| auto isNumberKind = [](ComponentComparandKind k) { |
| return k == ComponentComparandKind::NumberDouble || |
| k == ComponentComparandKind::NumberFromUint; |
| }; |
| if (isNumberKind(lk) && isNumberKind(rk)) |
| { |
| if (lk == ComponentComparandKind::NumberFromUint && |
| rk == ComponentComparandKind::NumberFromUint) |
| { |
| *outShape = ComparisonShape::Uint32; |
| return true; |
| } |
| *outShape = ComparisonShape::Number; |
| return true; |
| } |
| if (lk != rk) |
| { |
| return false; |
| } |
| switch (lk) |
| { |
| case ComponentComparandKind::Boolean: |
| *outShape = ComparisonShape::Boolean; |
| return true; |
| case ComponentComparandKind::String: |
| *outShape = ComparisonShape::String; |
| return true; |
| case ComponentComparandKind::Color: |
| *outShape = ComparisonShape::Color; |
| return true; |
| case ComponentComparandKind::Enum: |
| *outShape = ComparisonShape::Enum; |
| return true; |
| case ComponentComparandKind::Trigger: |
| case ComponentComparandKind::Asset: |
| case ComponentComparandKind::Artboard: |
| *outShape = ComparisonShape::Uint32; |
| return true; |
| case ComponentComparandKind::ViewModel: |
| *outShape = ComparisonShape::ViewModel; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| struct ComparandSlot |
| { |
| ConditionComparandNumber* number = nullptr; |
| ConditionComparandBoolean* boolean = nullptr; |
| ConditionComparandString* string = nullptr; |
| ConditionComparandColor* color = nullptr; |
| ConditionComparandUint32* uint32 = nullptr; |
| ConditionComparandViewModel* viewModel = nullptr; |
| }; |
| |
| void clearComparandSlot(ComparandSlot& s) |
| { |
| delete s.number; |
| delete s.boolean; |
| delete s.string; |
| delete s.color; |
| delete s.uint32; |
| delete s.viewModel; |
| s = ComparandSlot{}; |
| } |
| |
| // Builds one comparand for `c` and kind `k` given the resolved comparison. |
| bool makeComparand(TransitionComparator* c, |
| ComponentComparandKind k, |
| ComparisonShape shape, |
| ComparandSlot* slot) |
| { |
| switch (shape) |
| { |
| case ComparisonShape::Number: |
| { |
| if (c->is<TransitionPropertyArtboardComparator>()) |
| { |
| slot->number = new ConditionComparandArtboardProperty( |
| c->as<TransitionPropertyArtboardComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr) |
| { |
| return false; |
| } |
| if (bp->is<BindablePropertyNumber>()) |
| { |
| slot->number = new ConditionComparandNumberBindable( |
| bp->as<BindablePropertyNumber>()); |
| return true; |
| } |
| if (bp->is<BindablePropertyInteger>()) |
| { |
| slot->number = new ConditionComparandNumberBindableInteger( |
| bp->as<BindablePropertyInteger>()); |
| return true; |
| } |
| return false; |
| } |
| if (c->is<TransitionValueNumberComparator>()) |
| { |
| slot->number = new ConditionComparandNumberValue( |
| c->as<TransitionValueNumberComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| auto* comp = c->as<TransitionPropertyComponentComparator>(); |
| if (k == ComponentComparandKind::NumberDouble) |
| { |
| slot->number = |
| new ConditionComparandComponentCoreNumber(comp); |
| return true; |
| } |
| if (k == ComponentComparandKind::NumberFromUint) |
| { |
| slot->number = makeComponentNumberComparand(comp, k); |
| return true; |
| } |
| return false; |
| } |
| return false; |
| } |
| case ComparisonShape::Boolean: |
| { |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr || !bp->is<BindablePropertyBoolean>()) |
| { |
| return false; |
| } |
| slot->boolean = new ConditionComparandBooleanBindable( |
| bp->as<BindablePropertyBoolean>()); |
| return true; |
| } |
| if (c->is<TransitionValueBooleanComparator>()) |
| { |
| slot->boolean = new ConditionComparandBooleanValue( |
| c->as<TransitionValueBooleanComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| slot->boolean = new ConditionComparandComponentCoreBoolean( |
| c->as<TransitionPropertyComponentComparator>()); |
| return true; |
| } |
| return false; |
| } |
| case ComparisonShape::String: |
| { |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr || !bp->is<BindablePropertyString>()) |
| { |
| return false; |
| } |
| slot->string = new ConditionComparandStringBindable( |
| bp->as<BindablePropertyString>()); |
| return true; |
| } |
| if (c->is<TransitionValueStringComparator>()) |
| { |
| slot->string = new ConditionComparandStringValue( |
| c->as<TransitionValueStringComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| slot->string = new ConditionComparandComponentCoreString( |
| c->as<TransitionPropertyComponentComparator>()); |
| return true; |
| } |
| return false; |
| } |
| case ComparisonShape::Color: |
| { |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr || !bp->is<BindablePropertyColor>()) |
| { |
| return false; |
| } |
| slot->color = new ConditionComparandColorBindable( |
| bp->as<BindablePropertyColor>()); |
| return true; |
| } |
| if (c->is<TransitionValueColorComparator>()) |
| { |
| slot->color = new ConditionComparandColorValue( |
| c->as<TransitionValueColorComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| slot->color = new ConditionComparandComponentCoreColor( |
| c->as<TransitionPropertyComponentComparator>()); |
| return true; |
| } |
| return false; |
| } |
| case ComparisonShape::Enum: |
| { |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr || !bp->is<BindablePropertyEnum>()) |
| { |
| return false; |
| } |
| slot->uint32 = new ConditionComparandEnumBindable( |
| bp->as<BindablePropertyEnum>()); |
| return true; |
| } |
| if (c->is<TransitionValueEnumComparator>()) |
| { |
| slot->uint32 = new ConditionComparandEnumValue( |
| c->as<TransitionValueEnumComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| slot->uint32 = new ConditionComparandComponentCoreUint( |
| c->as<TransitionPropertyComponentComparator>()); |
| return true; |
| } |
| return false; |
| } |
| case ComparisonShape::Uint32: |
| { |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr) |
| { |
| return false; |
| } |
| if (k == ComponentComparandKind::NumberFromUint && |
| bp->is<BindablePropertyInteger>()) |
| { |
| slot->uint32 = new ConditionComparandIntegerBindable( |
| bp->as<BindablePropertyInteger>()); |
| return true; |
| } |
| if (k == ComponentComparandKind::Trigger && |
| bp->is<BindablePropertyTrigger>()) |
| { |
| slot->uint32 = new ConditionComparandTriggerBindable( |
| bp->as<BindablePropertyTrigger>()); |
| return true; |
| } |
| if (k == ComponentComparandKind::Asset && |
| bp->is<BindablePropertyAsset>()) |
| { |
| slot->uint32 = new ConditionComparandAssetBindable( |
| bp->as<BindablePropertyAsset>()); |
| return true; |
| } |
| if (k == ComponentComparandKind::Artboard && |
| bp->is<BindablePropertyArtboard>()) |
| { |
| slot->uint32 = new ConditionComparandArtboardBindable( |
| bp->as<BindablePropertyArtboard>()); |
| return true; |
| } |
| return false; |
| } |
| if (c->is<TransitionValueTriggerComparator>()) |
| { |
| slot->uint32 = new ConditionComparandTriggerValue( |
| c->as<TransitionValueTriggerComparator>()); |
| return true; |
| } |
| if (c->is<TransitionValueAssetComparator>()) |
| { |
| slot->uint32 = new ConditionComparandAssetValue( |
| c->as<TransitionValueAssetComparator>()); |
| return true; |
| } |
| if (c->is<TransitionValueArtboardComparator>()) |
| { |
| slot->uint32 = new ConditionComparandArtboardValue( |
| c->as<TransitionValueArtboardComparator>()); |
| return true; |
| } |
| if (c->is<TransitionPropertyComponentComparator>()) |
| { |
| slot->uint32 = new ConditionComparandComponentCoreUint( |
| c->as<TransitionPropertyComponentComparator>()); |
| return true; |
| } |
| return false; |
| } |
| case ComparisonShape::ViewModel: |
| { |
| if (k != ComponentComparandKind::ViewModel) |
| { |
| return false; |
| } |
| if (c->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* bp = c->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (bp == nullptr || !bp->is<BindablePropertyViewModel>()) |
| { |
| return false; |
| } |
| slot->viewModel = new ConditionComparandViewModelBindable( |
| bp->as<BindablePropertyViewModel>()); |
| return true; |
| } |
| return false; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| ConditionComparison* wrapComparison(ComparisonShape shape, |
| ComparandSlot& leftSlot, |
| ComparandSlot& rightSlot, |
| ConditionOperation* op) |
| { |
| switch (shape) |
| { |
| case ComparisonShape::Number: |
| if (leftSlot.number == nullptr || rightSlot.number == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonNumber(leftSlot.number, |
| rightSlot.number, |
| op); |
| case ComparisonShape::Boolean: |
| if (leftSlot.boolean == nullptr || rightSlot.boolean == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonBoolean(leftSlot.boolean, |
| rightSlot.boolean, |
| op); |
| case ComparisonShape::String: |
| if (leftSlot.string == nullptr || rightSlot.string == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonString(leftSlot.string, |
| rightSlot.string, |
| op); |
| case ComparisonShape::Color: |
| if (leftSlot.color == nullptr || rightSlot.color == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonColor(leftSlot.color, |
| rightSlot.color, |
| op); |
| case ComparisonShape::Enum: |
| if (leftSlot.uint32 == nullptr || rightSlot.uint32 == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonEnum(leftSlot.uint32, |
| rightSlot.uint32, |
| op); |
| case ComparisonShape::Uint32: |
| if (leftSlot.uint32 == nullptr || rightSlot.uint32 == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonUint32(leftSlot.uint32, |
| rightSlot.uint32, |
| op); |
| case ComparisonShape::ViewModel: |
| if (leftSlot.viewModel == nullptr || rightSlot.viewModel == nullptr) |
| { |
| return nullptr; |
| } |
| return new ConditionComparisonViewModel(leftSlot.viewModel, |
| rightSlot.viewModel, |
| op); |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool buildComparandsFromIntersect(TransitionComparator* left, |
| TransitionComparator* right, |
| ComponentComparandKind lk, |
| ComponentComparandKind rk, |
| ConditionOperation* op, |
| ConditionComparison** outComparison) |
| { |
| ComparisonShape shape; |
| if (!resolveComparisonShape(lk, rk, &shape)) |
| { |
| return false; |
| } |
| ComparandSlot leftSlot, rightSlot; |
| if (!makeComparand(left, lk, shape, &leftSlot) || |
| !makeComparand(right, rk, shape, &rightSlot)) |
| { |
| clearComparandSlot(leftSlot); |
| clearComparandSlot(rightSlot); |
| return false; |
| } |
| ConditionComparison* wrapped = |
| wrapComparison(shape, leftSlot, rightSlot, op); |
| if (wrapped == nullptr) |
| { |
| clearComparandSlot(leftSlot); |
| clearComparandSlot(rightSlot); |
| return false; |
| } |
| *outComparison = wrapped; |
| return true; |
| } |
| |
| } // namespace |
| |
| TransitionViewModelCondition::~TransitionViewModelCondition() |
| { |
| if (m_leftComparator != nullptr) |
| { |
| delete m_leftComparator; |
| m_leftComparator = nullptr; |
| } |
| if (m_rightComparator != nullptr) |
| { |
| delete m_rightComparator; |
| m_rightComparator = nullptr; |
| } |
| if (m_comparison != nullptr) |
| { |
| delete m_comparison; |
| m_comparison = nullptr; |
| } |
| } |
| |
| bool TransitionViewModelCondition::canEvaluate( |
| const StateMachineInstance* stateMachineInstance) const |
| { |
| if (!leftComparator() || !rightComparator()) |
| { |
| return false; |
| } |
| if (!stateMachineInstance->dataContext() && |
| (rightComparator()->is<TransitionPropertyViewModelComparator>() || |
| leftComparator()->is<TransitionPropertyViewModelComparator>())) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| bool TransitionViewModelCondition::evaluate( |
| const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) const |
| { |
| if (canEvaluate(stateMachineInstance) && m_comparison != nullptr) |
| { |
| return m_comparison->compare(stateMachineInstance, layerInstance); |
| } |
| return false; |
| } |
| |
| void TransitionViewModelCondition::useInLayer( |
| const StateMachineInstance* stateMachineInstance, |
| StateMachineLayerInstance* layerInstance) const |
| { |
| if (leftComparator() != nullptr) |
| { |
| return leftComparator()->useInLayer(stateMachineInstance, |
| layerInstance); |
| } |
| } |
| |
| ConditionOperation* TransitionViewModelCondition::operation( |
| TransitionConditionOp op) |
| { |
| switch (op) |
| { |
| case TransitionConditionOp::equal: |
| return new ConditionOperationEqual(); |
| case TransitionConditionOp::notEqual: |
| return new ConditionOperationNotEqual(); |
| case TransitionConditionOp::lessThanOrEqual: |
| return new ConditionOperationLessThanOrEqual(); |
| case TransitionConditionOp::lessThan: |
| return new ConditionOperationLessThan(); |
| case TransitionConditionOp::greaterThanOrEqual: |
| return new ConditionOperationGreaterThanOrEqual(); |
| case TransitionConditionOp::greaterThan: |
| return new ConditionOperationGreaterThan(); |
| } |
| return new ConditionOperation(); |
| } |
| |
| void TransitionViewModelCondition::initialize() |
| { |
| TransitionComparator* left = leftComparator(); |
| TransitionComparator* right = rightComparator(); |
| if (left == nullptr || right == nullptr) |
| { |
| return; |
| } |
| |
| // Asymmetric: Self compares the left bindable against its data bind source, |
| // not a typed comparand intersection. |
| if (right->is<TransitionSelfComparator>()) |
| { |
| if (left->is<TransitionPropertyViewModelComparator>()) |
| { |
| auto* leftBindable = |
| left->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (leftBindable != nullptr) |
| { |
| m_comparison = new ConditionComparisonSelf(leftBindable); |
| return; |
| } |
| } |
| m_comparison = new ConditionComparisonNone(); |
| return; |
| } |
| |
| // Asymmetric: value trigger on the right uses ConditionComparisonSelf with |
| // a VM trigger on the left (not uint32 vs a value comparand). |
| if (left->is<TransitionPropertyViewModelComparator>() && |
| right->is<TransitionValueTriggerComparator>()) |
| { |
| auto* lbp = left->as<TransitionPropertyViewModelComparator>() |
| ->bindableProperty(); |
| if (lbp != nullptr && lbp->is<BindablePropertyTrigger>()) |
| { |
| m_comparison = new ConditionComparisonSelf(lbp); |
| return; |
| } |
| } |
| |
| std::vector<ComponentComparandKind> leftKinds; |
| std::vector<ComponentComparandKind> rightKinds; |
| appendComparableKinds(left, ComparatorSide::Left, leftKinds); |
| appendComparableKinds(right, ComparatorSide::Right, rightKinds); |
| |
| ComponentComparandKind lk{}; |
| ComponentComparandKind rk{}; |
| if (!intersectCompatibleKinds(leftKinds, rightKinds, &lk, &rk)) |
| { |
| m_comparison = new ConditionComparisonNone(); |
| return; |
| } |
| |
| ConditionOperation* condOp = operation(op()); |
| ConditionComparison* cmp = nullptr; |
| if (buildComparandsFromIntersect(left, right, lk, rk, condOp, &cmp)) |
| { |
| m_comparison = cmp; |
| return; |
| } |
| delete condOp; |
| m_comparison = new ConditionComparisonNone(); |
| } |
| |
| ConditionComparandComponentCoreNumber::ConditionComparandComponentCoreNumber( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| float ConditionComparandComponentCoreNumber::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return 0.0f; |
| } |
| return CoreRegistry::getDouble(target, (int)m_comparator->propertyKey()); |
| } |
| |
| ConditionComparandComponentCoreUintAsNumber:: |
| ConditionComparandComponentCoreUintAsNumber( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| float ConditionComparandComponentCoreUintAsNumber::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return 0.0f; |
| } |
| return (float)CoreRegistry::getUint(target, |
| (int)m_comparator->propertyKey()); |
| } |
| |
| ConditionComparandComponentCoreBoolean::ConditionComparandComponentCoreBoolean( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| bool ConditionComparandComponentCoreBoolean::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return false; |
| } |
| return CoreRegistry::getBool(target, (int)m_comparator->propertyKey()); |
| } |
| |
| ConditionComparandComponentCoreString::ConditionComparandComponentCoreString( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| std::string ConditionComparandComponentCoreString::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return std::string{}; |
| } |
| return CoreRegistry::getString(target, (int)m_comparator->propertyKey()); |
| } |
| |
| ConditionComparandComponentCoreColor::ConditionComparandComponentCoreColor( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| int ConditionComparandComponentCoreColor::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return 0; |
| } |
| return CoreRegistry::getColor(target, (int)m_comparator->propertyKey()); |
| } |
| |
| ConditionComparandComponentCoreUint::ConditionComparandComponentCoreUint( |
| TransitionPropertyComponentComparator* comparator) : |
| m_comparator(comparator) |
| {} |
| |
| uint32_t ConditionComparandComponentCoreUint::value( |
| const StateMachineInstance* stateMachineInstance) |
| { |
| Core* target = resolveComponentTarget(stateMachineInstance, m_comparator); |
| if (target == nullptr) |
| { |
| return 0; |
| } |
| return CoreRegistry::getUint(target, (int)m_comparator->propertyKey()); |
| } |
| |
| bool ConditionComparison::compareNumbers(float left, float right) |
| { |
| return m_operation->compareNumbers(left, right); |
| } |
| |
| bool ConditionComparison::compareStrings(const std::string& left, |
| const std::string& right) |
| { |
| return m_operation->compareStrings(left, right); |
| } |
| |
| bool ConditionComparison::compareBooleans(bool left, bool right) |
| { |
| return m_operation->compareBooleans(left, right); |
| } |
| |
| bool ConditionComparison::compareColors(int left, int right) |
| { |
| return m_operation->compareInts(left, right); |
| } |
| |
| bool ConditionComparison::compareUints32(uint32_t left, uint32_t right) |
| { |
| return m_operation->compareUints32(left, right); |
| } |
| |
| bool ConditionComparison::comparePointers(const void* left, const void* right) |
| { |
| return m_operation->compareBooleans(left == right, true); |
| } |