mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-05-17 21:36:06 +08:00
Solos
So far: - [x] Editor - [x] Dart Tests - [x] Flutter Runtime - [x] C++ Runtime - [x] C++ Tests Hierarchy interaction: <img width="280" alt="CleanShot 2023-03-24 at 21 44 53@2x" src="https://user-images.githubusercontent.com/454182/227696646-09f9cbe5-c482-4bab-aae9-b0b36c16047e.png"> Nesting: <img width="394" alt="CleanShot 2023-03-24 at 21 45 23@2x" src="https://user-images.githubusercontent.com/454182/227696653-b1132ba1-5471-4c6d-9b59-20387389ae12.png"> Inspector active solo selection: <img width="253" alt="CleanShot 2023-03-24 at 21 45 33@2x" src="https://user-images.githubusercontent.com/454182/227696660-6676acfa-15ab-4ae2-a866-4b7898bc1f52.png"> Animatable with timeline hierarchy value too: <img width="510" alt="CleanShot 2023-03-24 at 21 46 07@2x" src="https://user-images.githubusercontent.com/454182/227696686-255064c9-43fd-4213-9e3f-9cd46cca9de3.png"> Diffs= daaf140ba Solos (#5047) 44ef23f7e Text fix variable and opacity (#5017) 3c396d3b8 Text style variation at runtime. (#5014)
This commit is contained in:
@ -1 +1 @@
|
||||
41b699beb873be31f4d38ba79bd613cc83f74eca
|
||||
daaf140ba0a062ba9ec7f4eafa1e198d9121b220
|
||||
|
@ -1,4 +1,10 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
analyzer:
|
||||
exclude:
|
||||
- windows/**
|
||||
- ios/**
|
||||
- macos/**
|
||||
- web/**
|
||||
|
||||
linter:
|
||||
rules:
|
||||
|
@ -123,6 +123,7 @@ import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||
import 'package:rive/src/rive_core/shapes/star.dart';
|
||||
import 'package:rive/src/rive_core/shapes/straight_vertex.dart';
|
||||
import 'package:rive/src/rive_core/shapes/triangle.dart';
|
||||
import 'package:rive/src/rive_core/solo.dart';
|
||||
import 'package:rive/src/rive_core/text/text.dart';
|
||||
import 'package:rive/src/rive_core/text/text_style.dart';
|
||||
import 'package:rive/src/rive_core/text/text_style_axis.dart';
|
||||
@ -152,6 +153,8 @@ class RiveCoreContext {
|
||||
return Node();
|
||||
case NestedArtboardBase.typeKey:
|
||||
return NestedArtboard();
|
||||
case SoloBase.typeKey:
|
||||
return Solo();
|
||||
case AnimationBase.typeKey:
|
||||
return Animation();
|
||||
case LinearAnimationBase.typeKey:
|
||||
@ -508,6 +511,11 @@ class RiveCoreContext {
|
||||
object.animationId = value;
|
||||
}
|
||||
break;
|
||||
case SoloBase.activeComponentIdPropertyKey:
|
||||
if (object is SoloBase && value is int) {
|
||||
object.activeComponentId = value;
|
||||
}
|
||||
break;
|
||||
case AnimationBase.namePropertyKey:
|
||||
if (object is AnimationBase && value is String) {
|
||||
object.name = value;
|
||||
@ -1300,6 +1308,7 @@ class RiveCoreContext {
|
||||
case DrawableBase.drawableFlagsPropertyKey:
|
||||
case NestedArtboardBase.artboardIdPropertyKey:
|
||||
case NestedAnimationBase.animationIdPropertyKey:
|
||||
case SoloBase.activeComponentIdPropertyKey:
|
||||
case LinearAnimationBase.fpsPropertyKey:
|
||||
case LinearAnimationBase.durationPropertyKey:
|
||||
case LinearAnimationBase.loopValuePropertyKey:
|
||||
@ -1527,6 +1536,8 @@ class RiveCoreContext {
|
||||
return (object as NestedArtboardBase).artboardId;
|
||||
case NestedAnimationBase.animationIdPropertyKey:
|
||||
return (object as NestedAnimationBase).animationId;
|
||||
case SoloBase.activeComponentIdPropertyKey:
|
||||
return (object as SoloBase).activeComponentId;
|
||||
case LinearAnimationBase.fpsPropertyKey:
|
||||
return (object as LinearAnimationBase).fps;
|
||||
case LinearAnimationBase.durationPropertyKey:
|
||||
@ -2000,6 +2011,11 @@ class RiveCoreContext {
|
||||
object.animationId = value;
|
||||
}
|
||||
break;
|
||||
case SoloBase.activeComponentIdPropertyKey:
|
||||
if (object is SoloBase) {
|
||||
object.activeComponentId = value;
|
||||
}
|
||||
break;
|
||||
case LinearAnimationBase.fpsPropertyKey:
|
||||
if (object is LinearAnimationBase) {
|
||||
object.fps = value;
|
||||
|
54
lib/src/generated/solo_base.dart
Normal file
54
lib/src/generated/solo_base.dart
Normal file
@ -0,0 +1,54 @@
|
||||
/// Core automatically generated lib/src/generated/solo_base.dart.
|
||||
/// Do not modify manually.
|
||||
|
||||
import 'package:rive/src/generated/component_base.dart';
|
||||
import 'package:rive/src/generated/container_component_base.dart';
|
||||
import 'package:rive/src/generated/transform_component_base.dart';
|
||||
import 'package:rive/src/generated/world_transform_component_base.dart';
|
||||
import 'package:rive/src/rive_core/node.dart';
|
||||
|
||||
abstract class SoloBase extends Node {
|
||||
static const int typeKey = 147;
|
||||
@override
|
||||
int get coreType => SoloBase.typeKey;
|
||||
@override
|
||||
Set<int> get coreTypes => {
|
||||
SoloBase.typeKey,
|
||||
NodeBase.typeKey,
|
||||
TransformComponentBase.typeKey,
|
||||
WorldTransformComponentBase.typeKey,
|
||||
ContainerComponentBase.typeKey,
|
||||
ComponentBase.typeKey
|
||||
};
|
||||
|
||||
/// --------------------------------------------------------------------------
|
||||
/// ActiveComponentId field with key 296.
|
||||
static const int activeComponentIdInitialValue = 0;
|
||||
int _activeComponentId = activeComponentIdInitialValue;
|
||||
static const int activeComponentIdPropertyKey = 296;
|
||||
|
||||
/// Identifier of the active child in the solo set.
|
||||
int get activeComponentId => _activeComponentId;
|
||||
|
||||
/// Change the [_activeComponentId] field value.
|
||||
/// [activeComponentIdChanged] will be invoked only if the field's value has
|
||||
/// changed.
|
||||
set activeComponentId(int value) {
|
||||
if (_activeComponentId == value) {
|
||||
return;
|
||||
}
|
||||
int from = _activeComponentId;
|
||||
_activeComponentId = value;
|
||||
if (hasValidated) {
|
||||
activeComponentIdChanged(from, value);
|
||||
}
|
||||
}
|
||||
|
||||
void activeComponentIdChanged(int from, int to);
|
||||
|
||||
@override
|
||||
void copy(covariant SoloBase source) {
|
||||
super.copy(source);
|
||||
_activeComponentId = source._activeComponentId;
|
||||
}
|
||||
}
|
@ -138,10 +138,12 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
Component component = _dependencyOrder[i];
|
||||
_dirtDepth = i;
|
||||
int d = component.dirt;
|
||||
if (d == 0) {
|
||||
|
||||
if (d == 0 || (d & ComponentDirt.collapsed) != 0) {
|
||||
continue;
|
||||
}
|
||||
component.dirt = 0;
|
||||
|
||||
component.dirt &= ComponentDirt.collapsed;
|
||||
component.update(d);
|
||||
if (_dirtDepth < i) {
|
||||
break;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:rive/src/generated/component_base.dart';
|
||||
import 'package:rive/src/rive_core/artboard.dart';
|
||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||
import 'package:rive/src/rive_core/container_component.dart';
|
||||
import 'package:rive_common/utilities.dart';
|
||||
|
||||
@ -10,6 +11,23 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
||||
Artboard? _artboard;
|
||||
dynamic _userData;
|
||||
|
||||
/// Whether this Component's update processes at all.
|
||||
bool get isCollapsed => (dirt & ComponentDirt.collapsed) != 0;
|
||||
|
||||
bool propagateCollapse(bool collapse) {
|
||||
if (isCollapsed == collapse) {
|
||||
return false;
|
||||
}
|
||||
if (collapse) {
|
||||
dirt |= ComponentDirt.collapsed;
|
||||
} else {
|
||||
dirt &= ~ComponentDirt.collapsed;
|
||||
}
|
||||
onDirty(dirt);
|
||||
artboard?.onComponentDirty(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Override to true if you want some object inheriting from Component to not
|
||||
/// have a parent. Most objects will validate that they have a parent during
|
||||
/// the onAdded callback otherwise they are considered invalid and are culled
|
||||
@ -18,7 +36,7 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
||||
|
||||
// Used during update process.
|
||||
int graphOrder = 0;
|
||||
int dirt = 0xFFFF;
|
||||
int dirt = ComponentDirt.filthy;
|
||||
|
||||
// This is really only for sanity and earlying out of recursive loops.
|
||||
static const int maxTreeDepth = 5000;
|
||||
@ -112,6 +130,7 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
||||
if (_parent == value) {
|
||||
return;
|
||||
}
|
||||
dirt &= ~ComponentDirt.collapsed;
|
||||
var old = _parent;
|
||||
_parent = value;
|
||||
parentId = value?.id ?? Core.missingId;
|
||||
|
@ -1,44 +1,50 @@
|
||||
class ComponentDirt {
|
||||
static const int dependents = 1 << 0;
|
||||
/// Means to not process the update!
|
||||
static const int collapsed = 1 << 0;
|
||||
|
||||
static const int dependents = 1 << 1;
|
||||
|
||||
/// General flag for components are dirty (if this is up, the update cycle
|
||||
/// runs). It gets automatically applied with any other dirt.
|
||||
static const int components = 1 << 1;
|
||||
static const int components = 1 << 2;
|
||||
|
||||
/// Draw order needs to be re-computed.
|
||||
static const int drawOrder = 1 << 2;
|
||||
static const int drawOrder = 1 << 3;
|
||||
|
||||
/// Draw order needs to be re-computed.
|
||||
static const int naturalDrawOrder = 1 << 3;
|
||||
static const int naturalDrawOrder = 1 << 4;
|
||||
|
||||
/// Path is dirty and needs to be rebuilt.
|
||||
static const int path = 1 << 4;
|
||||
static const int path = 1 << 5;
|
||||
|
||||
/// Text shape is dirty, the shaper needs to re-run.
|
||||
static const int textShape = 1 << 4;
|
||||
static const int textShape = 1 << 5;
|
||||
|
||||
/// Vertices have changed, re-order cached lists.
|
||||
static const int vertices = 1 << 5;
|
||||
static const int vertices = 1 << 6;
|
||||
|
||||
/// Used by any component that needs to recompute their local transform.
|
||||
/// Usually components that have their transform dirty will also have their
|
||||
/// worldTransform dirty.
|
||||
static const int transform = 1 << 6;
|
||||
static const int transform = 1 << 7;
|
||||
|
||||
/// Used by any component that needs to update its world transform.
|
||||
static const int worldTransform = 1 << 7;
|
||||
static const int worldTransform = 1 << 8;
|
||||
|
||||
/// Dirt used to mark some stored paint needs to be rebuilt or that we just
|
||||
/// want to trigger an update cycle so painting occurs.
|
||||
static const int paint = 1 << 8;
|
||||
static const int paint = 1 << 9;
|
||||
|
||||
/// Used by the gradients track when the stops need to be re-ordered.
|
||||
static const int stops = 1 << 9;
|
||||
static const int stops = 1 << 10;
|
||||
|
||||
/// Used by ClippingShape to help Shape know when to recalculate its list of
|
||||
/// clipping sources.
|
||||
static const int clip = 1 << 10;
|
||||
static const int clip = 1 << 11;
|
||||
|
||||
/// Set when blend modes need to be updated.
|
||||
static const int blendMode = 1 << 11;
|
||||
static const int blendMode = 1 << 12;
|
||||
|
||||
/// All dirty. Every flag (apart from Collapsed) is set.
|
||||
static const int filthy = 0xFFFE;
|
||||
}
|
||||
|
@ -3,47 +3,10 @@ class ComponentFlags {
|
||||
/// drawables and paths).
|
||||
static const int hidden = 1 << 0;
|
||||
|
||||
// Whether the component was locked for editing in the editor.
|
||||
/// Whether the component was locked for editing in the editor.
|
||||
static const int locked = 1 << 1;
|
||||
|
||||
/// These options are used by [TransformComponentConstraint]s, any other
|
||||
/// component that's not in that class hierarchy can re-use these values as
|
||||
/// they won't collide.
|
||||
///
|
||||
/// -----------
|
||||
|
||||
/// Whether the [TransformComponentConstraint]'s constrained value is offset
|
||||
/// from the design time value (in the matching space).
|
||||
static const int offset = 1 << 2;
|
||||
|
||||
/// Whether the transform component is copied.
|
||||
static const int copy = 1 << 3;
|
||||
|
||||
/// Set when a minimum value should be applied to the constrained value.
|
||||
static const int min = 1 << 4;
|
||||
|
||||
/// Set when a maximum value should be applied to the constrained value.
|
||||
static const int max = 1 << 5;
|
||||
|
||||
/// Whether an X transform component is copied.
|
||||
static const int copyX = 1 << 3;
|
||||
|
||||
/// Set when a minimum value should be applied to the constrained X value.
|
||||
static const int minX = 1 << 4;
|
||||
|
||||
/// Set when a maximum value should be applied to the constrained X value.
|
||||
static const int maxX = 1 << 5;
|
||||
|
||||
/// Whether a Y transform component is copied.
|
||||
static const int copyY = 1 << 6;
|
||||
|
||||
/// Set when a minimum value should be applied to the constrained Y value.
|
||||
static const int minY = 1 << 7;
|
||||
|
||||
/// Set when a maximum value should be applied to the constrained Y value.
|
||||
static const int maxY = 1 << 8;
|
||||
|
||||
/// End of TransformComponentConstraint
|
||||
///
|
||||
/// ----------
|
||||
/// 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;
|
||||
}
|
||||
|
@ -22,7 +22,9 @@ abstract class ContainerComponent extends ContainerComponentBase {
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
void childAdded(Component child) {}
|
||||
void childAdded(Component child) {
|
||||
_propagateCollapseToChildren(isCollapsed);
|
||||
}
|
||||
|
||||
void childRemoved(Component child) {}
|
||||
|
||||
@ -71,4 +73,19 @@ abstract class ContainerComponent extends ContainerComponentBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _propagateCollapseToChildren(bool collapse) {
|
||||
for (final child in children) {
|
||||
child.propagateCollapse(collapse);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool propagateCollapse(bool collapse) {
|
||||
if (!super.propagateCollapse(collapse)) {
|
||||
return false;
|
||||
}
|
||||
_propagateCollapseToChildren(collapse);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -85,5 +85,7 @@ abstract class Drawable extends DrawableBase {
|
||||
@override
|
||||
void drawableFlagsChanged(int from, int to) => addDirt(ComponentDirt.paint);
|
||||
|
||||
bool get isHidden => (drawableFlags & ComponentFlags.hidden) != 0;
|
||||
bool get isHidden =>
|
||||
(drawableFlags & ComponentFlags.hidden) != 0 ||
|
||||
(dirt & ComponentDirt.collapsed) != 0;
|
||||
}
|
||||
|
@ -85,4 +85,18 @@ class PathComposer extends Component {
|
||||
_recomputePath();
|
||||
}
|
||||
}
|
||||
|
||||
void syncCollapse() {
|
||||
var collapsed = (dirt & ComponentDirt.collapsed) != 0;
|
||||
if (collapsed == shape.isCollapsed) {
|
||||
return;
|
||||
}
|
||||
if (collapsed) {
|
||||
dirt |= ComponentDirt.collapsed;
|
||||
} else {
|
||||
dirt &= ~ComponentDirt.collapsed;
|
||||
}
|
||||
onDirty(dirt);
|
||||
artboard?.onComponentDirty(this);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,12 @@ class Shape extends ShapeBase with ShapePaintContainer {
|
||||
_worldBounds = _localBounds = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void onDirty(int mask) {
|
||||
pathComposer.syncCollapse();
|
||||
super.onDirty(mask);
|
||||
}
|
||||
|
||||
bool addPath(Path path) {
|
||||
paintChanged();
|
||||
var added = paths.add(path);
|
||||
|
53
lib/src/rive_core/solo.dart
Normal file
53
lib/src/rive_core/solo.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'package:rive/src/generated/solo_base.dart';
|
||||
import 'package:rive/src/rive_core/component.dart';
|
||||
import 'package:rive/src/rive_core/container_component.dart';
|
||||
|
||||
export 'package:rive/src/generated/solo_base.dart';
|
||||
|
||||
class Solo extends SoloBase {
|
||||
Component? _activeComponent;
|
||||
@override
|
||||
void activeComponentIdChanged(int from, int to) =>
|
||||
activeComponent = context.resolve(to);
|
||||
|
||||
Component? get activeComponent => _activeComponent;
|
||||
|
||||
set activeComponent(Component? value) {
|
||||
if (_activeComponent == value) {
|
||||
return;
|
||||
}
|
||||
_activeComponent = value;
|
||||
activeComponentId = value?.id ?? Core.missingId;
|
||||
_updateCollapse();
|
||||
}
|
||||
|
||||
void _updateCollapse() {
|
||||
for (final child in children) {
|
||||
if (child == _activeComponent) {
|
||||
child.propagateCollapse(false);
|
||||
} else {
|
||||
child.propagateCollapse(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void childRemoved(Component child) {
|
||||
super.childRemoved(child);
|
||||
_updateCollapse();
|
||||
}
|
||||
|
||||
@override
|
||||
void childAdded(Component child) {
|
||||
super.childAdded(child);
|
||||
_updateCollapse();
|
||||
}
|
||||
|
||||
@override
|
||||
void onAddedDirty() {
|
||||
super.onAddedDirty();
|
||||
if (activeComponentId != Core.missingId) {
|
||||
activeComponent = context.resolve(activeComponentId);
|
||||
}
|
||||
}
|
||||
}
|
@ -329,6 +329,14 @@ class Text extends TextBase with TextStyleContainer {
|
||||
computeShape();
|
||||
}
|
||||
}
|
||||
if (dirt & ComponentDirt.worldTransform != 0) {
|
||||
for (final style in styles) {
|
||||
for (final paint in style.shapePaints) {
|
||||
paint.renderOpacity = renderOpacity;
|
||||
}
|
||||
}
|
||||
markPaintDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -104,17 +104,20 @@ class TextStyle extends TextStyleBase
|
||||
_variationHelper?.buildDependencies();
|
||||
}
|
||||
|
||||
void removeVariations() => _variations.toSet().forEach(context.removeObject);
|
||||
|
||||
@override
|
||||
set asset(FontAsset? value) {
|
||||
if (asset == value) {
|
||||
return;
|
||||
}
|
||||
_variations.toSet().forEach(context.removeObject);
|
||||
|
||||
super.asset = value;
|
||||
if (asset?.whenDecoded(_fontDecoded, notifyAlreadyDecoded: false) ??
|
||||
false) {
|
||||
// Already decoded.
|
||||
_markShapeDirty();
|
||||
_variationHelper?.addDirt(ComponentDirt.textShape);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user