blob: 7e19f3edefe0dd4732415c35649c54043b38ca35 [file]
/*
* 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;
}