mirror of
https://github.com/rive-app/rive-flutter
synced 2025-07-05 21:55:58 +00:00
sort hit shapes when draw order changes and stop propagation on hit s…
sort hit shapes when draw order changes and stop propagation on hit success Diffs= 8bca56dca sort hit shapes when draw order changes and stop propagation on hit s… (#6624) 9d605a1fe Updating harfbuzz to 8.3.0 (#6652) 5cb42a9b0 Unity compute bounds (#6649) b765280df Fix path for downstream runtime. (#6645) 1cf6a65f1 Fix downstream cpp tests (#6643) a35883508 Single test script for windows and mac. (#6642) 37ce9aaea Fix tests to use harfbuzz renames. (#6641) 9338d6ec6 make a change to force a mono flush (#6638) 6059f744d update mono to keep details in commit and not pr (#6637) a394393a0 update mono scripts to be able to create "fixing" pr (#6636) Co-authored-by: hernan <hernan@rive.app>
This commit is contained in:
@ -1 +1 @@
|
||||
5e834adc22de1f8b1b445d79019b9b285a964913
|
||||
8bca56dcaffd0f563a91f628b0ed432eca71acb5
|
||||
|
@ -6,6 +6,7 @@ import 'package:rive/src/rive_core/animation/nested_bool.dart';
|
||||
import 'package:rive/src/rive_core/animation/nested_input.dart';
|
||||
import 'package:rive/src/rive_core/animation/nested_number.dart';
|
||||
import 'package:rive/src/rive_core/nested_artboard.dart';
|
||||
import 'package:rive/src/rive_core/state_machine_controller.dart';
|
||||
import 'package:rive_common/math.dart';
|
||||
|
||||
export 'package:rive/src/generated/animation/nested_state_machine_base.dart';
|
||||
@ -18,11 +19,13 @@ abstract class NestedStateMachineInstance {
|
||||
|
||||
bool hitTest(Vec2D position);
|
||||
|
||||
void pointerMove(Vec2D position);
|
||||
HitResult pointerMove(Vec2D position);
|
||||
|
||||
void pointerDown(Vec2D position, PointerDownEvent event);
|
||||
HitResult pointerDown(Vec2D position, PointerDownEvent event);
|
||||
|
||||
void pointerUp(Vec2D position);
|
||||
HitResult pointerUp(Vec2D position);
|
||||
|
||||
HitResult pointerExit(Vec2D position);
|
||||
|
||||
dynamic getInputValue(int id);
|
||||
void setInputValue(int id, dynamic value);
|
||||
@ -79,13 +82,17 @@ class NestedStateMachine extends NestedStateMachineBase {
|
||||
bool hitTest(Vec2D position) =>
|
||||
_stateMachineInstance?.hitTest(position) ?? false;
|
||||
|
||||
void pointerMove(Vec2D position) =>
|
||||
_stateMachineInstance?.pointerMove(position);
|
||||
HitResult pointerMove(Vec2D position) =>
|
||||
_stateMachineInstance?.pointerMove(position) ?? HitResult.none;
|
||||
|
||||
void pointerDown(Vec2D position, PointerDownEvent event) =>
|
||||
_stateMachineInstance?.pointerDown(position, event);
|
||||
HitResult pointerDown(Vec2D position, PointerDownEvent event) =>
|
||||
_stateMachineInstance?.pointerDown(position, event) ?? HitResult.none;
|
||||
|
||||
void pointerUp(Vec2D position) => _stateMachineInstance?.pointerUp(position);
|
||||
HitResult pointerUp(Vec2D position) =>
|
||||
_stateMachineInstance?.pointerUp(position) ?? HitResult.none;
|
||||
|
||||
HitResult pointerExit(Vec2D position) =>
|
||||
_stateMachineInstance?.pointerExit(position) ?? HitResult.none;
|
||||
|
||||
void _isActiveChanged() {
|
||||
// When a nested state machine re-activates (usually when an input changes)
|
||||
|
@ -44,6 +44,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
}
|
||||
|
||||
bool _frameOrigin = true;
|
||||
bool hasChangedDrawOrderInLastUpdate = false;
|
||||
|
||||
/// Returns true when the artboard will shift the origin from the top left to
|
||||
/// the relative width/height of the artboard itself. This is what the editor
|
||||
@ -238,6 +239,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
didUpdate = true;
|
||||
}
|
||||
}
|
||||
hasChangedDrawOrderInLastUpdate = false;
|
||||
|
||||
// Joysticks can be applied before updating components if none of the
|
||||
// joysticks have "external" control. If they are controlled/moved by some
|
||||
@ -443,7 +445,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
fill.draw(canvas, path);
|
||||
}
|
||||
|
||||
for (var drawable = _firstDrawable;
|
||||
for (var drawable = firstDrawable;
|
||||
drawable != null;
|
||||
drawable = drawable.prev) {
|
||||
if (drawable.isHidden || drawable.renderOpacity == 0) {
|
||||
@ -551,7 +553,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
@override
|
||||
Vec2D get worldTranslation => Vec2D();
|
||||
|
||||
Drawable? _firstDrawable;
|
||||
Drawable? firstDrawable;
|
||||
|
||||
void computeDrawOrder() {
|
||||
_drawables.clear();
|
||||
@ -589,12 +591,13 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
}
|
||||
|
||||
void sortDrawOrder() {
|
||||
hasChangedDrawOrderInLastUpdate = true;
|
||||
// Clear out rule first/last items.
|
||||
for (final rule in _sortedDrawRules) {
|
||||
rule.first = rule.last = null;
|
||||
}
|
||||
|
||||
_firstDrawable = null;
|
||||
firstDrawable = null;
|
||||
Drawable? lastDrawable;
|
||||
for (final drawable in _drawables) {
|
||||
var rules = drawable.flattenedDrawRules;
|
||||
@ -614,7 +617,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
drawable.prev = lastDrawable;
|
||||
drawable.next = null;
|
||||
if (lastDrawable == null) {
|
||||
lastDrawable = _firstDrawable = drawable;
|
||||
lastDrawable = firstDrawable = drawable;
|
||||
} else {
|
||||
lastDrawable.next = drawable;
|
||||
lastDrawable = drawable;
|
||||
@ -632,8 +635,8 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
rule.drawable!.prev?.next = rule.first;
|
||||
rule.first?.prev = rule.drawable!.prev;
|
||||
}
|
||||
if (rule.drawable == _firstDrawable) {
|
||||
_firstDrawable = rule.first;
|
||||
if (rule.drawable == firstDrawable) {
|
||||
firstDrawable = rule.first;
|
||||
}
|
||||
rule.drawable?.prev = rule.last;
|
||||
rule.last?.next = rule.drawable;
|
||||
@ -652,7 +655,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
}
|
||||
}
|
||||
|
||||
_firstDrawable = lastDrawable;
|
||||
firstDrawable = lastDrawable;
|
||||
}
|
||||
|
||||
// Make an instance of the artboard, clones internal objects and properties.
|
||||
|
@ -9,4 +9,8 @@ class ComponentFlags {
|
||||
/// Whether this Component is disconnected from the hierarchy meaning it won't
|
||||
/// receive any update cycles nor will any drawables draw.
|
||||
static const int disconnected = 1 << 2;
|
||||
|
||||
/// Whether this Component lets hit events pass through to components behind
|
||||
/// it (used by shapes at runtine)
|
||||
static const int opaque = 1 << 3;
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ abstract class Drawable extends DrawableBase {
|
||||
@override
|
||||
void blendModeValueChanged(int from, int to) {}
|
||||
|
||||
@override
|
||||
void isTargetOpaqueChanged(bool from, bool to) {}
|
||||
|
||||
List<ClippingShape> _clippingShapes = [];
|
||||
|
||||
bool clip(Canvas canvas) {
|
||||
@ -88,4 +91,8 @@ abstract class Drawable extends DrawableBase {
|
||||
bool get isHidden =>
|
||||
(drawableFlags & ComponentFlags.hidden) != 0 ||
|
||||
(dirt & ComponentDirt.collapsed) != 0;
|
||||
|
||||
bool get isTargetOpaque {
|
||||
return (drawableFlags & ComponentFlags.opaque) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ library rive_core;
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:rive/src/core/core.dart';
|
||||
@ -22,6 +23,8 @@ import 'package:rive/src/rive_core/animation/state_machine_listener.dart';
|
||||
import 'package:rive/src/rive_core/animation/state_machine_trigger.dart';
|
||||
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||
import 'package:rive/src/rive_core/artboard.dart';
|
||||
import 'package:rive/src/rive_core/component.dart';
|
||||
import 'package:rive/src/rive_core/drawable.dart';
|
||||
import 'package:rive/src/rive_core/event.dart';
|
||||
import 'package:rive/src/rive_core/nested_artboard.dart';
|
||||
import 'package:rive/src/rive_core/node.dart';
|
||||
@ -363,8 +366,7 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
onStateChange?.call(stateMachine.name, stateName);
|
||||
});
|
||||
|
||||
late List<_HitShape> hitShapes;
|
||||
late List<NestedArtboard> hitNestedArtboards;
|
||||
late List<_HitComponent> hitComponents = [];
|
||||
|
||||
Artboard? _artboard;
|
||||
|
||||
@ -407,7 +409,7 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
if (component is Shape) {
|
||||
var hitShape = hitShapeLookup[component];
|
||||
if (hitShape == null) {
|
||||
hitShapeLookup[component] = hitShape = _HitShape(component);
|
||||
hitShapeLookup[component] = hitShape = _HitShape(component, this);
|
||||
}
|
||||
hitShape.events.add(event);
|
||||
}
|
||||
@ -416,19 +418,18 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
});
|
||||
}
|
||||
}
|
||||
hitShapes = hitShapeLookup.values.toList();
|
||||
hitShapeLookup.values.toList().forEach(hitComponents.add);
|
||||
|
||||
_artboard = core as RuntimeArtboard;
|
||||
|
||||
List<NestedArtboard> nestedArtboards = [];
|
||||
if (_artboard != null) {
|
||||
for (final nestedArtboard in _artboard!.activeNestedArtboards) {
|
||||
if (nestedArtboard.hasNestedStateMachine) {
|
||||
nestedArtboards.add(nestedArtboard);
|
||||
hitComponents.add(_HitNestedArtboard(nestedArtboard, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
hitNestedArtboards = nestedArtboards;
|
||||
_sortHittableComponents();
|
||||
return super.init(core);
|
||||
}
|
||||
|
||||
@ -457,8 +458,40 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
}
|
||||
}
|
||||
|
||||
void _sortHittableComponents() {
|
||||
Drawable? firstDrawable = artboard?.firstDrawable;
|
||||
if (firstDrawable != null) {
|
||||
// walk to the end, so we can visit in reverse-order
|
||||
while (firstDrawable!.prev != null) {
|
||||
firstDrawable = firstDrawable.prev;
|
||||
}
|
||||
|
||||
int hitComponentsCount = hitComponents.length;
|
||||
int currentSortedIndex = 0;
|
||||
while (firstDrawable != null) {
|
||||
for (var i = currentSortedIndex; i < hitComponentsCount; i++) {
|
||||
if (hitComponents.elementAt(i).component == firstDrawable) {
|
||||
if (currentSortedIndex != i) {
|
||||
hitComponents.swap(i, currentSortedIndex);
|
||||
}
|
||||
currentSortedIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentSortedIndex == hitComponentsCount) {
|
||||
break;
|
||||
}
|
||||
firstDrawable = firstDrawable.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void apply(CoreContext core, double elapsedSeconds) {
|
||||
if (artboard?.hasChangedDrawOrderInLastUpdate ?? false) {
|
||||
_sortHittableComponents();
|
||||
}
|
||||
|
||||
bool keepGoing = false;
|
||||
for (final layerController in layerControllers) {
|
||||
if (layerController.apply(core, elapsedSeconds)) {
|
||||
@ -513,14 +546,14 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
}
|
||||
}
|
||||
|
||||
bool _processEvent(
|
||||
HitResult _processEvent(
|
||||
Vec2D position, {
|
||||
PointerEvent? pointerEvent,
|
||||
ListenerType? hitEvent,
|
||||
}) {
|
||||
var artboard = this.artboard;
|
||||
if (artboard == null) {
|
||||
return false;
|
||||
return HitResult.none;
|
||||
}
|
||||
if (artboard.frameOrigin) {
|
||||
// ignore: parameter_assignments
|
||||
@ -530,89 +563,25 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
artboard.height * artboard.originY,
|
||||
);
|
||||
}
|
||||
const hitRadius = 2;
|
||||
var hitArea = IAABB(
|
||||
(position.x - hitRadius).round(),
|
||||
(position.y - hitRadius).round(),
|
||||
(position.x + hitRadius).round(),
|
||||
(position.y + hitRadius).round(),
|
||||
);
|
||||
|
||||
bool hitSomething = false;
|
||||
for (final hitShape in hitShapes) {
|
||||
// for (final hitShape in event.shapes) {
|
||||
var shape = hitShape.shape;
|
||||
var bounds = shape.worldBounds;
|
||||
|
||||
// Quick reject
|
||||
bool isOver = false;
|
||||
if (bounds.contains(position)) {
|
||||
// Make hit tester.
|
||||
|
||||
var hitTester = TransformingHitTester(hitArea);
|
||||
shape.fillHitTester(hitTester);
|
||||
|
||||
// TODO: figure out where we get the fill rule. We could get it from
|
||||
// the Shape's first fill or do we want to store it on the event as a
|
||||
// user-selectable value in the inspector?
|
||||
|
||||
// Just use bounds for now
|
||||
isOver = hitTester.test();
|
||||
if (isOver) {
|
||||
hitSomething = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hoverChange = hitShape.isHovered != isOver;
|
||||
hitShape.isHovered = isOver;
|
||||
|
||||
// iterate all events associated with this hit shape
|
||||
for (final event in hitShape.events) {
|
||||
// Always update hover states regardless of which specific event type
|
||||
// we're trying to trigger.
|
||||
if (hoverChange) {
|
||||
if (isOver && event.listenerType == ListenerType.enter) {
|
||||
event.performChanges(this, position);
|
||||
isActive = true;
|
||||
} else if (!isOver && event.listenerType == ListenerType.exit) {
|
||||
event.performChanges(this, position);
|
||||
isActive = true;
|
||||
}
|
||||
}
|
||||
if (isOver && hitEvent == event.listenerType) {
|
||||
event.performChanges(this, position);
|
||||
isActive = true;
|
||||
bool hitOpaque = false;
|
||||
HitResult hitResult = HitResult.none;
|
||||
for (final hitComponent in hitComponents) {
|
||||
hitResult = hitComponent.processEvent(position,
|
||||
hitEvent: hitEvent, pointerEvent: pointerEvent, canHit: !hitOpaque);
|
||||
if (hitResult != HitResult.none) {
|
||||
hitSomething = true;
|
||||
if (hitResult == HitResult.hitOpaque) {
|
||||
hitOpaque = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final nestedArtboard in hitNestedArtboards) {
|
||||
if (nestedArtboard.isCollapsed) {
|
||||
continue;
|
||||
}
|
||||
var nestedPosition = nestedArtboard.worldToLocal(position);
|
||||
if (nestedPosition == null) {
|
||||
// Mounted artboard isn't ready or has a 0 scale transform.
|
||||
continue;
|
||||
}
|
||||
for (final nestedStateMachine
|
||||
in nestedArtboard.animations.whereType<NestedStateMachine>()) {
|
||||
switch (hitEvent) {
|
||||
case ListenerType.down:
|
||||
nestedStateMachine.pointerDown(
|
||||
nestedPosition,
|
||||
pointerEvent as PointerDownEvent,
|
||||
);
|
||||
break;
|
||||
case ListenerType.up:
|
||||
nestedStateMachine.pointerUp(nestedPosition);
|
||||
break;
|
||||
default:
|
||||
nestedStateMachine.pointerMove(nestedPosition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hitSomething;
|
||||
return hitSomething
|
||||
? hitOpaque
|
||||
? HitResult.hitOpaque
|
||||
: HitResult.hit
|
||||
: HitResult.none;
|
||||
}
|
||||
|
||||
/// Hit testing. If any listeners were hit, returns true.
|
||||
@ -633,78 +602,44 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
artboard.height * artboard.originY,
|
||||
);
|
||||
}
|
||||
const hitRadius = 2;
|
||||
var hitArea = IAABB(
|
||||
(position.x - hitRadius).round(),
|
||||
(position.y - hitRadius).round(),
|
||||
(position.x + hitRadius).round(),
|
||||
(position.y + hitRadius).round(),
|
||||
);
|
||||
|
||||
for (final hitShape in hitShapes) {
|
||||
var shape = hitShape.shape;
|
||||
var bounds = shape.worldBounds;
|
||||
|
||||
// Quick reject
|
||||
bool isOver = false;
|
||||
if (bounds.contains(position)) {
|
||||
// Make hit tester.
|
||||
var hitTester = TransformingHitTester(hitArea);
|
||||
shape.fillHitTester(hitTester);
|
||||
|
||||
isOver = hitTester.test();
|
||||
if (isOver) {
|
||||
return true; // exit early
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final nestedArtboard in hitNestedArtboards) {
|
||||
if (nestedArtboard.isCollapsed) {
|
||||
continue;
|
||||
}
|
||||
var nestedPosition = nestedArtboard.worldToLocal(position);
|
||||
if (nestedPosition == null) {
|
||||
// Mounted artboard isn't ready or has a 0 scale transform.
|
||||
continue;
|
||||
}
|
||||
for (final nestedStateMachine
|
||||
in nestedArtboard.animations.whereType<NestedStateMachine>()) {
|
||||
if (nestedStateMachine.hitTest(nestedPosition)) {
|
||||
return true; // exit early
|
||||
}
|
||||
for (final hitComponent in hitComponents) {
|
||||
if (hitComponent.hitTest(position)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // no hit targets found
|
||||
}
|
||||
|
||||
void pointerMove(Vec2D position) => _processEvent(
|
||||
HitResult pointerMove(Vec2D position) => _processEvent(
|
||||
position,
|
||||
hitEvent: ListenerType.move,
|
||||
);
|
||||
|
||||
void pointerDown(Vec2D position, PointerDownEvent event) {
|
||||
if (_processEvent(
|
||||
HitResult pointerDown(Vec2D position, PointerDownEvent event) {
|
||||
final hitResult = _processEvent(
|
||||
position,
|
||||
hitEvent: ListenerType.down,
|
||||
pointerEvent: event,
|
||||
)) {
|
||||
);
|
||||
if (hitResult != HitResult.none) {
|
||||
_recognizer.addPointer(event);
|
||||
}
|
||||
return hitResult;
|
||||
}
|
||||
|
||||
void pointerUp(Vec2D position) => _processEvent(
|
||||
HitResult pointerUp(Vec2D position) => _processEvent(
|
||||
position,
|
||||
hitEvent: ListenerType.up,
|
||||
);
|
||||
|
||||
void pointerExit(Vec2D position) => _processEvent(
|
||||
HitResult pointerExit(Vec2D position) => _processEvent(
|
||||
position,
|
||||
hitEvent: ListenerType.exit,
|
||||
);
|
||||
|
||||
void pointerEnter(Vec2D position) => _processEvent(
|
||||
HitResult pointerEnter(Vec2D position) => _processEvent(
|
||||
position,
|
||||
hitEvent: ListenerType.enter,
|
||||
);
|
||||
@ -725,14 +660,170 @@ class StateMachineController extends RiveAnimationController<CoreContext>
|
||||
}
|
||||
}
|
||||
|
||||
enum HitResult {
|
||||
none,
|
||||
hit,
|
||||
hitOpaque,
|
||||
}
|
||||
|
||||
class _HitComponent {
|
||||
final Component component;
|
||||
final StateMachineController controller;
|
||||
HitResult processEvent(
|
||||
Vec2D position, {
|
||||
PointerEvent? pointerEvent,
|
||||
ListenerType? hitEvent,
|
||||
bool canHit = true,
|
||||
}) {
|
||||
return HitResult.none;
|
||||
}
|
||||
|
||||
bool hitTest(Vec2D position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_HitComponent(this.component, this.controller);
|
||||
}
|
||||
|
||||
/// Representation of a Shape from the Artboard Instance and all the events it
|
||||
/// triggers. Allows tracking hover and performing hit detection only once on
|
||||
/// shapes that trigger multiple events.
|
||||
class _HitShape {
|
||||
Shape shape;
|
||||
class _HitShape extends _HitComponent {
|
||||
final Shape shape;
|
||||
double hitRadius = 2;
|
||||
bool isHovered = false;
|
||||
List<StateMachineListener> events = [];
|
||||
_HitShape(this.shape);
|
||||
|
||||
_HitShape(this.shape, StateMachineController controller)
|
||||
: super(shape, controller);
|
||||
|
||||
@override
|
||||
bool hitTest(Vec2D position) {
|
||||
var shape = component as Shape;
|
||||
var bounds = shape.worldBounds;
|
||||
|
||||
// Quick reject
|
||||
if (bounds.contains(position)) {
|
||||
var hitArea = IAABB(
|
||||
(position.x - hitRadius).round(),
|
||||
(position.y - hitRadius).round(),
|
||||
(position.x + hitRadius).round(),
|
||||
(position.y + hitRadius).round(),
|
||||
);
|
||||
// Make hit tester.
|
||||
var hitTester = TransformingHitTester(hitArea);
|
||||
shape.fillHitTester(hitTester);
|
||||
return hitTester.test(); // exit early
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
HitResult processEvent(
|
||||
Vec2D position, {
|
||||
PointerEvent? pointerEvent,
|
||||
ListenerType? hitEvent,
|
||||
bool canHit = true,
|
||||
}) {
|
||||
var shape = component as Shape;
|
||||
var isOver = false;
|
||||
if (canHit) {
|
||||
isOver = hitTest(position);
|
||||
}
|
||||
////
|
||||
bool hoverChange = isHovered != isOver;
|
||||
isHovered = isOver;
|
||||
|
||||
// iterate all events associated with this hit shape
|
||||
for (final event in events) {
|
||||
// Always update hover states regardless of which specific event type
|
||||
// we're trying to trigger.
|
||||
if (hoverChange) {
|
||||
if (isOver && event.listenerType == ListenerType.enter) {
|
||||
event.performChanges(controller, position);
|
||||
controller.isActive = true;
|
||||
} else if (!isOver && event.listenerType == ListenerType.exit) {
|
||||
event.performChanges(controller, position);
|
||||
controller.isActive = true;
|
||||
}
|
||||
}
|
||||
if (isOver && hitEvent == event.listenerType) {
|
||||
event.performChanges(controller, position);
|
||||
controller.isActive = true;
|
||||
}
|
||||
}
|
||||
////
|
||||
return isOver
|
||||
? shape.isTargetOpaque
|
||||
? HitResult.hitOpaque
|
||||
: HitResult.hit
|
||||
: HitResult.none;
|
||||
}
|
||||
}
|
||||
|
||||
class _HitNestedArtboard extends _HitComponent {
|
||||
final NestedArtboard nestedArtboard;
|
||||
_HitNestedArtboard(this.nestedArtboard, StateMachineController controller)
|
||||
: super(nestedArtboard, controller);
|
||||
|
||||
@override
|
||||
bool hitTest(Vec2D position) {
|
||||
var nestedPosition = nestedArtboard.worldToLocal(position);
|
||||
if (nestedArtboard.isCollapsed) {
|
||||
return false;
|
||||
}
|
||||
if (nestedPosition == null) {
|
||||
// Mounted artboard isn't ready or has a 0 scale transform.
|
||||
return false;
|
||||
}
|
||||
for (final nestedStateMachine
|
||||
in nestedArtboard.animations.whereType<NestedStateMachine>()) {
|
||||
if (nestedStateMachine.hitTest(nestedPosition)) {
|
||||
return true; // exit early
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
HitResult processEvent(
|
||||
Vec2D position, {
|
||||
PointerEvent? pointerEvent,
|
||||
ListenerType? hitEvent,
|
||||
bool canHit = true,
|
||||
}) {
|
||||
HitResult hitResult = HitResult.none;
|
||||
if (nestedArtboard.isCollapsed) {
|
||||
return hitResult;
|
||||
}
|
||||
var nestedPosition = nestedArtboard.worldToLocal(position);
|
||||
if (nestedPosition == null) {
|
||||
// Mounted artboard isn't ready or has a 0 scale transform.
|
||||
return hitResult;
|
||||
}
|
||||
for (final nestedStateMachine
|
||||
in nestedArtboard.animations.whereType<NestedStateMachine>()) {
|
||||
if (canHit) {
|
||||
switch (hitEvent) {
|
||||
case ListenerType.down:
|
||||
hitResult = nestedStateMachine.pointerDown(
|
||||
nestedPosition,
|
||||
pointerEvent as PointerDownEvent,
|
||||
);
|
||||
break;
|
||||
case ListenerType.up:
|
||||
hitResult = nestedStateMachine.pointerUp(nestedPosition);
|
||||
break;
|
||||
default:
|
||||
hitResult = nestedStateMachine.pointerMove(nestedPosition);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
nestedStateMachine.pointerExit(nestedPosition);
|
||||
}
|
||||
}
|
||||
return hitResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// This allows a value of type T or T?
|
||||
|
@ -6,6 +6,8 @@ import 'package:rive/src/rive_core/animation/nested_linear_animation.dart';
|
||||
import 'package:rive/src/rive_core/animation/nested_state_machine.dart';
|
||||
import 'package:rive/src/rive_core/artboard.dart';
|
||||
import 'package:rive/src/rive_core/nested_artboard.dart';
|
||||
import 'package:rive/src/rive_core/state_machine_controller.dart'
|
||||
as state_machine_core;
|
||||
import 'package:rive/src/runtime_mounted_artboard.dart';
|
||||
import 'package:rive_common/math.dart';
|
||||
|
||||
@ -123,15 +125,24 @@ class RuntimeNestedStateMachineInstance extends NestedStateMachineInstance {
|
||||
bool hitTest(Vec2D position) => stateMachineController.hitTest(position);
|
||||
|
||||
@override
|
||||
void pointerDown(Vec2D position, PointerDownEvent event) =>
|
||||
stateMachineController.pointerDown(position, event);
|
||||
state_machine_core.HitResult pointerDown(
|
||||
Vec2D position, PointerDownEvent event) {
|
||||
final result = stateMachineController.pointerDown(position, event);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
void pointerMove(Vec2D position) =>
|
||||
state_machine_core.HitResult pointerMove(Vec2D position) =>
|
||||
stateMachineController.pointerMove(position);
|
||||
|
||||
@override
|
||||
void pointerUp(Vec2D position) => stateMachineController.pointerUp(position);
|
||||
state_machine_core.HitResult pointerUp(Vec2D position) =>
|
||||
stateMachineController.pointerUp(position);
|
||||
|
||||
@override
|
||||
state_machine_core.HitResult pointerExit(Vec2D position) =>
|
||||
stateMachineController.pointerExit(position);
|
||||
|
||||
@override
|
||||
dynamic getInputValue(int id) => stateMachineController.getInputValue(id);
|
||||
|
@ -320,8 +320,7 @@ class RiveAnimationState extends State<RiveAnimation> {
|
||||
bool get _shouldAddHitTesting => _artboard!.animationControllers.any(
|
||||
(controller) =>
|
||||
controller is StateMachineController &&
|
||||
(controller.hitShapes.isNotEmpty ||
|
||||
controller.hitNestedArtboards.isNotEmpty),
|
||||
controller.hitComponents.isNotEmpty,
|
||||
);
|
||||
|
||||
@override
|
||||
|
Reference in New Issue
Block a user