| /* | |
| * Copyright 2024 Rive | |
| */ | |
| #include "rive/animation/transition_focus_condition.hpp" | |
| #include "rive/animation/state_machine_instance.hpp" | |
| #include "rive/animation/transition_condition_op.hpp" | |
| #include "rive/animation/transition_property_component_comparator.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/focus_data.hpp" | |
| #include "rive/input/focus_manager.hpp" | |
| #include "rive/node.hpp" | |
| using namespace rive; | |
| bool TransitionFocusCondition::evaluate( | |
| const StateMachineInstance* stateMachineInstance, | |
| StateMachineLayerInstance* layerInstance) const | |
| { | |
| (void)layerInstance; | |
| if (stateMachineInstance == nullptr) | |
| { | |
| return false; | |
| } | |
| // The target focusable component is stored on a component comparator. In | |
| // the editor it lives on the right (the left side is the implicit "current | |
| // focus"), but on runtime import the single exported comparator is assigned | |
| // to the left slot, so accept it from either side. | |
| TransitionComparator* comparator = rightComparator(); | |
| if (comparator == nullptr || | |
| !comparator->is<TransitionPropertyComponentComparator>()) | |
| { | |
| comparator = leftComparator(); | |
| } | |
| if (comparator == nullptr || | |
| !comparator->is<TransitionPropertyComponentComparator>()) | |
| { | |
| return false; | |
| } | |
| auto focusComparator = | |
| comparator->as<TransitionPropertyComponentComparator>(); | |
| const FocusManager* manager = stateMachineInstance->focusManager(); | |
| if (manager == nullptr) | |
| { | |
| return false; | |
| } | |
| auto target = | |
| stateMachineInstance->artboard()->resolve(focusComparator->objectId()); | |
| bool focused = false; | |
| if (target != nullptr && target->is<Node>()) | |
| { | |
| auto node = target->as<Node>(); | |
| for (auto child : node->children()) | |
| { | |
| if (child->is<FocusData>()) | |
| { | |
| auto focusNode = child->as<FocusData>()->focusNode(); | |
| // Use hasFocus (not hasPrimaryFocus): the target is considered | |
| // focused when it or any descendant is the primary focus, so a | |
| // scope/container counts as focused while a child leaf holds | |
| // focus. | |
| focused = focusNode != nullptr && manager->hasFocus(focusNode); | |
| break; | |
| } | |
| } | |
| } | |
| return op() == TransitionConditionOp::equal ? focused : !focused; | |
| } |