blob: 563e31c427ea7606f02c89f52bc4c148b94dce0a [file]
#ifndef _RIVE_FOCUS_DATA_HPP_
#define _RIVE_FOCUS_DATA_HPP_
#include "rive/animation/listener_invocation.hpp"
#include "rive/component_dirt.hpp"
#include "rive/generated/focus_data_base.hpp"
#include "rive/input/focus_node.hpp"
#include "rive/input/gamepad_listener.hpp"
#include "rive/input/keyboard_listener.hpp"
#include "rive/input/focusable.hpp"
#include "rive/lazy_vector.hpp"
#include "rive/math/aabb.hpp"
#include "rive/refcnt.hpp"
#include <vector>
namespace rive
{
class FocusListener;
class FocusData : public FocusDataBase, public Focusable
{
public:
~FocusData() override;
rcp<FocusNode> focusNode();
void detachFocusable()
{
if (m_focusNode != nullptr)
{
m_focusNode->clearFocusable();
}
}
/// Register a listener to be notified of focus/blur events.
void addFocusListener(FocusListener* listener);
/// Unregister a listener.
void removeFocusListener(FocusListener* listener);
/// Register a listener to be notified of key input events.
void addKeyboardListener(KeyboardListener* listener);
/// Unregister a keyboard listener.
void removeKeyboardListener(KeyboardListener* listener);
/// Register a listener for the capture phase of key events, which runs
/// before the bubble and outermost first. Flags this node's FocusNode so
/// FocusManager::keyInput knows to run that phase at all.
void addCaptureKeyboardListener(KeyboardListener* listener);
/// Unregister a capture-phase keyboard listener.
void removeCaptureKeyboardListener(KeyboardListener* listener);
/// Register a listener to be notified of text input events.
void addTextInputListener(KeyboardListener* listener);
/// Unregister a text input listener.
void removeTextInputListener(KeyboardListener* listener);
void addGamepadListener(GamepadListener* listener);
void removeGamepadListener(GamepadListener* listener);
/// Programmatically focus this node.
void focus();
/// Find the parent FocusData by walking up the component hierarchy.
/// Returns nullptr if no parent FocusData exists.
FocusData* findParentFocusData() const;
/// Find the closest FocusNode for a component by walking up the hierarchy.
/// Checks parent nodes for FocusData children, and crosses artboard
/// boundaries using externalParentFocusNode.
/// @param component The component to start searching from
/// @return The closest FocusNode, or nullptr if none found
static rcp<FocusNode> findClosestFocusNode(Component* component);
/// True if this focus target should participate in tab/spatial navigation
/// and pointer focus (not under a collapsed/hidden/opacity-zero branch,
/// including nested artboard hosts). Non-FocusData focusables are treated
/// as visible by FocusManager.
bool isEligibleForFocusTraversal() const override;
// Focusable interface implementation
/// The bubble. A TextInput parent edits first, once, when any keyboard
/// listener opted it in; then every normal listener is offered the key.
bool keyInput(
Key value,
KeyModifiers modifiers,
bool isPressed,
bool isRepeat) override;
/// The capture phase: every capture listener is offered the key, as
/// peers, and any one of them claiming it claims it for the node.
bool captureKeyInput(
Key value,
KeyModifiers modifiers,
bool isPressed,
bool isRepeat) override;
bool textInput(const std::string& text) override;
/// Forwards to the parent TextInput's selection (mirroring how
/// key/text input special-case a TextInput parent).
bool selectedText(std::string& outText) const override;
/// True when the parent is a TextInput (same special case).
bool acceptsTextInput() const override;
/// Forwards to the parent TextInput (same special case): it asks to blur
/// on an outside press and owns the hit test for it.
bool blursOnOutsidePress() const override;
bool hitTestFocusable(const Vec2D& rootPosition) override;
bool gamepadDispatch(
const ListenerInvocation& invocation,
ScriptedDrawable** outDispatchedScriptedDrawable = nullptr) override;
void focused() override;
void blurred() override;
bool worldPosition(Vec2D& outPosition) override;
/// Root-space bounds computed at call time; the FocusNode's cached bounds
/// go stale when an ancestor host moves this artboard instance.
bool worldBounds(AABB& outBounds) override;
Artboard* focusableArtboard() const override { return artboard(); }
bool acceptsKeyboardInput() const override
{
return !m_keyboardListeners.empty() ||
!m_captureKeyboardListeners.empty() || !m_textInputListeners.empty();
}
// Component overrides for update cycle
void buildDependencies() override;
void update(ComponentDirt value) override;
protected:
void focusFlagsChanged() override;
void edgeBehaviorValueChanged() override;
void nameChanged() override;
private:
void updateWorldBounds();
void scrollIntoView();
void scrollConstraintToShowBounds(
class ScrollConstraint* constraint,
const AABB& elementBounds);
rcp<FocusNode> m_focusNode;
// Lazy: most FocusData have no listeners of a given kind (text input and
// gamepad ones are rare, capture ones rarer), and a FocusData exists per
// artboard instance, so per list row.
LazyVector<FocusListener*> m_focusListeners;
LazyVector<KeyboardListener*> m_keyboardListeners;
LazyVector<KeyboardListener*> m_captureKeyboardListeners;
LazyVector<KeyboardListener*> m_textInputListeners;
LazyVector<GamepadListener*> m_gamepadListeners;
};
} // namespace rive
#endif