Joystick updates
- Adds invert x/y to joystick (Thanks @alxgibsn)
- Adds the invert to Flutter and C++ runtimes.
- Updates Cpp core generator to latest Flutter+NNBD.
- Fixes warnings and errors from latest Flutter (Dart SDK really).
Diffs=
a4fb3dc7d Joystick updates (#5261)
diff --git a/.rive_head b/.rive_head
index 38bffa2..9c8e55f 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-8f8e06b9e56bc373e55a7f9b940d0457fe119200
+a4fb3dc7decb50f6965e21ebced098cbdc35883d
diff --git a/dev/core_generator/lib/main.dart b/dev/core_generator/lib/main.dart
index a48945c..1d433d9 100644
--- a/dev/core_generator/lib/main.dart
+++ b/dev/core_generator/lib/main.dart
@@ -9,7 +9,7 @@
Directory(defsPath).list(recursive: true).listen(
(entity) {
if (entity is File && entity.path.toLowerCase().endsWith('.json')) {
- Definition(entity.path.substring(defsPath.length));
+ Definition.make(entity.path.substring(defsPath.length));
}
},
onDone: Definition.generate,
diff --git a/dev/core_generator/lib/src/definition.dart b/dev/core_generator/lib/src/definition.dart
index 44a479b..596b969 100644
--- a/dev/core_generator/lib/src/definition.dart
+++ b/dev/core_generator/lib/src/definition.dart
@@ -19,20 +19,20 @@
final String _filename;
static final _formatter = CppFormatter();
- String _name;
+ String? _name;
final List<Property> _properties = [];
List<Property> get properties => _properties
.where((property) => property.isRuntime)
.toList(growable: false);
- Definition _extensionOf;
- Key _key;
+ Definition? _extensionOf;
+ Key? _key;
bool _isAbstract = false;
bool _editorOnly = false;
bool _forRuntime = true;
bool get forRuntime => _forRuntime;
- factory Definition(String filename) {
+ static Definition? make(String filename) {
var definition = definitions[filename];
if (definition != null) {
return definition;
@@ -40,7 +40,7 @@
var file = File(defsPath + filename);
var contents = file.readAsStringSync();
- Map<String, dynamic> definitionData;
+ late Map<String, dynamic> definitionData;
try {
dynamic parsedJson = json.decode(contents);
if (parsedJson is Map<String, dynamic>) {
@@ -54,10 +54,11 @@
definition = Definition.fromFilename(filename, definitionData);
return definition;
}
+
Definition.fromFilename(this._filename, Map<String, dynamic> data) {
dynamic extendsFilename = data['extends'];
if (extendsFilename is String) {
- _extensionOf = Definition(extendsFilename);
+ _extensionOf = Definition.make(extendsFilename);
}
dynamic nameValue = data['name'];
if (nameValue is String) {
@@ -81,8 +82,8 @@
if (properties is Map<String, dynamic>) {
for (final MapEntry<String, dynamic> entry in properties.entries) {
if (entry.value is Map<String, dynamic>) {
- var property =
- Property(this, entry.key, entry.value as Map<String, dynamic>);
+ var property = Property.make(
+ this, entry.key, entry.value as Map<String, dynamic>);
if (property == null) {
continue;
}
@@ -95,7 +96,7 @@
? _filename.substring(defsPath.length)
: _filename;
- String get name => _name;
+ String? get name => _name;
String get localCodeFilename => '${stripExtension(_filename)}_base.hpp';
String get concreteCodeFilename => 'rive/${stripExtension(_filename)}.hpp';
@@ -112,11 +113,12 @@
var includes = <String>{
defineContextExtension
? 'rive/core.hpp'
- : _extensionOf.concreteCodeFilename
+ : _extensionOf!.concreteCodeFilename
};
for (final property in properties) {
- if (property.type.include != null) {
- includes.add(property.type.include);
+ var include = property.type.include;
+ if (include != null) {
+ includes.add(include);
}
includes.add('rive/core/field_types/' +
property.type.snakeRuntimeCoreName +
@@ -141,7 +143,7 @@
code.writeln('protected:');
code.writeln('typedef $superTypeName Super;');
code.writeln('public:');
- code.writeln('static const uint16_t typeKey = ${_key.intValue};\n');
+ code.writeln('static const uint16_t typeKey = ${_key!.intValue};\n');
code.write(comment(
'Helper to quickly determine if a core object extends another '
@@ -163,7 +165,7 @@
if (properties.isNotEmpty) {
for (final property in properties) {
code.writeln('static const uint16_t ${property.name}PropertyKey = '
- '${property.key.intValue};');
+ '${property.key!.intValue};');
}
if (properties.any((prop) => !prop.isEncoded)) {
code.writeln('private:');
@@ -244,7 +246,7 @@
}
}
if (_extensionOf != null) {
- code.writeln('${_extensionOf.name}::'
+ code.writeln('${_extensionOf!.name}::'
'copy(object); ');
}
code.writeln('}');
@@ -269,7 +271,7 @@
code.writeln('}');
}
if (_extensionOf != null) {
- code.writeln('return ${_extensionOf.name}::'
+ code.writeln('return ${_extensionOf!.name}::'
'deserialize(propertyKey, reader); }');
} else {
code.writeln('return false; }');
@@ -306,7 +308,7 @@
concreteCode.writeln('}');
var formattedCode =
- await _formatter.formatAndGuard(_name, concreteCode.toString());
+ await _formatter.formatAndGuard(_name!, concreteCode.toString());
concreteFile.writeAsStringSync(formattedCode, flush: true);
}
if (!_isAbstract) {
@@ -341,34 +343,34 @@
Map<int, Property> properties = {};
for (final definition in definitions.values) {
if (definition._key?.intValue != null) {
- var other = ids[definition._key.intValue];
+ var other = ids[definition._key!.intValue];
if (other != null) {
color('Duplicate type ids for $definition and $other.',
front: Styles.RED);
runGenerator = false;
} else {
- ids[definition._key.intValue] = definition;
+ ids[definition._key!.intValue!] = definition;
}
}
for (final property in definition._properties) {
- if (property.key.isMissing) {
+ if (property.key!.isMissing) {
continue;
}
- var other = properties[property.key.intValue];
+ var other = properties[property.key!.intValue];
if (other != null) {
color(
'''Duplicate field ids for ${property.definition}.$property '''
'''and ${other.definition}.$other.''',
front: Styles.RED);
runGenerator = false;
- } else if (property.key.intValue < minPropertyId) {
+ } else if (property.key!.intValue! < minPropertyId) {
color(
'${property.definition}.$property: ids less than '
'$minPropertyId are reserved.',
front: Styles.RED);
runGenerator = false;
} else {
- properties[property.key.intValue] = property;
+ properties[property.key!.intValue!] = property;
}
}
}
@@ -377,16 +379,14 @@
int nextFieldId = minPropertyId - 1;
int nextId = 0;
for (final definition in definitions.values) {
- if (definition._key != null &&
- definition._key.intValue != null &&
- definition._key.intValue > nextId) {
- nextId = definition._key.intValue;
+ var intValue = definition._key?.intValue;
+ if (intValue != null && intValue > nextId) {
+ nextId = intValue;
}
for (final field in definition._properties) {
- if (field != null &&
- field.key.intValue != null &&
- field.key.intValue > nextFieldId) {
- nextFieldId = field.key.intValue;
+ var intValue = field.key?.intValue;
+ if (intValue != null && intValue > nextFieldId) {
+ nextFieldId = intValue;
}
}
}
@@ -440,10 +440,10 @@
for (final definition in runtimeDefinitions) {
for (final property in definition.properties) {
usedFieldTypes[property.type] ??= [];
- usedFieldTypes[property.type].add(property);
+ usedFieldTypes[property.type]!.add(property);
if (!property.isEncoded) {
getSetFieldTypes[property.type] ??= [];
- getSetFieldTypes[property.type].add(property);
+ getSetFieldTypes[property.type]!.add(property);
}
}
}
@@ -453,12 +453,14 @@
'int propertyKey, ${fieldType.cppName} value){');
ctxCode.writeln('switch (propertyKey) {');
var properties = getSetFieldTypes[fieldType];
- for (final property in properties) {
- ctxCode.writeln('case ${property.definition.name}Base'
- '::${property.name}PropertyKey:');
- ctxCode.writeln('object->as<${property.definition.name}Base>()->'
- '${property.name}(value);');
- ctxCode.writeln('break;');
+ if (properties != null) {
+ for (final property in properties) {
+ ctxCode.writeln('case ${property.definition.name}Base'
+ '::${property.name}PropertyKey:');
+ ctxCode.writeln('object->as<${property.definition.name}Base>()->'
+ '${property.name}(value);');
+ ctxCode.writeln('break;');
+ }
}
ctxCode.writeln('}}');
}
@@ -468,11 +470,14 @@
'Core* object, int propertyKey){');
ctxCode.writeln('switch (propertyKey) {');
var properties = getSetFieldTypes[fieldType];
- for (final property in properties) {
- ctxCode.writeln('case ${property.definition.name}Base'
- '::${property.name}PropertyKey:');
- ctxCode.writeln('return object->as<${property.definition.name}Base>()->'
- '${property.name}();');
+ if (properties != null) {
+ for (final property in properties) {
+ ctxCode.writeln('case ${property.definition.name}Base'
+ '::${property.name}PropertyKey:');
+ ctxCode
+ .writeln('return object->as<${property.definition.name}Base>()->'
+ '${property.name}();');
+ }
}
ctxCode.writeln('}');
ctxCode.writeln('return ${fieldType.defaultValue ?? 'nullptr'};');
@@ -484,9 +489,11 @@
for (final fieldType in usedFieldTypes.keys) {
var properties = usedFieldTypes[fieldType];
- for (final property in properties) {
- ctxCode.writeln('case ${property.definition.name}Base'
- '::${property.name}PropertyKey:');
+ if (properties != null) {
+ for (final property in properties) {
+ ctxCode.writeln('case ${property.definition.name}Base'
+ '::${property.name}PropertyKey:');
+ }
}
ctxCode.writeln('return Core${fieldType.capitalizedName}Type::id;');
}
@@ -496,10 +503,9 @@
ctxCode.writeln('};}');
var output = generatedHppPath;
- var folder =
- output != null && output.isNotEmpty && output[output.length - 1] == '/'
- ? output.substring(0, output.length - 1)
- : output;
+ var folder = output.isNotEmpty && output[output.length - 1] == '/'
+ ? output.substring(0, output.length - 1)
+ : output;
var file = File('$folder/core_registry.hpp');
file.createSync(recursive: true);
diff --git a/dev/core_generator/lib/src/field_type.dart b/dev/core_generator/lib/src/field_type.dart
index 486bd92..60f3025 100644
--- a/dev/core_generator/lib/src/field_type.dart
+++ b/dev/core_generator/lib/src/field_type.dart
@@ -8,26 +8,25 @@
abstract class FieldType {
final String name;
- String _cppName;
- final String include;
- String get cppName => _cppName;
- String get cppGetterName => _cppName;
+ String? _cppName;
+ final String? include;
+ String? get cppName => _cppName;
+ String? get cppGetterName => _cppName;
- String _runtimeCoreType;
+ final String _runtimeCoreType;
String get runtimeCoreType => _runtimeCoreType;
FieldType(
this.name,
- String runtimeCoreType, {
- String cppName,
+ this._runtimeCoreType, {
+ String? cppName,
this.include,
}) {
_cppName = cppName ?? name;
- _runtimeCoreType = runtimeCoreType;
_types[name] = this;
}
- static FieldType find(dynamic key) {
+ static FieldType? find(dynamic key) {
if (key is! String) {
return null;
}
@@ -40,10 +39,10 @@
}
String equalityCheck(String varAName, String varBName) {
- return "$varAName == $varBName";
+ return '$varAName == $varBName';
}
- String get defaultValue => null;
+ String? get defaultValue => null;
String get uncapitalizedName => '${name[0].toLowerCase()}${name.substring(1)}'
.replaceAll('<', '')
@@ -61,5 +60,5 @@
.replaceAllMapped(RegExp('(.+?)([A-Z])'), (Match m) => '${m[1]}_${m[2]}')
.toLowerCase();
- String convertCpp(String value) => value;
+ String? convertCpp(String value) => value;
}
diff --git a/dev/core_generator/lib/src/field_types/bytes_field_type.dart b/dev/core_generator/lib/src/field_types/bytes_field_type.dart
index 163772b..3ff7f55 100644
--- a/dev/core_generator/lib/src/field_types/bytes_field_type.dart
+++ b/dev/core_generator/lib/src/field_types/bytes_field_type.dart
@@ -16,7 +16,7 @@
String get cppGetterName => 'Span<const uint8_t>';
@override
- String convertCpp(String value) {
+ String? convertCpp(String value) {
return null;
}
}
diff --git a/dev/core_generator/lib/src/field_types/double_field_type.dart b/dev/core_generator/lib/src/field_types/double_field_type.dart
index 69e4f49..1f565bb 100644
--- a/dev/core_generator/lib/src/field_types/double_field_type.dart
+++ b/dev/core_generator/lib/src/field_types/double_field_type.dart
@@ -7,7 +7,7 @@
String get defaultValue => '0.0f';
@override
- String convertCpp(String value) {
+ String? convertCpp(String value) {
var result = value;
if (result.isNotEmpty) {
if (result[result.length - 1] != 'f') {
diff --git a/dev/core_generator/lib/src/field_types/initialize.dart b/dev/core_generator/lib/src/field_types/initialize.dart
index 01c0b2a..5d37260 100644
--- a/dev/core_generator/lib/src/field_types/initialize.dart
+++ b/dev/core_generator/lib/src/field_types/initialize.dart
@@ -3,7 +3,7 @@
import 'package:core_generator/src/field_type.dart';
import 'package:core_generator/src/field_types/bytes_field_type.dart';
-List<FieldType> fields;
+late List<FieldType> fields;
void initializeFields() {
fields = [
diff --git a/dev/core_generator/lib/src/field_types/string_field_type.dart b/dev/core_generator/lib/src/field_types/string_field_type.dart
index db4842f..3fb6d9f 100644
--- a/dev/core_generator/lib/src/field_types/string_field_type.dart
+++ b/dev/core_generator/lib/src/field_types/string_field_type.dart
@@ -11,7 +11,7 @@
String get cppGetterName => 'const std::string&';
@override
- String convertCpp(String value) {
+ String? convertCpp(String value) {
var result = value;
if (result.length > 1) {
if (result[0] == '\'') {
diff --git a/dev/core_generator/lib/src/field_types/uint_field_type.dart b/dev/core_generator/lib/src/field_types/uint_field_type.dart
index daa2457..df9b751 100644
--- a/dev/core_generator/lib/src/field_types/uint_field_type.dart
+++ b/dev/core_generator/lib/src/field_types/uint_field_type.dart
@@ -13,5 +13,6 @@
// We do this to fix up CoreContext.invalidProperyKey
@override
- String convertCpp(String value) => value.replaceAll('CoreContext.', 'Core::');
+ String? convertCpp(String value) =>
+ value.replaceAll('CoreContext.', 'Core::');
}
diff --git a/dev/core_generator/lib/src/key.dart b/dev/core_generator/lib/src/key.dart
index a455656..8e1a8d0 100644
--- a/dev/core_generator/lib/src/key.dart
+++ b/dev/core_generator/lib/src/key.dart
@@ -2,14 +2,14 @@
import 'property.dart';
class Key {
- final String stringValue;
- final int intValue;
+ final String? stringValue;
+ final int? intValue;
bool get isMissing => intValue == null;
Key(this.stringValue, this.intValue);
Key.forDefinition(Definition def)
- : stringValue = def.name.toLowerCase(),
+ : stringValue = def.name?.toLowerCase(),
intValue = null;
Key.forProperty(Property field)
: stringValue = field.name.toLowerCase(),
@@ -17,7 +17,7 @@
Key withIntValue(int id) => Key(stringValue, id);
- factory Key.fromJSON(dynamic data) {
+ static Key? fromJSON(dynamic data) {
if (data is! Map<String, dynamic>) {
return null;
}
diff --git a/dev/core_generator/lib/src/property.dart b/dev/core_generator/lib/src/property.dart
index 76b3be8..e13ae67 100644
--- a/dev/core_generator/lib/src/property.dart
+++ b/dev/core_generator/lib/src/property.dart
@@ -7,22 +7,23 @@
final String name;
final FieldType type;
final Definition definition;
- String initialValue;
- String initialValueRuntime;
+ String? initialValue;
+ String? initialValueRuntime;
bool isVirtual = false;
bool animates = false;
- String group;
- Key key;
- String description;
+ String? group;
+ Key? key;
+ String? description;
bool isNullable = false;
bool isRuntime = true;
bool isCoop = true;
bool isSetOverride = false;
bool isGetOverride = false;
bool isEncoded = false;
- FieldType typeRuntime;
+ FieldType? typeRuntime;
- factory Property(Definition type, String name, Map<String, dynamic> data) {
+ static Property? make(
+ Definition type, String name, Map<String, dynamic> data) {
if (data['runtime'] is bool && data['runtime'] == false) {
return null;
}
@@ -34,11 +35,10 @@
color('Invalid field type ${data['type']} for $name.', front: Styles.RED);
return null;
}
- return Property.make(type, name, fieldType, data);
+ return Property(type, name, fieldType, data);
}
- Property.make(
- this.definition, this.name, this.type, Map<String, dynamic> data) {
+ Property(this.definition, this.name, this.type, Map<String, dynamic> data) {
dynamic encodedValue = data['encoded'];
if (encodedValue is bool) {
isEncoded = encodedValue;
@@ -99,9 +99,7 @@
FieldType getExportType() => typeRuntime ?? type;
@override
- String toString() {
- return '$name(${key.intValue})';
- }
+ String toString() => '$name(${key?.intValue})';
String get capitalizedName => '${name[0].toUpperCase()}${name.substring(1)}'
.replaceAll('<', '')
diff --git a/dev/core_generator/pubspec.yaml b/dev/core_generator/pubspec.yaml
index 6e7eb7c..5227dc5 100644
--- a/dev/core_generator/pubspec.yaml
+++ b/dev/core_generator/pubspec.yaml
@@ -1,7 +1,7 @@
name: core_generator
environment:
- sdk: ">=2.6.0 <3.0.0"
+ sdk: ">=2.12.0 <3.0.0"
dependencies:
- colorize: ^2.0.0
+ colorize: ^3.0.0
diff --git a/dev/defs/joystick.json b/dev/defs/joystick.json
index 4ef5e38..17137f0 100644
--- a/dev/defs/joystick.json
+++ b/dev/defs/joystick.json
@@ -32,8 +32,7 @@
"key": {
"int": 303,
"string": "posx"
- },
- "runtime": false
+ }
},
"posY": {
"type": "double",
@@ -41,8 +40,7 @@
"key": {
"int": 304,
"string": "posy"
- },
- "runtime": false
+ }
},
"originX": {
"type": "double",
@@ -51,8 +49,7 @@
"int": 307,
"string": "originx"
},
- "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right).",
- "runtime": false
+ "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right)."
},
"originY": {
"type": "double",
@@ -61,8 +58,7 @@
"int": 308,
"string": "originy"
},
- "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom).",
- "runtime": false
+ "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom)."
},
"width": {
"type": "double",
@@ -70,8 +66,7 @@
"key": {
"int": 305,
"string": "width"
- },
- "runtime": false
+ }
},
"height": {
"type": "double",
@@ -79,8 +74,7 @@
"key": {
"int": 306,
"string": "height"
- },
- "runtime": false
+ }
},
"xId": {
"type": "Id",
@@ -103,6 +97,25 @@
"string": "y_id"
},
"description": "Identifier used to track the animation used for the y axis of the joystick."
+ },
+ "joystickFlags": {
+ "type": "uint",
+ "initialValue": "0",
+ "key": {
+ "int": 312,
+ "string": "joystickflags"
+ }
+ },
+ "handleSourceId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 313,
+ "string": "handlesourceid"
+ },
+ "description": "Identifier used to track the custom handle source of the joystick."
}
}
}
\ No newline at end of file
diff --git a/include/rive/animation/blend_state_1d.hpp b/include/rive/animation/blend_state_1d.hpp
index ebe394a..8b3092d 100644
--- a/include/rive/animation/blend_state_1d.hpp
+++ b/include/rive/animation/blend_state_1d.hpp
@@ -7,10 +7,7 @@
class BlendState1D : public BlendState1DBase
{
public:
- // -1 (4294967295) is our flag value for input not set. It means it wasn't set at edit
- // time.
- const uint32_t noInputSpecified = -1;
- bool hasValidInputId() const { return inputId() != noInputSpecified; }
+ bool hasValidInputId() const { return inputId() != Core::emptyId; }
StatusCode import(ImportStack& importStack) override;
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 39e9034..b53430c 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -43,6 +43,7 @@
std::vector<DrawTarget*> m_DrawTargets;
std::vector<NestedArtboard*> m_NestedArtboards;
std::vector<Joystick*> m_Joysticks;
+ bool m_JoysticksApplyBeforeUpdate = true;
unsigned int m_DirtDepth = 0;
std::unique_ptr<RenderPath> m_BackgroundPath;
diff --git a/include/rive/core.hpp b/include/rive/core.hpp
index 0b5567f..b397d49 100644
--- a/include/rive/core.hpp
+++ b/include/rive/core.hpp
@@ -12,6 +12,7 @@
class Core
{
public:
+ const uint32_t emptyId = -1;
static const int invalidPropertyKey = 0;
virtual ~Core() {}
virtual uint16_t coreType() const = 0;
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index 3dd4042..5ba4c82 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -517,6 +517,12 @@
case JoystickBase::yIdPropertyKey:
object->as<JoystickBase>()->yId(value);
break;
+ case JoystickBase::joystickFlagsPropertyKey:
+ object->as<JoystickBase>()->joystickFlags(value);
+ break;
+ case JoystickBase::handleSourceIdPropertyKey:
+ object->as<JoystickBase>()->handleSourceId(value);
+ break;
case WeightBase::valuesPropertyKey:
object->as<WeightBase>()->values(value);
break;
@@ -781,6 +787,24 @@
case JoystickBase::yPropertyKey:
object->as<JoystickBase>()->y(value);
break;
+ case JoystickBase::posXPropertyKey:
+ object->as<JoystickBase>()->posX(value);
+ break;
+ case JoystickBase::posYPropertyKey:
+ object->as<JoystickBase>()->posY(value);
+ break;
+ case JoystickBase::originXPropertyKey:
+ object->as<JoystickBase>()->originX(value);
+ break;
+ case JoystickBase::originYPropertyKey:
+ object->as<JoystickBase>()->originY(value);
+ break;
+ case JoystickBase::widthPropertyKey:
+ object->as<JoystickBase>()->width(value);
+ break;
+ case JoystickBase::heightPropertyKey:
+ object->as<JoystickBase>()->height(value);
+ break;
case BoneBase::lengthPropertyKey:
object->as<BoneBase>()->length(value);
break;
@@ -1059,6 +1083,10 @@
return object->as<JoystickBase>()->xId();
case JoystickBase::yIdPropertyKey:
return object->as<JoystickBase>()->yId();
+ case JoystickBase::joystickFlagsPropertyKey:
+ return object->as<JoystickBase>()->joystickFlags();
+ case JoystickBase::handleSourceIdPropertyKey:
+ return object->as<JoystickBase>()->handleSourceId();
case WeightBase::valuesPropertyKey:
return object->as<WeightBase>()->values();
case WeightBase::indicesPropertyKey:
@@ -1238,6 +1266,18 @@
return object->as<JoystickBase>()->x();
case JoystickBase::yPropertyKey:
return object->as<JoystickBase>()->y();
+ case JoystickBase::posXPropertyKey:
+ return object->as<JoystickBase>()->posX();
+ case JoystickBase::posYPropertyKey:
+ return object->as<JoystickBase>()->posY();
+ case JoystickBase::originXPropertyKey:
+ return object->as<JoystickBase>()->originX();
+ case JoystickBase::originYPropertyKey:
+ return object->as<JoystickBase>()->originY();
+ case JoystickBase::widthPropertyKey:
+ return object->as<JoystickBase>()->width();
+ case JoystickBase::heightPropertyKey:
+ return object->as<JoystickBase>()->height();
case BoneBase::lengthPropertyKey:
return object->as<BoneBase>()->length();
case RootBoneBase::xPropertyKey:
@@ -1408,6 +1448,8 @@
case ArtboardBase::defaultStateMachineIdPropertyKey:
case JoystickBase::xIdPropertyKey:
case JoystickBase::yIdPropertyKey:
+ case JoystickBase::joystickFlagsPropertyKey:
+ case JoystickBase::handleSourceIdPropertyKey:
case WeightBase::valuesPropertyKey:
case WeightBase::indicesPropertyKey:
case TendonBase::boneIdPropertyKey:
@@ -1495,6 +1537,12 @@
case ArtboardBase::originYPropertyKey:
case JoystickBase::xPropertyKey:
case JoystickBase::yPropertyKey:
+ case JoystickBase::posXPropertyKey:
+ case JoystickBase::posYPropertyKey:
+ case JoystickBase::originXPropertyKey:
+ case JoystickBase::originYPropertyKey:
+ case JoystickBase::widthPropertyKey:
+ case JoystickBase::heightPropertyKey:
case BoneBase::lengthPropertyKey:
case RootBoneBase::xPropertyKey:
case RootBoneBase::yPropertyKey:
diff --git a/include/rive/generated/joystick_base.hpp b/include/rive/generated/joystick_base.hpp
index 401188e..d32b1a4 100644
--- a/include/rive/generated/joystick_base.hpp
+++ b/include/rive/generated/joystick_base.hpp
@@ -31,14 +31,30 @@
static const uint16_t xPropertyKey = 299;
static const uint16_t yPropertyKey = 300;
+ static const uint16_t posXPropertyKey = 303;
+ static const uint16_t posYPropertyKey = 304;
+ static const uint16_t originXPropertyKey = 307;
+ static const uint16_t originYPropertyKey = 308;
+ static const uint16_t widthPropertyKey = 305;
+ static const uint16_t heightPropertyKey = 306;
static const uint16_t xIdPropertyKey = 301;
static const uint16_t yIdPropertyKey = 302;
+ static const uint16_t joystickFlagsPropertyKey = 312;
+ static const uint16_t handleSourceIdPropertyKey = 313;
private:
float m_X = 0.0f;
float m_Y = 0.0f;
+ float m_PosX = 0.0f;
+ float m_PosY = 0.0f;
+ float m_OriginX = 0.5f;
+ float m_OriginY = 0.5f;
+ float m_Width = 100.0f;
+ float m_Height = 100.0f;
uint32_t m_XId = -1;
uint32_t m_YId = -1;
+ uint32_t m_JoystickFlags = 0;
+ uint32_t m_HandleSourceId = -1;
public:
inline float x() const { return m_X; }
@@ -63,6 +79,72 @@
yChanged();
}
+ inline float posX() const { return m_PosX; }
+ void posX(float value)
+ {
+ if (m_PosX == value)
+ {
+ return;
+ }
+ m_PosX = value;
+ posXChanged();
+ }
+
+ inline float posY() const { return m_PosY; }
+ void posY(float value)
+ {
+ if (m_PosY == value)
+ {
+ return;
+ }
+ m_PosY = value;
+ posYChanged();
+ }
+
+ inline float originX() const { return m_OriginX; }
+ void originX(float value)
+ {
+ if (m_OriginX == value)
+ {
+ return;
+ }
+ m_OriginX = value;
+ originXChanged();
+ }
+
+ inline float originY() const { return m_OriginY; }
+ void originY(float value)
+ {
+ if (m_OriginY == value)
+ {
+ return;
+ }
+ m_OriginY = value;
+ originYChanged();
+ }
+
+ inline float width() const { return m_Width; }
+ void width(float value)
+ {
+ if (m_Width == value)
+ {
+ return;
+ }
+ m_Width = value;
+ widthChanged();
+ }
+
+ inline float height() const { return m_Height; }
+ void height(float value)
+ {
+ if (m_Height == value)
+ {
+ return;
+ }
+ m_Height = value;
+ heightChanged();
+ }
+
inline uint32_t xId() const { return m_XId; }
void xId(uint32_t value)
{
@@ -85,13 +167,43 @@
yIdChanged();
}
+ inline uint32_t joystickFlags() const { return m_JoystickFlags; }
+ void joystickFlags(uint32_t value)
+ {
+ if (m_JoystickFlags == value)
+ {
+ return;
+ }
+ m_JoystickFlags = value;
+ joystickFlagsChanged();
+ }
+
+ inline uint32_t handleSourceId() const { return m_HandleSourceId; }
+ void handleSourceId(uint32_t value)
+ {
+ if (m_HandleSourceId == value)
+ {
+ return;
+ }
+ m_HandleSourceId = value;
+ handleSourceIdChanged();
+ }
+
Core* clone() const override;
void copy(const JoystickBase& object)
{
m_X = object.m_X;
m_Y = object.m_Y;
+ m_PosX = object.m_PosX;
+ m_PosY = object.m_PosY;
+ m_OriginX = object.m_OriginX;
+ m_OriginY = object.m_OriginY;
+ m_Width = object.m_Width;
+ m_Height = object.m_Height;
m_XId = object.m_XId;
m_YId = object.m_YId;
+ m_JoystickFlags = object.m_JoystickFlags;
+ m_HandleSourceId = object.m_HandleSourceId;
Component::copy(object);
}
@@ -105,12 +217,36 @@
case yPropertyKey:
m_Y = CoreDoubleType::deserialize(reader);
return true;
+ case posXPropertyKey:
+ m_PosX = CoreDoubleType::deserialize(reader);
+ return true;
+ case posYPropertyKey:
+ m_PosY = CoreDoubleType::deserialize(reader);
+ return true;
+ case originXPropertyKey:
+ m_OriginX = CoreDoubleType::deserialize(reader);
+ return true;
+ case originYPropertyKey:
+ m_OriginY = CoreDoubleType::deserialize(reader);
+ return true;
+ case widthPropertyKey:
+ m_Width = CoreDoubleType::deserialize(reader);
+ return true;
+ case heightPropertyKey:
+ m_Height = CoreDoubleType::deserialize(reader);
+ return true;
case xIdPropertyKey:
m_XId = CoreUintType::deserialize(reader);
return true;
case yIdPropertyKey:
m_YId = CoreUintType::deserialize(reader);
return true;
+ case joystickFlagsPropertyKey:
+ m_JoystickFlags = CoreUintType::deserialize(reader);
+ return true;
+ case handleSourceIdPropertyKey:
+ m_HandleSourceId = CoreUintType::deserialize(reader);
+ return true;
}
return Component::deserialize(propertyKey, reader);
}
@@ -118,8 +254,16 @@
protected:
virtual void xChanged() {}
virtual void yChanged() {}
+ virtual void posXChanged() {}
+ virtual void posYChanged() {}
+ virtual void originXChanged() {}
+ virtual void originYChanged() {}
+ virtual void widthChanged() {}
+ virtual void heightChanged() {}
virtual void xIdChanged() {}
virtual void yIdChanged() {}
+ virtual void joystickFlagsChanged() {}
+ virtual void handleSourceIdChanged() {}
};
} // namespace rive
diff --git a/include/rive/joystick.hpp b/include/rive/joystick.hpp
index d076a14..e75556e 100644
--- a/include/rive/joystick.hpp
+++ b/include/rive/joystick.hpp
@@ -1,20 +1,39 @@
#ifndef _RIVE_JOYSTICK_HPP_
#define _RIVE_JOYSTICK_HPP_
#include "rive/generated/joystick_base.hpp"
+#include "rive/joystick_flags.hpp"
+#include "rive/math/mat2d.hpp"
#include <stdio.h>
+
namespace rive
{
class Artboard;
class LinearAnimation;
+class TransformComponent;
class Joystick : public JoystickBase
{
public:
+ void update(ComponentDirt value) override;
void apply(Artboard* artboard) const;
StatusCode onAddedClean(CoreContext* context) override;
+ StatusCode onAddedDirty(CoreContext* context) override;
+
+ bool isJoystickFlagged(JoystickFlags flag) const
+ {
+ return (((JoystickFlags)joystickFlags()) & flag) == flag;
+ }
+
+ bool canApplyBeforeUpdate() const { return m_handleSource == nullptr; }
+
+protected:
+ void buildDependencies() override;
private:
+ Mat2D m_worldTransform;
+ Mat2D m_inverseWorldTransform;
LinearAnimation* m_xAnimation = nullptr;
LinearAnimation* m_yAnimation = nullptr;
+ TransformComponent* m_handleSource = nullptr;
};
} // namespace rive
diff --git a/include/rive/joystick_flags.hpp b/include/rive/joystick_flags.hpp
new file mode 100644
index 0000000..d3e73cd
--- /dev/null
+++ b/include/rive/joystick_flags.hpp
@@ -0,0 +1,64 @@
+#ifndef _RIVE_JOYSTICK_FLAGS_HPP_
+#define _RIVE_JOYSTICK_FLAGS_HPP_
+namespace rive
+{
+enum class JoystickFlags : unsigned char
+{
+ /// Whether to invert the application of the x axis.
+ invertX = 1 << 0,
+
+ /// Whether to invert the application of the y axis.
+ invertY = 1 << 1,
+
+ /// Whether this Joystick works in world space.
+ worldSpace = 1 << 2
+};
+
+inline constexpr JoystickFlags operator&(JoystickFlags lhs, JoystickFlags rhs)
+{
+ return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) &
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+}
+
+inline constexpr JoystickFlags operator^(JoystickFlags lhs, JoystickFlags rhs)
+{
+ return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) ^
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+}
+
+inline constexpr JoystickFlags operator|(JoystickFlags lhs, JoystickFlags rhs)
+{
+ return static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) |
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+}
+
+inline constexpr JoystickFlags operator~(JoystickFlags rhs)
+{
+ return static_cast<JoystickFlags>(~static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+}
+
+inline JoystickFlags& operator|=(JoystickFlags& lhs, JoystickFlags rhs)
+{
+ lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) |
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+
+ return lhs;
+}
+
+inline JoystickFlags& operator&=(JoystickFlags& lhs, JoystickFlags rhs)
+{
+ lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) &
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+
+ return lhs;
+}
+
+inline JoystickFlags& operator^=(JoystickFlags& lhs, JoystickFlags rhs)
+{
+ lhs = static_cast<JoystickFlags>(static_cast<std::underlying_type<JoystickFlags>::type>(lhs) ^
+ static_cast<std::underlying_type<JoystickFlags>::type>(rhs));
+
+ return lhs;
+}
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/include/rive/math/aabb.hpp b/include/rive/math/aabb.hpp
index 81c1c6a..5d0ef5d 100644
--- a/include/rive/math/aabb.hpp
+++ b/include/rive/math/aabb.hpp
@@ -90,6 +90,12 @@
static void join(AABB& out, const AABB& a, const AABB& b);
void expand(const AABB& other) { join(*this, *this, other); }
+
+ Vec2D factorFrom(Vec2D point) const
+ {
+ return Vec2D((point.x - left()) * 2.0f / width() - 1.0f,
+ (point.y - top()) * 2.0f / height() - 1.0f);
+ }
};
} // namespace rive
diff --git a/src/artboard.cpp b/src/artboard.cpp
index ed104ee..2c5b233 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -150,8 +150,15 @@
break;
case JoystickBase::typeKey:
- m_Joysticks.push_back(object->as<Joystick>());
+ {
+ Joystick* joystick = object->as<Joystick>();
+ if (!joystick->canApplyBeforeUpdate())
+ {
+ m_JoysticksApplyBeforeUpdate = false;
+ }
+ m_Joysticks.push_back(joystick);
break;
+ }
}
}
@@ -465,12 +472,23 @@
bool Artboard::advance(double elapsedSeconds)
{
- for (auto joystick : m_Joysticks)
+ if (m_JoysticksApplyBeforeUpdate)
{
- joystick->apply(this);
+ for (auto joystick : m_Joysticks)
+ {
+ joystick->apply(this);
+ }
}
bool didUpdate = updateComponents();
+ if (!m_JoysticksApplyBeforeUpdate)
+ {
+ for (auto joystick : m_Joysticks)
+ {
+ joystick->apply(this);
+ }
+ updateComponents();
+ }
for (auto nestedArtboard : m_NestedArtboards)
{
if (nestedArtboard->advance((float)elapsedSeconds))
diff --git a/src/joystick.cpp b/src/joystick.cpp
index 5a61281..334face 100644
--- a/src/joystick.cpp
+++ b/src/joystick.cpp
@@ -1,23 +1,94 @@
#include "rive/joystick.hpp"
#include "rive/artboard.hpp"
+#include "rive/transform_component.hpp"
using namespace rive;
+StatusCode Joystick::onAddedDirty(CoreContext* context)
+{
+ StatusCode status = Super::onAddedDirty(context);
+ if (status != StatusCode::Ok)
+ {
+ return status;
+ }
+ if (handleSourceId() != Core::emptyId)
+ {
+ auto coreObject = context->resolve(handleSourceId());
+ if (coreObject == nullptr || !coreObject->is<TransformComponent>())
+ {
+ return StatusCode::MissingObject;
+ }
+ m_handleSource = static_cast<TransformComponent*>(coreObject);
+ }
+
+ return StatusCode::Ok;
+}
+
StatusCode Joystick::onAddedClean(CoreContext* context)
{
m_xAnimation = artboard()->animation(xId());
m_yAnimation = artboard()->animation(yId());
+
return StatusCode::Ok;
}
+void Joystick::buildDependencies()
+{
+ // We only need to update if we're in world space (and that is only required
+ // at runtime if we have a custom handle source).
+ if (m_handleSource != nullptr && parent() != nullptr)
+ {
+ parent()->addDependent(this);
+ m_handleSource->addDependent(this);
+ }
+}
+
+void Joystick::update(ComponentDirt value)
+{
+ if (m_handleSource == nullptr)
+ {
+ return;
+ }
+ if (hasDirt(value, ComponentDirt::WorldTransform | ComponentDirt::Transform))
+ {
+ Mat2D world = Mat2D::fromTranslate(posX(), posY());
+ if (parent() != nullptr && parent()->is<WorldTransformComponent>())
+ {
+ world = parent()->as<WorldTransformComponent>()->worldTransform() * world;
+ }
+
+ if (m_worldTransform != world)
+ {
+ m_worldTransform = world;
+ m_inverseWorldTransform = world.invertOrIdentity();
+ }
+
+ auto pos = m_inverseWorldTransform * m_handleSource->worldTranslation();
+
+ auto localBounds = AABB(-width() * originX(),
+ -height() * originY(),
+ -width() * originX() + width(),
+ -height() * originY() + height());
+
+ auto local = localBounds.factorFrom(pos);
+ x(local.x);
+ y(local.y);
+ }
+}
+
void Joystick::apply(Artboard* artboard) const
{
if (m_xAnimation != nullptr)
{
- m_xAnimation->apply(artboard, (x() + 1.0f) / 2.0f * m_xAnimation->durationSeconds());
+ m_xAnimation->apply(artboard,
+ ((isJoystickFlagged(JoystickFlags::invertX) ? -x() : x()) + 1.0f) /
+ 2.0f * m_xAnimation->durationSeconds());
}
if (m_yAnimation != nullptr)
{
- m_yAnimation->apply(artboard, (y() + 1.0f) / 2.0f * m_yAnimation->durationSeconds());
+
+ m_yAnimation->apply(artboard,
+ ((isJoystickFlagged(JoystickFlags::invertY) ? -y() : y()) + 1.0f) /
+ 2.0f * m_yAnimation->durationSeconds());
}
}