diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt index f18d8a04..45cb9e51 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt @@ -1,9 +1,11 @@ package com.margelo.nitro.rive +import android.util.Log import androidx.annotation.Keep import com.facebook.proguard.annotations.DoNotStrip import com.facebook.react.uimanager.ThemedReactContext import com.margelo.nitro.core.Promise +import com.rive.BindData import com.rive.RiveReactNativeView import com.rive.ViewConfiguration import app.rive.runtime.kotlin.core.Fit as RiveFit @@ -11,8 +13,30 @@ import app.rive.runtime.kotlin.core.Alignment as RiveAlignment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +fun Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName?.toBindData(): BindData { + if (this == null) return BindData.Auto + + return when (this) { + is Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.First -> { + val instance = (this.asFirstOrNull() as? HybridViewModelInstance)?.viewModelInstance + ?: throw Error("Invalid ViewModelInstance") + BindData.Instance(instance) + } + is Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.Second -> { + when (this.asSecondOrNull()) { + DataBindMode.AUTO -> BindData.Auto + DataBindMode.NONE -> BindData.None + else -> BindData.None + } + } + is Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.Third -> { + val name = this.asThirdOrNull()?.byName ?: throw Error("Missing byName value") + BindData.ByName(name) + } + } +} + object DefaultConfiguration { - const val AUTOBIND = false const val AUTOPLAY = true val FIT = RiveFit.CONTAIN val ALIGNMENT = RiveAlignment.CENTER @@ -25,6 +49,8 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { //region State override val view: RiveReactNativeView = RiveReactNativeView(context) private var needsReload = false + private var dataBindingChanged = false + private var initialUpdate = true private var registeredFile: HybridRiveFile? = null //endregion @@ -41,10 +67,6 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { set(value) { changed(field, value) { field = it } } - override var autoBind: Boolean? = null - set(value) { - changed(field, value) { field = it } - } override var file: HybridRiveFileSpec = HybridRiveFile() set(value) { if (field != value) { @@ -56,6 +78,13 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { override var alignment: Alignment? = null override var fit: Fit? = null override var layoutScaleFactor: Double? = null + override var dataBind: Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName? = null + set(value) { + if (field != value) { + field = value + dataBindingChanged = true + } + } //endregion //region View Methods @@ -73,6 +102,11 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { view.bindViewModelInstance(hybridVmi.viewModelInstance) } + override fun getViewModelInstance(): HybridViewModelInstanceSpec? { + val viewModelInstance = view.getViewModelInstance() ?: return null + return HybridViewModelInstance(viewModelInstance) + } + override fun play() = executeOnUiThread { view.play() } override fun pause() = executeOnUiThread { view.pause() } @@ -109,28 +143,32 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { } override fun afterUpdate() { - val hybridFile = file as? HybridRiveFile - val riveFile = hybridFile?.riveFile ?: return - - val config = ViewConfiguration( - artboardName = artboardName, - stateMachineName = stateMachineName, - autoPlay = autoPlay ?: DefaultConfiguration.AUTOPLAY, - autoBind = autoBind ?: DefaultConfiguration.AUTOBIND, - riveFile = riveFile, - alignment = convertAlignment(alignment) ?: DefaultConfiguration.ALIGNMENT, - fit = convertFit(fit) ?: DefaultConfiguration.FIT, - layoutScaleFactor = layoutScaleFactor?.toFloat() ?: DefaultConfiguration.LAYOUTSCALEFACTOR, - ) - view.configure(config, needsReload) - - if (needsReload && hybridFile != null) { - hybridFile.registerView(this) - registeredFile = hybridFile - } - - needsReload = false - super.afterUpdate() + logged(TAG, "afterUpdate") { + val hybridFile = file as? HybridRiveFile + val riveFile = hybridFile?.riveFile ?: return@logged + + val config = ViewConfiguration( + artboardName = artboardName, + stateMachineName = stateMachineName, + autoPlay = autoPlay ?: DefaultConfiguration.AUTOPLAY, + riveFile = riveFile, + alignment = convertAlignment(alignment) ?: DefaultConfiguration.ALIGNMENT, + fit = convertFit(fit) ?: DefaultConfiguration.FIT, + layoutScaleFactor = layoutScaleFactor?.toFloat() ?: DefaultConfiguration.LAYOUTSCALEFACTOR, + bindData = dataBind.toBindData() + ) + view.configure(config, dataBindingChanged=dataBindingChanged, needsReload, initialUpdate= initialUpdate) + + if (needsReload && hybridFile != null) { + hybridFile.registerView(this) + registeredFile = hybridFile + } + + needsReload = false + dataBindingChanged = false + initialUpdate = false + super.afterUpdate() + } } //endregion @@ -184,5 +222,14 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { Fit.LAYOUT -> RiveFit.LAYOUT } } + + fun logged(tag: String, note: String? = null, fn: () -> Unit) { + try { + fn() + } catch (e: Exception) { + // TODO add onError callback + Log.e("[RIVE]", "$tag ${note ?: ""} $e") + } + } //endregion } diff --git a/android/src/main/java/com/rive/RiveReactNativeView.kt b/android/src/main/java/com/rive/RiveReactNativeView.kt index ee9d62ce..3b08e383 100644 --- a/android/src/main/java/com/rive/RiveReactNativeView.kt +++ b/android/src/main/java/com/rive/RiveReactNativeView.kt @@ -14,6 +14,7 @@ import app.rive.runtime.kotlin.core.SMIBoolean import app.rive.runtime.kotlin.core.SMIInput import app.rive.runtime.kotlin.core.SMINumber import app.rive.runtime.kotlin.core.ViewModelInstance +import app.rive.runtime.kotlin.core.errors.ViewModelException import com.margelo.nitro.core.AnyMap import com.margelo.nitro.rive.EventPropertiesOutput import com.margelo.nitro.rive.EventPropertiesOutputExtensions as EPO @@ -21,20 +22,27 @@ import com.margelo.nitro.rive.RiveEventType import com.margelo.nitro.rive.UnifiedRiveEvent as RNEvent import kotlinx.coroutines.CompletableDeferred +sealed class BindData { + data object None : BindData() + data object Auto : BindData() + data class Instance(val instance: ViewModelInstance) : BindData() + data class ByName(val name: String) : BindData() +} + data class ViewConfiguration( val artboardName: String?, val stateMachineName: String?, - val autoBind: Boolean, val autoPlay: Boolean, val riveFile: File, val alignment: Alignment, val fit: Fit, - val layoutScaleFactor: Float? + val layoutScaleFactor: Float?, + val bindData: BindData ) @SuppressLint("ViewConstructor") class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { - private var riveAnimationView: RiveAnimationView? = null + internal var riveAnimationView: RiveAnimationView? = null private var eventListeners: MutableList = mutableListOf() private val viewReadyDeferred = CompletableDeferred() private var _activeStateMachineName: String? = null @@ -49,14 +57,14 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { return viewReadyDeferred.await() } - fun configure(config: ViewConfiguration, reload: Boolean = false) { + fun configure(config: ViewConfiguration, dataBindingChanged: Boolean, reload: Boolean = false, initialUpdate: Boolean = false) { if (reload) { riveAnimationView?.setRiveFile( config.riveFile, artboardName = config.artboardName, stateMachineName = config.stateMachineName, autoplay = config.autoPlay, - autoBind = config.autoBind, + autoBind = config.bindData is BindData.Auto, alignment = config.alignment, fit = config.fit ) @@ -67,6 +75,11 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { // TODO: this seems to require a reload for the view to take the new value (bug on Android) riveAnimationView?.layoutScaleFactor = config.layoutScaleFactor } + + if (dataBindingChanged || initialUpdate) { + applyDataBinding(config.bindData) + } + viewReadyDeferred.complete(true) } @@ -77,6 +90,58 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { } } + fun getViewModelInstance(): ViewModelInstance? { + val stateMachines = riveAnimationView?.controller?.stateMachines + return if (!stateMachines.isNullOrEmpty()) { + stateMachines.first().viewModelInstance + } else { + null + } + } + + fun applyDataBinding(bindData: BindData) { + val stateMachines = riveAnimationView?.controller?.stateMachines + if (stateMachines.isNullOrEmpty()) return + + val stateMachine = stateMachines.first() + + when (bindData) { + is BindData.None -> { + stateMachine.viewModelInstance = null + } + is BindData.Auto -> { + val artboard = riveAnimationView?.controller?.activeArtboard + val file = riveAnimationView?.controller?.file + if (artboard != null && file != null) { + try { + file.defaultViewModelForArtboard(artboard) + } catch (e: ViewModelException) { + null + }?.let { + val instance = it.createDefaultInstance() + stateMachine.viewModelInstance = instance + } + } + } + is BindData.Instance -> { + stateMachine.viewModelInstance = bindData.instance + } + is BindData.ByName -> { + val artboard = riveAnimationView?.controller?.activeArtboard + val file = riveAnimationView?.controller?.file + if (artboard != null && file != null) { + val viewModel = file.defaultViewModelForArtboard(artboard) + val instance = viewModel.createInstanceFromName(bindData.name) + stateMachine.viewModelInstance = instance + } + } + } + + stateMachine.name.let { smName -> + riveAnimationView?.play(smName, isStateMachine = true) + } + } + fun play() = riveAnimationView?.play() fun pause() = riveAnimationView?.pause(); diff --git a/example/assets/rive/many_viewmodels.riv b/example/assets/rive/many_viewmodels.riv new file mode 100644 index 00000000..db36b442 Binary files /dev/null and b/example/assets/rive/many_viewmodels.riv differ diff --git a/example/src/pages/ManyViewModels.tsx b/example/src/pages/ManyViewModels.tsx new file mode 100644 index 00000000..4423e456 --- /dev/null +++ b/example/src/pages/ManyViewModels.tsx @@ -0,0 +1,168 @@ +import { StyleSheet, View, Text, TouchableOpacity } from 'react-native'; +import { useState, useMemo } from 'react'; +import type { Metadata } from '../helpers/metadata'; +import { + DataBindMode, + RiveView, + useRiveFile, + type ViewModelInstance, +} from 'react-native-rive'; + +type BindModeOption = + | 'none' + | 'auto' + | 'red' + | 'green' + | 'blue' + | 'green-instance'; + +type BindModeSelectorProps = { + selectedMode: BindModeOption; + onModeChange: (mode: BindModeOption) => void; +}; + +function BindModeSelector({ + selectedMode, + onModeChange, +}: BindModeSelectorProps) { + const modes: BindModeOption[] = [ + 'none', + 'auto', + 'red', + 'green', + 'blue', + 'green-instance', + ]; + + return ( + + Binding Mode: + + {modes.map((mode) => ( + onModeChange(mode)} + > + + {mode === 'green-instance' ? 'green (instance)' : mode} + + + ))} + + + ); +} + +function getDataBindValue( + mode: BindModeOption, + greenInstance: ViewModelInstance | null +) { + if (mode === 'none') return DataBindMode.None; + if (mode === 'auto') return DataBindMode.Auto; + if (mode === 'green-instance' && greenInstance) return greenInstance; + return { byName: mode }; +} + +export default function ManyViewModels() { + const { riveFile } = useRiveFile( + require('../../assets/rive/many_viewmodels.riv') + ); + const [bindMode, setBindMode] = useState('none'); + + // Create a ViewModelInstance for "green" to demonstrate instance binding + const greenInstance = useMemo(() => { + if (!riveFile) return null; + try { + const viewModel = riveFile.defaultArtboardViewModel(); + if (!viewModel) return null; + return viewModel.createInstanceByName('green'); + } catch (e) { + console.error('Failed to create green instance:', e); + return null; + } + }, [riveFile]); + + const dataBindValue = getDataBindValue(bindMode, greenInstance); + + return ( + + + {riveFile && ( + + )} + + ); +} + +ManyViewModels.metadata = { + name: 'Select View Model', + description: + 'Interactive data binding mode selector (none, auto, byName, and instance)', +} satisfies Metadata; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + }, + rive: { + flex: 1, + width: '100%', + height: '100%', + }, +}); + +const selectorStyles = StyleSheet.create({ + container: { + padding: 16, + backgroundColor: '#f8f8f8', + borderBottomWidth: 1, + borderBottomColor: '#e0e0e0', + }, + label: { + fontSize: 14, + fontWeight: '600', + marginBottom: 8, + color: '#333', + }, + buttonRow: { + flexDirection: 'row', + gap: 8, + flexWrap: 'wrap', + }, + button: { + paddingHorizontal: 16, + paddingVertical: 8, + backgroundColor: '#fff', + borderRadius: 8, + borderWidth: 1, + borderColor: '#d0d0d0', + }, + buttonActive: { + backgroundColor: '#007AFF', + borderColor: '#007AFF', + }, + buttonText: { + fontSize: 14, + fontWeight: '500', + color: '#333', + textTransform: 'capitalize', + }, + buttonTextActive: { + color: '#fff', + }, +}); diff --git a/example/src/pages/RiveDataBindingExample.tsx b/example/src/pages/RiveDataBindingExample.tsx index b0236340..b72b66c6 100644 --- a/example/src/pages/RiveDataBindingExample.tsx +++ b/example/src/pages/RiveDataBindingExample.tsx @@ -3,7 +3,6 @@ import { useEffect, useMemo } from 'react'; import { Fit, RiveView, - useRive, useRiveNumber, type ViewModelInstance, type RiveFile, @@ -61,14 +60,6 @@ function DataBindingExample({ instance: ViewModelInstance; file: RiveFile; }) { - const { riveViewRef, setHybridRef } = useRive(); - - useEffect(() => { - if (riveViewRef) { - riveViewRef.bindViewModelInstance(instance); - } - }, [riveViewRef, instance]); - const { error: coinValueError } = useRiveNumber('Coin/Item_Value', instance); if (coinValueError) { @@ -104,12 +95,11 @@ function DataBindingExample({ return ( ); } diff --git a/example/src/pages/RiveEventsExample.tsx b/example/src/pages/RiveEventsExample.tsx index 2e421d69..5284914b 100644 --- a/example/src/pages/RiveEventsExample.tsx +++ b/example/src/pages/RiveEventsExample.tsx @@ -46,7 +46,6 @@ export default function EventsExample() { ) : riveFile ? ( { ) : riveFile ? ( ; - -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { - const backgroundColor = useThemeColor({}, 'background'); - const colorScheme = useColorScheme() ?? 'light'; - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollOffset(scrollRef); - const headerAnimatedStyle = useAnimatedStyle(() => { - return { - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] - ), - }, - { - scale: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [2, 1, 1] - ), - }, - ], - }; - }); - - return ( - - - {headerImage} - - {children} - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: HEADER_HEIGHT, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/ios/HybridRiveView.swift b/ios/HybridRiveView.swift index c4abac0e..3cd95eee 100644 --- a/ios/HybridRiveView.swift +++ b/ios/HybridRiveView.swift @@ -1,21 +1,54 @@ import Foundation -import UIKit -import RiveRuntime import NitroModules +import RiveRuntime +import UIKit private struct DefaultConfiguration { - static let autoBind = false static let autoPlay = true static let alignment = RiveAlignment.center static let fit = RiveFit.contain static let layoutScaleFactor = RiveRuntime.RiveViewModel.layoutScaleFactorAutomatic } -class HybridRiveView : HybridRiveViewSpec { +typealias HybridDataBindMode = Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName + +extension Optional +where Wrapped == HybridDataBindMode { + func toDataBingMode() throws -> BindData { + guard let value = self else { + return .auto + } + + switch value { + case .first(let viewModelInstance): + if let instance = (viewModelInstance as? HybridViewModelInstance)?.viewModelInstance { + return .instance(instance) + } else { + throw RuntimeError.error(withMessage: "Invalid ViewModelInstance") + } + case .second(let mode): + switch mode { + case .auto: + return .auto + case .none: + return .none + } + case .third(let dataBindByName): + return .byName(dataBindByName.byName) + } + } +} + +class HybridRiveView: HybridRiveViewSpec { // MARK: View Props + var dataBind: HybridDataBindMode? = nil { + didSet { + dataBindingChanged = true + } + } + var artboardName: String? { didSet { needsReload = true } } var stateMachineName: String? { didSet { needsReload = true } } - var autoBind: Bool? { didSet { needsReload = true } } var autoPlay: Bool? { didSet { needsReload = true } } var file: (any HybridRiveFileSpec) = HybridRiveFile() { didSet { needsReload = true } @@ -23,55 +56,62 @@ class HybridRiveView : HybridRiveViewSpec { var alignment: Alignment? var fit: Fit? var layoutScaleFactor: Double? - + func awaitViewReady() throws -> Promise { return Promise.async { [self] in return try await getRiveView().awaitViewReady() } } - + func bindViewModelInstance(viewModelInstance: (any HybridViewModelInstanceSpec)) throws { - guard let viewModelInstance = (viewModelInstance as? HybridViewModelInstance)?.viewModelInstance else { return } + guard let viewModelInstance = (viewModelInstance as? HybridViewModelInstance)?.viewModelInstance + else { return } try getRiveView().bindViewModelInstance(viewModelInstance: viewModelInstance) } - + + func getViewModelInstance() throws -> (any HybridViewModelInstanceSpec)? { + guard let viewModelInstance = try getRiveView().getViewModelInstance() else { return nil } + return HybridViewModelInstance(viewModelInstance: viewModelInstance) + } + func play() throws { try getRiveView().play() } - + func pause() throws { try getRiveView().pause() } - + func onEventListener(onEvent: @escaping (UnifiedRiveEvent) -> Void) throws { try getRiveView().addEventListener(onEvent) } - + func removeEventListeners() throws { try getRiveView().removeEventListeners() } - + func setNumberInputValue(name: String, value: Double, path: String?) throws { try getRiveView().setNumberInputValue(name: name, value: Float(value), path: path) } - + func getNumberInputValue(name: String, path: String?) throws -> Double { return try Double(getRiveView().getNumberInputValue(name: name, path: path)) } - + func setBooleanInputValue(name: String, value: Bool, path: String?) throws { try getRiveView().setBooleanInputValue(name: name, value: value, path: path) } - + func getBooleanInputValue(name: String, path: String?) throws -> Bool { - return try getRiveView().getBooleanInputValue(name: name, path: path) } - + return try getRiveView().getBooleanInputValue(name: name, path: path) + } + func triggerInput(name: String, path: String?) throws { try getRiveView().triggerInput(name: name, path: path) } - + func setTextRunValue(name: String, value: String, path: String?) throws { try getRiveView().setTextRunValue(name: name, value: value, path: path) } - + func getTextRunValue(name: String, path: String?) throws -> String { return try getRiveView().getTextRunValue(name: name, path: path) } - + // MARK: Views var view: UIView = RiveReactNativeView() func getRiveView() throws -> RiveReactNativeView { @@ -80,35 +120,45 @@ class HybridRiveView : HybridRiveViewSpec { } return riveView } - + // MARK: Update func afterUpdate() { - guard let hybridFile = file as? HybridRiveFile, - let file = hybridFile.riveFile else { return } - - let config = ViewConfiguration( - artboardName: artboardName, - stateMachineName: stateMachineName, - autoBind: autoBind ?? DefaultConfiguration.autoBind, - autoPlay: autoPlay ?? DefaultConfiguration.autoPlay, - riveFile: file, - viewSource: hybridFile, - alignment: convertAlignment(alignment) ?? DefaultConfiguration.alignment, - fit: convertFit(fit) ?? DefaultConfiguration.fit, - layoutScaleFactor: layoutScaleFactor ?? DefaultConfiguration.layoutScaleFactor - ) - - try? getRiveView().configure(config, reload: needsReload) - needsReload = false - } - + logged(tag: "HybridRiveView", note: "afterUpdate") { + guard let hybridFile = file as? HybridRiveFile, + let file = hybridFile.riveFile + else { return } + + let config = ViewConfiguration( + artboardName: artboardName, + stateMachineName: stateMachineName, + autoPlay: autoPlay ?? DefaultConfiguration.autoPlay, + riveFile: file, + viewSource: hybridFile, + alignment: convertAlignment(alignment) ?? DefaultConfiguration.alignment, + fit: convertFit(fit) ?? DefaultConfiguration.fit, + layoutScaleFactor: layoutScaleFactor ?? DefaultConfiguration.layoutScaleFactor, + bindData: try dataBind.toDataBingMode() + ) + + let riveView = try getRiveView() + riveView.configure( + config, dataBindingChanged: dataBindingChanged, reload: needsReload, + initialUpdate: initialUpdate) + needsReload = false + dataBindingChanged = false + initialUpdate = false + } + } + // MARK: Internal State private var needsReload = false - + private var dataBindingChanged = false + private var initialUpdate = true + // MARK: Helpers private func convertAlignment(_ alignment: Alignment?) -> RiveAlignment? { guard let alignment = alignment else { return nil } - + switch alignment { case .topleft: return .topLeft case .topcenter: return .topCenter @@ -121,10 +171,10 @@ class HybridRiveView : HybridRiveViewSpec { case .bottomright: return .bottomRight } } - + private func convertFit(_ fit: Fit?) -> RiveFit? { guard let fit = fit else { return nil } - + switch fit { case .fill: return .fill case .contain: return .contain diff --git a/ios/RiveReactNativeView.swift b/ios/RiveReactNativeView.swift index 18a04acb..3c99e8e2 100644 --- a/ios/RiveReactNativeView.swift +++ b/ios/RiveReactNativeView.swift @@ -1,22 +1,29 @@ -import UIKit -import RiveRuntime import NitroModules +import RiveRuntime +import UIKit protocol RiveViewSource: AnyObject { func registerView(_ view: RiveReactNativeView) func unregisterView(_ view: RiveReactNativeView) } +enum BindData { + case none + case auto + case instance(RiveDataBindingViewModel.Instance) + case byName(String) +} + struct ViewConfiguration { let artboardName: String? let stateMachineName: String? - let autoBind: Bool let autoPlay: Bool let riveFile: RiveFile let viewSource: RiveViewSource? let alignment: RiveRuntime.RiveAlignment let fit: RiveRuntime.RiveFit let layoutScaleFactor: Double + let bindData: BindData } class RiveReactNativeView: UIView, RiveStateMachineDelegate { @@ -30,10 +37,10 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { // MARK: Public Config Properties var autoPlay: Bool = true - + // MARK: - Public Methods - - func awaitViewReady()async -> Bool { + + func awaitViewReady() async -> Bool { if !isViewReady { await withCheckedContinuation { continuation in viewReadyContinuation = continuation @@ -42,8 +49,8 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { } return true } - - func configure(_ config: ViewConfiguration, reload: Bool = false) { + + func configure(_ config: ViewConfiguration, dataBindingChanged: Bool = false, reload: Bool = false, initialUpdate: Bool = false) { if reload { cleanup() let model = RiveModel(riveFile: config.riveFile) @@ -66,16 +73,56 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { viewReadyContinuation?.resume() viewReadyContinuation = nil } + + if dataBindingChanged || initialUpdate { + applyDataBinding(config.bindData) + } } - + func bindViewModelInstance(viewModelInstance: RiveDataBindingViewModel.Instance) { baseViewModel?.riveModel?.stateMachine?.bind(viewModelInstance: viewModelInstance) } - + + func getViewModelInstance() -> RiveDataBindingViewModel.Instance? { + return baseViewModel?.riveModel?.stateMachine?.viewModelInstance + } + + func applyDataBinding(_ bindData: BindData) { + let stateMachine = baseViewModel?.riveModel?.stateMachine + let artboard = baseViewModel?.riveModel?.artboard + + switch bindData { + case .none: + baseViewModel?.riveModel?.disableAutoBind() + + case .auto: + baseViewModel?.riveModel?.enableAutoBind { instance in + // Auto-bind callback + } + + case .byName(let name): + guard let artboard = artboard, + let riveFile = baseViewModel?.riveModel?.riveFile, + let viewModel = riveFile.defaultViewModel(for: artboard), + let instance = viewModel.createInstance(fromName: name) + else { + return + } + stateMachine?.bind(viewModelInstance: instance) + // this should be added if we support only playing artboards on their own - https://github.com/rive-app/rive-nitro-react-native/pull/23#discussion_r2534698281 + // artboard.bind(viewModelInstance: instance) + + case .instance(let instance): + stateMachine?.bind(viewModelInstance: instance) + artboard?.bind(viewModelInstance: instance) + } + baseViewModel?.play() + } + func play() { baseViewModel?.play() } - + func pause() { baseViewModel?.pause() } @@ -89,74 +136,76 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { func addEventListener(_ onEvent: @escaping (UnifiedRiveEvent) -> Void) { eventListeners.append(onEvent) } - + func removeEventListeners() { eventListeners.removeAll() } - + func setNumberInputValue(name: String, value: Float, path: String?) throws { try handleInput(name: name, path: path, type: .number) { (input: RiveRuntime.RiveSMINumber) in input.setValue(value) } } - + func getNumberInputValue(name: String, path: String?) throws -> Float { try handleInput(name: name, path: path, type: .number) { (input: RiveRuntime.RiveSMINumber) in input.value() } } - + func setBooleanInputValue(name: String, value: Bool, path: String?) throws { try handleInput(name: name, path: path, type: .boolean) { (input: RiveRuntime.RiveSMIBool) in input.setValue(value) } } - + func getBooleanInputValue(name: String, path: String?) throws -> Bool { try handleInput(name: name, path: path, type: .boolean) { (input: RiveRuntime.RiveSMIBool) in input.value() } } - + func triggerInput(name: String, path: String?) throws { try handleInput(name: name, path: path, type: .trigger) { (input: RiveRuntime.RiveSMITrigger) in input.fire() } } - + func setTextRunValue(name: String, value: String, path: String?) throws { let textRun = try textRunOptionPath(name: name, path: path) textRun.setText(value) } - + func getTextRunValue(name: String, path: String?) throws -> String { let textRun = try textRunOptionPath(name: name, path: path) return textRun.text() } - - private func textRunOptionPath(name: String, path: String?) throws -> RiveRuntime.RiveTextValueRun { + + private func textRunOptionPath(name: String, path: String?) throws -> RiveRuntime.RiveTextValueRun + { let textRun: RiveRuntime.RiveTextValueRun? if let path = path { textRun = baseViewModel?.riveModel?.artboard?.textRun(name, path: path) } else { textRun = baseViewModel?.riveModel?.artboard?.textRun(name) } - + guard let textRun = textRun else { - throw RuntimeError.error(withMessage: "Could not find text run `\(name)`\(path.map { " at path `\($0)`" } ?? "")") + throw RuntimeError.error( + withMessage: "Could not find text run `\(name)`\(path.map { " at path `\($0)`" } ?? "")") } - + return textRun } - + // MARK: - Internal deinit { cleanup() } - + private func createViewFromViewModel() { riveView = baseViewModel?.createRiveView() - + if let riveView = riveView { riveView.translatesAutoresizingMaskIntoConstraints = false addSubview(riveView) @@ -165,11 +214,11 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { riveView.leadingAnchor.constraint(equalTo: leadingAnchor), riveView.trailingAnchor.constraint(equalTo: trailingAnchor), riveView.topAnchor.constraint(equalTo: topAnchor), - riveView.bottomAnchor.constraint(equalTo: bottomAnchor) + riveView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } } - + private func cleanup() { riveView?.removeFromSuperview() riveView?.stateMachineDelegate = nil @@ -180,27 +229,30 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { self.viewSource = nil } } - + @objc func onRiveEventReceived(onRiveEvent riveEvent: RiveRuntime.RiveEvent) { let eventType = UnifiedRiveEvent( name: riveEvent.name(), - type: riveEvent is RiveRuntime.RiveOpenUrlEvent ? RiveEventType.openurl : RiveEventType.general, + type: riveEvent is RiveRuntime.RiveOpenUrlEvent + ? RiveEventType.openurl : RiveEventType.general, delay: Double(riveEvent.delay()), properties: convertEventProperties(riveEvent.properties()), url: (riveEvent as? RiveRuntime.RiveOpenUrlEvent)?.url(), target: (riveEvent as? RiveRuntime.RiveOpenUrlEvent)?.target() ) - + for listener in eventListeners { listener(eventType) } } - - private func convertEventProperties(_ properties: Dictionary?) -> Dictionary?{ + + private func convertEventProperties(_ properties: [String: Any]?) -> [String: + EventPropertiesOutput]? + { guard let properties = properties else { return nil } - - var newMap: Dictionary = [:] - + + var newMap: [String: EventPropertiesOutput] = [:] + for (key, value) in properties { if let string = value as? String { newMap[key] = .string(string) @@ -210,17 +262,19 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { newMap[key] = .boolean(boolean) } } - + return newMap } - + private enum InputType { case number case boolean case trigger } - - private func handleInput(name: String, path: String?, type: InputType, onSuccess: (P) throws -> T) throws -> T { + + private func handleInput( + name: String, path: String?, type: InputType, onSuccess: (P) throws -> T + ) throws -> T { let input: P? if let path = path { switch type { @@ -241,11 +295,12 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { input = baseViewModel?.riveModel?.stateMachine?.getTrigger(name) as? P } } - + guard let input = input else { - throw RuntimeError.error(withMessage: "Could not find input `\(name)`\(path.map { " at path `\($0)`" } ?? "")") + throw RuntimeError.error( + withMessage: "Could not find input `\(name)`\(path.map { " at path `\($0)`" } ?? "")") } - + return try onSuccess(input) } } diff --git a/ios/Utils.swift b/ios/Utils.swift index ad8ed6a8..f5ef2bbc 100644 --- a/ios/Utils.swift +++ b/ios/Utils.swift @@ -13,3 +13,12 @@ final class SendableRef: @unchecked Sendable { self.value = value } } + +/// Executes a closure, logging any thrown error with an optional note and tag using RCTLogError. +func logged(tag: String, note: String? = nil, _ fn: () throws -> Void) { + do { + return try fn() + } catch (let e) { + RCTLogError("[RIVE] \(tag) \(note ?? "") \(e)") + } +} diff --git a/nitrogen/generated/android/c++/JDataBindByName.hpp b/nitrogen/generated/android/c++/JDataBindByName.hpp new file mode 100644 index 00000000..b2a0c367 --- /dev/null +++ b/nitrogen/generated/android/c++/JDataBindByName.hpp @@ -0,0 +1,57 @@ +/// +/// JDataBindByName.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "DataBindByName.hpp" + +#include + +namespace margelo::nitro::rive { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "DataBindByName" and the the Kotlin data class "DataBindByName". + */ + struct JDataBindByName final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/DataBindByName;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct DataBindByName by copying all values to C++. + */ + [[maybe_unused]] + [[nodiscard]] + DataBindByName toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldByName = clazz->getField("byName"); + jni::local_ref byName = this->getFieldValue(fieldByName); + return DataBindByName( + byName->toStdString() + ); + } + + public: + /** + * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java. + */ + [[maybe_unused]] + static jni::local_ref fromCpp(const DataBindByName& value) { + using JSignature = JDataBindByName(jni::alias_ref); + static const auto clazz = javaClassStatic(); + static const auto create = clazz->getStaticMethod("fromCpp"); + return create( + clazz, + jni::make_jstring(value.byName) + ); + } + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JDataBindMode.hpp b/nitrogen/generated/android/c++/JDataBindMode.hpp new file mode 100644 index 00000000..cde6f8b1 --- /dev/null +++ b/nitrogen/generated/android/c++/JDataBindMode.hpp @@ -0,0 +1,59 @@ +/// +/// JDataBindMode.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include "DataBindMode.hpp" + +namespace margelo::nitro::rive { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ enum "DataBindMode" and the the Kotlin enum "DataBindMode". + */ + struct JDataBindMode final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/DataBindMode;"; + + public: + /** + * Convert this Java/Kotlin-based enum to the C++ enum DataBindMode. + */ + [[maybe_unused]] + [[nodiscard]] + DataBindMode toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldOrdinal = clazz->getField("value"); + int ordinal = this->getFieldValue(fieldOrdinal); + return static_cast(ordinal); + } + + public: + /** + * Create a Java/Kotlin-based enum with the given C++ enum's value. + */ + [[maybe_unused]] + static jni::alias_ref fromCpp(DataBindMode value) { + static const auto clazz = javaClassStatic(); + static const auto fieldAUTO = clazz->getStaticField("AUTO"); + static const auto fieldNONE = clazz->getStaticField("NONE"); + + switch (value) { + case DataBindMode::AUTO: + return clazz->getStaticFieldValue(fieldAUTO); + case DataBindMode::NONE: + return clazz->getStaticFieldValue(fieldNONE); + default: + std::string stringValue = std::to_string(static_cast(value)); + throw std::invalid_argument("Invalid enum value (" + stringValue + "!"); + } + } + }; + +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JHybridRiveViewSpec.cpp b/nitrogen/generated/android/c++/JHybridRiveViewSpec.cpp index b105873a..6ac225d0 100644 --- a/nitrogen/generated/android/c++/JHybridRiveViewSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridRiveViewSpec.cpp @@ -15,6 +15,10 @@ namespace margelo::nitro::rive { enum class Alignment; } namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridViewModelInstanceSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridViewModelInstanceSpec; } +// Forward declaration of `DataBindMode` to properly resolve imports. +namespace margelo::nitro::rive { enum class DataBindMode; } +// Forward declaration of `DataBindByName` to properly resolve imports. +namespace margelo::nitro::rive { struct DataBindByName; } // Forward declaration of `UnifiedRiveEvent` to properly resolve imports. namespace margelo::nitro::rive { struct UnifiedRiveEvent; } // Forward declaration of `RiveEventType` to properly resolve imports. @@ -29,17 +33,22 @@ namespace margelo::nitro::rive { enum class RiveEventType; } #include "JAlignment.hpp" #include "Fit.hpp" #include "JFit.hpp" -#include -#include #include "HybridViewModelInstanceSpec.hpp" +#include "DataBindMode.hpp" +#include "DataBindByName.hpp" +#include +#include "JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp" #include "JHybridViewModelInstanceSpec.hpp" +#include "JDataBindMode.hpp" +#include "JDataBindByName.hpp" +#include +#include #include "UnifiedRiveEvent.hpp" #include #include "JFunc_void_UnifiedRiveEvent.hpp" #include "JUnifiedRiveEvent.hpp" #include "RiveEventType.hpp" #include "JRiveEventType.hpp" -#include #include #include "JEventPropertiesOutput.hpp" @@ -90,15 +99,6 @@ namespace margelo::nitro::rive { static const auto method = javaClassStatic()->getMethod /* stateMachineName */)>("setStateMachineName"); method(_javaPart, stateMachineName.has_value() ? jni::make_jstring(stateMachineName.value()) : nullptr); } - std::optional JHybridRiveViewSpec::getAutoBind() { - static const auto method = javaClassStatic()->getMethod()>("getAutoBind"); - auto __result = method(_javaPart); - return __result != nullptr ? std::make_optional(static_cast(__result->value())) : std::nullopt; - } - void JHybridRiveViewSpec::setAutoBind(std::optional autoBind) { - static const auto method = javaClassStatic()->getMethod /* autoBind */)>("setAutoBind"); - method(_javaPart, autoBind.has_value() ? jni::JBoolean::valueOf(autoBind.value()) : nullptr); - } std::optional JHybridRiveViewSpec::getAutoPlay() { static const auto method = javaClassStatic()->getMethod()>("getAutoPlay"); auto __result = method(_javaPart); @@ -144,6 +144,15 @@ namespace margelo::nitro::rive { static const auto method = javaClassStatic()->getMethod /* layoutScaleFactor */)>("setLayoutScaleFactor"); method(_javaPart, layoutScaleFactor.has_value() ? jni::JDouble::valueOf(layoutScaleFactor.value()) : nullptr); } + std::optional, DataBindMode, DataBindByName>> JHybridRiveViewSpec::getDataBind() { + static const auto method = javaClassStatic()->getMethod()>("getDataBind"); + auto __result = method(_javaPart); + return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt; + } + void JHybridRiveViewSpec::setDataBind(const std::optional, DataBindMode, DataBindByName>>& dataBind) { + static const auto method = javaClassStatic()->getMethod /* dataBind */)>("setDataBind"); + method(_javaPart, dataBind.has_value() ? JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName::fromCpp(dataBind.value()) : nullptr); + } // Methods std::shared_ptr> JHybridRiveViewSpec::awaitViewReady() { @@ -166,6 +175,11 @@ namespace margelo::nitro::rive { static const auto method = javaClassStatic()->getMethod /* viewModelInstance */)>("bindViewModelInstance"); method(_javaPart, std::dynamic_pointer_cast(viewModelInstance)->getJavaPart()); } + std::optional> JHybridRiveViewSpec::getViewModelInstance() { + static const auto method = javaClassStatic()->getMethod()>("getViewModelInstance"); + auto __result = method(_javaPart); + return __result != nullptr ? std::make_optional(__result->cthis()->shared_cast()) : std::nullopt; + } void JHybridRiveViewSpec::play() { static const auto method = javaClassStatic()->getMethod("play"); method(_javaPart); diff --git a/nitrogen/generated/android/c++/JHybridRiveViewSpec.hpp b/nitrogen/generated/android/c++/JHybridRiveViewSpec.hpp index 77741f6b..7ffd4db1 100644 --- a/nitrogen/generated/android/c++/JHybridRiveViewSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridRiveViewSpec.hpp @@ -54,8 +54,6 @@ namespace margelo::nitro::rive { void setArtboardName(const std::optional& artboardName) override; std::optional getStateMachineName() override; void setStateMachineName(const std::optional& stateMachineName) override; - std::optional getAutoBind() override; - void setAutoBind(std::optional autoBind) override; std::optional getAutoPlay() override; void setAutoPlay(std::optional autoPlay) override; std::shared_ptr getFile() override; @@ -66,11 +64,14 @@ namespace margelo::nitro::rive { void setFit(std::optional fit) override; std::optional getLayoutScaleFactor() override; void setLayoutScaleFactor(std::optional layoutScaleFactor) override; + std::optional, DataBindMode, DataBindByName>> getDataBind() override; + void setDataBind(const std::optional, DataBindMode, DataBindByName>>& dataBind) override; public: // Methods std::shared_ptr> awaitViewReady() override; void bindViewModelInstance(const std::shared_ptr& viewModelInstance) override; + std::optional> getViewModelInstance() override; void play() override; void pause() override; void onEventListener(const std::function& onEvent) override; diff --git a/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.cpp b/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.cpp new file mode 100644 index 00000000..03eac109 --- /dev/null +++ b/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.cpp @@ -0,0 +1,30 @@ +/// +/// JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp" + +namespace margelo::nitro::rive { + /** + * Converts JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName to std::variant, DataBindMode, DataBindByName> + */ + std::variant, DataBindMode, DataBindByName> JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName::toCpp() const { + if (isInstanceOf(JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName_impl::First::javaClassStatic())) { + // It's a `std::shared_ptr` + auto jniValue = static_cast(this)->getValue(); + return jniValue->cthis()->shared_cast(); + } else if (isInstanceOf(JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName_impl::Second::javaClassStatic())) { + // It's a `DataBindMode` + auto jniValue = static_cast(this)->getValue(); + return jniValue->toCpp(); + } else if (isInstanceOf(JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName_impl::Third::javaClassStatic())) { + // It's a `DataBindByName` + auto jniValue = static_cast(this)->getValue(); + return jniValue->toCpp(); + } + throw std::invalid_argument("Variant is unknown Kotlin instance!"); + } +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp b/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp new file mode 100644 index 00000000..938e427e --- /dev/null +++ b/nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp @@ -0,0 +1,90 @@ +/// +/// JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include "HybridViewModelInstanceSpec.hpp" +#include "DataBindMode.hpp" +#include "DataBindByName.hpp" +#include +#include "JHybridViewModelInstanceSpec.hpp" +#include "JDataBindMode.hpp" +#include "JDataBindByName.hpp" +#include + +namespace margelo::nitro::rive { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ std::variant and the Java class "Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName". + */ + class JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName;"; + + static jni::local_ref create_0(jni::alias_ref value) { + static const auto method = javaClassStatic()->getStaticMethod)>("create"); + return method(javaClassStatic(), value); + } + static jni::local_ref create_1(jni::alias_ref value) { + static const auto method = javaClassStatic()->getStaticMethod)>("create"); + return method(javaClassStatic(), value); + } + static jni::local_ref create_2(jni::alias_ref value) { + static const auto method = javaClassStatic()->getStaticMethod)>("create"); + return method(javaClassStatic(), value); + } + + static jni::local_ref fromCpp(const std::variant, DataBindMode, DataBindByName>& variant) { + switch (variant.index()) { + case 0: return create_0(std::dynamic_pointer_cast(std::get<0>(variant))->getJavaPart()); + case 1: return create_1(JDataBindMode::fromCpp(std::get<1>(variant))); + case 2: return create_2(JDataBindByName::fromCpp(std::get<2>(variant))); + default: throw std::invalid_argument("Variant holds unknown index! (" + std::to_string(variant.index()) + ")"); + } + } + + [[nodiscard]] std::variant, DataBindMode, DataBindByName> toCpp() const; + }; + + namespace JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName_impl { + class First: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName$First;"; + + [[nodiscard]] jni::local_ref getValue() const { + static const auto field = javaClassStatic()->getField("value"); + return getFieldValue(field); + } + }; + + class Second: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName$Second;"; + + [[nodiscard]] jni::local_ref getValue() const { + static const auto field = javaClassStatic()->getField("value"); + return getFieldValue(field); + } + }; + + class Third: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName$Third;"; + + [[nodiscard]] jni::local_ref getValue() const { + static const auto field = javaClassStatic()->getField("value"); + return getFieldValue(field); + } + }; + } // namespace JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName_impl +} // namespace margelo::nitro::rive diff --git a/nitrogen/generated/android/c++/views/JHybridRiveViewStateUpdater.cpp b/nitrogen/generated/android/c++/views/JHybridRiveViewStateUpdater.cpp index 5a99f716..a54a6113 100644 --- a/nitrogen/generated/android/c++/views/JHybridRiveViewStateUpdater.cpp +++ b/nitrogen/generated/android/c++/views/JHybridRiveViewStateUpdater.cpp @@ -44,10 +44,6 @@ void JHybridRiveViewStateUpdater::updateViewProps(jni::alias_ref /* view->setStateMachineName(props.stateMachineName.value); // TODO: Set isDirty = false } - if (props.autoBind.isDirty) { - view->setAutoBind(props.autoBind.value); - // TODO: Set isDirty = false - } if (props.autoPlay.isDirty) { view->setAutoPlay(props.autoPlay.value); // TODO: Set isDirty = false @@ -68,6 +64,10 @@ void JHybridRiveViewStateUpdater::updateViewProps(jni::alias_ref /* view->setLayoutScaleFactor(props.layoutScaleFactor.value); // TODO: Set isDirty = false } + if (props.dataBind.isDirty) { + view->setDataBind(props.dataBind.value); + // TODO: Set isDirty = false + } // Update hybridRef if it changed if (props.hybridRef.isDirty) { diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindByName.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindByName.kt new file mode 100644 index 00000000..68bb2367 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindByName.kt @@ -0,0 +1,38 @@ +/// +/// DataBindByName.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.rive + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + + +/** + * Represents the JavaScript object/struct "DataBindByName". + */ +@DoNotStrip +@Keep +data class DataBindByName( + @DoNotStrip + @Keep + val byName: String +) { + /* primary constructor */ + + private companion object { + /** + * Constructor called from C++ + */ + @DoNotStrip + @Keep + @Suppress("unused") + @JvmStatic + private fun fromCpp(byName: String): DataBindByName { + return DataBindByName(byName) + } + } +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindMode.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindMode.kt new file mode 100644 index 00000000..089facbf --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/DataBindMode.kt @@ -0,0 +1,21 @@ +/// +/// DataBindMode.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.rive + +import androidx.annotation.Keep +import com.facebook.proguard.annotations.DoNotStrip + +/** + * Represents the JavaScript enum/union "DataBindMode". + */ +@DoNotStrip +@Keep +enum class DataBindMode(@DoNotStrip @Keep val value: Int) { + AUTO(0), + NONE(1); +} diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveViewSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveViewSpec.kt index 17da7821..94744b36 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveViewSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridRiveViewSpec.kt @@ -55,12 +55,6 @@ abstract class HybridRiveViewSpec: HybridView() { @set:Keep abstract var stateMachineName: String? - @get:DoNotStrip - @get:Keep - @set:DoNotStrip - @set:Keep - abstract var autoBind: Boolean? - @get:DoNotStrip @get:Keep @set:DoNotStrip @@ -90,6 +84,12 @@ abstract class HybridRiveViewSpec: HybridView() { @set:DoNotStrip @set:Keep abstract var layoutScaleFactor: Double? + + @get:DoNotStrip + @get:Keep + @set:DoNotStrip + @set:Keep + abstract var dataBind: Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName? // Methods @DoNotStrip @@ -100,6 +100,10 @@ abstract class HybridRiveViewSpec: HybridView() { @Keep abstract fun bindViewModelInstance(viewModelInstance: HybridViewModelInstanceSpec): Unit + @DoNotStrip + @Keep + abstract fun getViewModelInstance(): HybridViewModelInstanceSpec? + @DoNotStrip @Keep abstract fun play(): Unit diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.kt new file mode 100644 index 00000000..1881f64a --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.kt @@ -0,0 +1,72 @@ +/// +/// Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.rive + +import com.facebook.proguard.annotations.DoNotStrip + + +/** + * Represents the TypeScript variant "HybridViewModelInstanceSpec | DataBindMode | DataBindByName". + */ +@Suppress("ClassName") +@DoNotStrip +sealed class Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName { + @DoNotStrip + data class First(@DoNotStrip val value: HybridViewModelInstanceSpec): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName() + @DoNotStrip + data class Second(@DoNotStrip val value: DataBindMode): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName() + @DoNotStrip + data class Third(@DoNotStrip val value: DataBindByName): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName() + + @Deprecated("getAs() is not type-safe. Use fold/asFirstOrNull/asSecondOrNull instead.", level = DeprecationLevel.ERROR) + inline fun getAs(): T? = when (this) { + is First -> value as? T + is Second -> value as? T + is Third -> value as? T + } + + val isFirst: Boolean + get() = this is First + val isSecond: Boolean + get() = this is Second + val isThird: Boolean + get() = this is Third + + fun asFirstOrNull(): HybridViewModelInstanceSpec? { + val value = (this as? First)?.value ?: return null + return value + } + fun asSecondOrNull(): DataBindMode? { + val value = (this as? Second)?.value ?: return null + return value + } + fun asThirdOrNull(): DataBindByName? { + val value = (this as? Third)?.value ?: return null + return value + } + + inline fun match(first: (HybridViewModelInstanceSpec) -> R, second: (DataBindMode) -> R, third: (DataBindByName) -> R): R { + return when (this) { + is First -> first(value) + is Second -> second(value) + is Third -> third(value) + } + } + + companion object { + @JvmStatic + @DoNotStrip + fun create(value: HybridViewModelInstanceSpec): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName = First(value) + @JvmStatic + @DoNotStrip + fun create(value: DataBindMode): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName = Second(value) + @JvmStatic + @DoNotStrip + fun create(value: DataBindByName): Variant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName = Third(value) + } +} diff --git a/nitrogen/generated/android/rive+autolinking.cmake b/nitrogen/generated/android/rive+autolinking.cmake index 9cbc9ca2..8471d548 100644 --- a/nitrogen/generated/android/rive+autolinking.cmake +++ b/nitrogen/generated/android/rive+autolinking.cmake @@ -52,6 +52,7 @@ target_sources( ../nitrogen/generated/android/c++/JHybridRiveFileSpec.cpp ../nitrogen/generated/android/c++/JHybridRiveFileFactorySpec.cpp ../nitrogen/generated/android/c++/JHybridRiveViewSpec.cpp + ../nitrogen/generated/android/c++/JVariant_HybridViewModelInstanceSpec_DataBindMode_DataBindByName.cpp ../nitrogen/generated/android/c++/JEventPropertiesOutput.cpp ../nitrogen/generated/android/c++/views/JHybridRiveViewStateUpdater.cpp ../nitrogen/generated/android/c++/JHybridViewModelSpec.cpp diff --git a/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.cpp index 7378f05d..9d3686b8 100644 --- a/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.cpp +++ b/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.cpp @@ -105,14 +105,6 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } - // pragma MARK: std::function - Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept { - auto swiftClosure = Rive::Func_void_bool::fromUnsafe(swiftClosureWrapper); - return [swiftClosure = std::move(swiftClosure)](bool result) mutable -> void { - swiftClosure.call(result); - }; - } - // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridViewModelInstanceSpec_(void* NON_NULL swiftUnsafePointer) noexcept { Rive::HybridViewModelInstanceSpec_cxx swiftPart = Rive::HybridViewModelInstanceSpec_cxx::fromUnsafe(swiftUnsafePointer); @@ -129,6 +121,14 @@ namespace margelo::nitro::rive::bridge::swift { return swiftPart.toUnsafe(); } + // pragma MARK: std::function + Func_void_bool create_Func_void_bool(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = Rive::Func_void_bool::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](bool result) mutable -> void { + swiftClosure.call(result); + }; + } + // pragma MARK: std::function Func_void_UnifiedRiveEvent create_Func_void_UnifiedRiveEvent(void* NON_NULL swiftClosureWrapper) noexcept { auto swiftClosure = Rive::Func_void_UnifiedRiveEvent::fromUnsafe(swiftClosureWrapper); diff --git a/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.hpp index fbda282f..1a78f1ca 100644 --- a/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/Rive-Swift-Cxx-Bridge.hpp @@ -14,6 +14,10 @@ namespace margelo::nitro::rive { enum class Alignment; } namespace margelo::nitro::rive { enum class ArtboardByTypes; } // Forward declaration of `ArtboardBy` to properly resolve imports. namespace margelo::nitro::rive { struct ArtboardBy; } +// Forward declaration of `DataBindByName` to properly resolve imports. +namespace margelo::nitro::rive { struct DataBindByName; } +// Forward declaration of `DataBindMode` to properly resolve imports. +namespace margelo::nitro::rive { enum class DataBindMode; } // Forward declaration of `Fit` to properly resolve imports. namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridRiveFileFactorySpec` to properly resolve imports. @@ -83,6 +87,8 @@ namespace Rive { class HybridViewModelTriggerPropertySpec_cxx; } #include "Alignment.hpp" #include "ArtboardBy.hpp" #include "ArtboardByTypes.hpp" +#include "DataBindByName.hpp" +#include "DataBindMode.hpp" #include "Fit.hpp" #include "HybridRiveFileFactorySpec.hpp" #include "HybridRiveFileSpec.hpp" @@ -418,6 +424,68 @@ namespace margelo::nitro::rive::bridge::swift { return *optional; } + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridViewModelInstanceSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridViewModelInstanceSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridViewModelInstanceSpec_(std__shared_ptr_HybridViewModelInstanceSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridViewModelInstanceSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridViewModelInstanceSpec_ weakify_std__shared_ptr_HybridViewModelInstanceSpec_(const std::shared_ptr& strong) noexcept { return strong; } + + // pragma MARK: std::variant, DataBindMode, DataBindByName> + /** + * Wrapper struct for `std::variant, DataBindMode, DataBindByName>`. + * std::variant cannot be used in Swift because of a Swift bug. + * Not even specializing it works. So we create a wrapper struct. + */ + struct std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_ { + std::variant, DataBindMode, DataBindByName> variant; + std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(std::variant, DataBindMode, DataBindByName> variant): variant(variant) { } + operator std::variant, DataBindMode, DataBindByName>() const noexcept { + return variant; + } + inline size_t index() const noexcept { + return variant.index(); + } + inline std::shared_ptr get_0() const noexcept { + return std::get<0>(variant); + } + inline DataBindMode get_1() const noexcept { + return std::get<1>(variant); + } + inline DataBindByName get_2() const noexcept { + return std::get<2>(variant); + } + }; + inline std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_ create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(const std::shared_ptr& value) noexcept { + return std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(value); + } + inline std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_ create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(DataBindMode value) noexcept { + return std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(value); + } + inline std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_ create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(const DataBindByName& value) noexcept { + return std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(value); + } + + // pragma MARK: std::optional, DataBindMode, DataBindByName>> + /** + * Specialized version of `std::optional, DataBindMode, DataBindByName>>`. + */ + using std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__ = std::optional, DataBindMode, DataBindByName>>; + inline std::optional, DataBindMode, DataBindByName>> create_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__(const std::variant, DataBindMode, DataBindByName>& value) noexcept { + return std::optional, DataBindMode, DataBindByName>>(value); + } + inline bool has_value_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__(const std::optional, DataBindMode, DataBindByName>>& optional) noexcept { + return optional.has_value(); + } + inline std::variant, DataBindMode, DataBindByName> get_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__(const std::optional, DataBindMode, DataBindByName>>& optional) noexcept { + return *optional; + } + // pragma MARK: std::shared_ptr> /** * Specialized version of `std::shared_ptr>`. @@ -452,17 +520,20 @@ namespace margelo::nitro::rive::bridge::swift { return Func_void_bool_Wrapper(std::move(value)); } - // pragma MARK: std::shared_ptr + // pragma MARK: std::optional> /** - * Specialized version of `std::shared_ptr`. + * Specialized version of `std::optional>`. */ - using std__shared_ptr_HybridViewModelInstanceSpec_ = std::shared_ptr; - std::shared_ptr create_std__shared_ptr_HybridViewModelInstanceSpec_(void* NON_NULL swiftUnsafePointer) noexcept; - void* NON_NULL get_std__shared_ptr_HybridViewModelInstanceSpec_(std__shared_ptr_HybridViewModelInstanceSpec_ cppType); - - // pragma MARK: std::weak_ptr - using std__weak_ptr_HybridViewModelInstanceSpec_ = std::weak_ptr; - inline std__weak_ptr_HybridViewModelInstanceSpec_ weakify_std__shared_ptr_HybridViewModelInstanceSpec_(const std::shared_ptr& strong) noexcept { return strong; } + using std__optional_std__shared_ptr_HybridViewModelInstanceSpec__ = std::optional>; + inline std::optional> create_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::shared_ptr& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::shared_ptr get_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::optional>& optional) noexcept { + return *optional; + } // pragma MARK: std::variant /** @@ -582,6 +653,15 @@ namespace margelo::nitro::rive::bridge::swift { return Result>>::withError(error); } + // pragma MARK: Result>> + using Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ = Result>>; + inline Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(const std::optional>& value) noexcept { + return Result>>::withValue(value); + } + inline Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(const std::exception_ptr& error) noexcept { + return Result>>::withError(error); + } + // pragma MARK: Result using Result_bool_ = Result; inline Result_bool_ create_Result_bool_(bool value) noexcept { @@ -600,30 +680,6 @@ namespace margelo::nitro::rive::bridge::swift { return Result::withError(error); } - // pragma MARK: std::optional> - /** - * Specialized version of `std::optional>`. - */ - using std__optional_std__shared_ptr_HybridViewModelInstanceSpec__ = std::optional>; - inline std::optional> create_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::shared_ptr& value) noexcept { - return std::optional>(value); - } - inline bool has_value_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::optional>& optional) noexcept { - return optional.has_value(); - } - inline std::shared_ptr get_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__(const std::optional>& optional) noexcept { - return *optional; - } - - // pragma MARK: Result>> - using Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ = Result>>; - inline Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(const std::optional>& value) noexcept { - return Result>>::withValue(value); - } - inline Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(const std::exception_ptr& error) noexcept { - return Result>>::withError(error); - } - // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/nitrogen/generated/ios/Rive-Swift-Cxx-Umbrella.hpp b/nitrogen/generated/ios/Rive-Swift-Cxx-Umbrella.hpp index b56d547e..3b0f52e3 100644 --- a/nitrogen/generated/ios/Rive-Swift-Cxx-Umbrella.hpp +++ b/nitrogen/generated/ios/Rive-Swift-Cxx-Umbrella.hpp @@ -14,6 +14,10 @@ namespace margelo::nitro::rive { enum class Alignment; } namespace margelo::nitro::rive { enum class ArtboardByTypes; } // Forward declaration of `ArtboardBy` to properly resolve imports. namespace margelo::nitro::rive { struct ArtboardBy; } +// Forward declaration of `DataBindByName` to properly resolve imports. +namespace margelo::nitro::rive { struct DataBindByName; } +// Forward declaration of `DataBindMode` to properly resolve imports. +namespace margelo::nitro::rive { enum class DataBindMode; } // Forward declaration of `Fit` to properly resolve imports. namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridRiveFileFactorySpec` to properly resolve imports. @@ -55,6 +59,8 @@ namespace margelo::nitro::rive { struct UnifiedRiveEvent; } #include "Alignment.hpp" #include "ArtboardBy.hpp" #include "ArtboardByTypes.hpp" +#include "DataBindByName.hpp" +#include "DataBindMode.hpp" #include "Fit.hpp" #include "HybridRiveFileFactorySpec.hpp" #include "HybridRiveFileSpec.hpp" diff --git a/nitrogen/generated/ios/c++/HybridRiveViewSpecSwift.hpp b/nitrogen/generated/ios/c++/HybridRiveViewSpecSwift.hpp index b64fefbf..686546f1 100644 --- a/nitrogen/generated/ios/c++/HybridRiveViewSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridRiveViewSpecSwift.hpp @@ -20,6 +20,10 @@ namespace margelo::nitro::rive { enum class Alignment; } namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridViewModelInstanceSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridViewModelInstanceSpec; } +// Forward declaration of `DataBindMode` to properly resolve imports. +namespace margelo::nitro::rive { enum class DataBindMode; } +// Forward declaration of `DataBindByName` to properly resolve imports. +namespace margelo::nitro::rive { struct DataBindByName; } // Forward declaration of `UnifiedRiveEvent` to properly resolve imports. namespace margelo::nitro::rive { struct UnifiedRiveEvent; } // Forward declaration of `RiveEventType` to properly resolve imports. @@ -31,12 +35,14 @@ namespace margelo::nitro::rive { enum class RiveEventType; } #include "HybridRiveFileSpec.hpp" #include "Alignment.hpp" #include "Fit.hpp" -#include #include "HybridViewModelInstanceSpec.hpp" +#include "DataBindMode.hpp" +#include "DataBindByName.hpp" +#include +#include #include "UnifiedRiveEvent.hpp" #include #include "RiveEventType.hpp" -#include #include #include "Rive-Swift-Cxx-Umbrella.hpp" @@ -93,13 +99,6 @@ namespace margelo::nitro::rive { inline void setStateMachineName(const std::optional& stateMachineName) noexcept override { _swiftPart.setStateMachineName(stateMachineName); } - inline std::optional getAutoBind() noexcept override { - auto __result = _swiftPart.getAutoBind(); - return __result; - } - inline void setAutoBind(std::optional autoBind) noexcept override { - _swiftPart.setAutoBind(autoBind); - } inline std::optional getAutoPlay() noexcept override { auto __result = _swiftPart.getAutoPlay(); return __result; @@ -135,6 +134,13 @@ namespace margelo::nitro::rive { inline void setLayoutScaleFactor(std::optional layoutScaleFactor) noexcept override { _swiftPart.setLayoutScaleFactor(layoutScaleFactor); } + inline std::optional, DataBindMode, DataBindByName>> getDataBind() noexcept override { + auto __result = _swiftPart.getDataBind(); + return __result; + } + inline void setDataBind(const std::optional, DataBindMode, DataBindByName>>& dataBind) noexcept override { + _swiftPart.setDataBind(dataBind); + } public: // Methods @@ -152,6 +158,14 @@ namespace margelo::nitro::rive { std::rethrow_exception(__result.error()); } } + inline std::optional> getViewModelInstance() override { + auto __result = _swiftPart.getViewModelInstance(); + if (__result.hasError()) [[unlikely]] { + std::rethrow_exception(__result.error()); + } + auto __value = std::move(__result.value()); + return __value; + } inline void play() override { auto __result = _swiftPart.play(); if (__result.hasError()) [[unlikely]] { diff --git a/nitrogen/generated/ios/c++/views/HybridRiveViewComponent.mm b/nitrogen/generated/ios/c++/views/HybridRiveViewComponent.mm index d83de668..0c4d01bd 100644 --- a/nitrogen/generated/ios/c++/views/HybridRiveViewComponent.mm +++ b/nitrogen/generated/ios/c++/views/HybridRiveViewComponent.mm @@ -81,11 +81,6 @@ - (void) updateProps:(const std::shared_ptr&)props swiftPart.setStateMachineName(newViewProps.stateMachineName.value); newViewProps.stateMachineName.isDirty = false; } - // autoBind: optional - if (newViewProps.autoBind.isDirty) { - swiftPart.setAutoBind(newViewProps.autoBind.value); - newViewProps.autoBind.isDirty = false; - } // autoPlay: optional if (newViewProps.autoPlay.isDirty) { swiftPart.setAutoPlay(newViewProps.autoPlay.value); @@ -111,6 +106,11 @@ - (void) updateProps:(const std::shared_ptr&)props swiftPart.setLayoutScaleFactor(newViewProps.layoutScaleFactor.value); newViewProps.layoutScaleFactor.isDirty = false; } + // dataBind: optional + if (newViewProps.dataBind.isDirty) { + swiftPart.setDataBind(newViewProps.dataBind.value); + newViewProps.dataBind.isDirty = false; + } swiftPart.afterUpdate(); diff --git a/nitrogen/generated/ios/swift/DataBindByName.swift b/nitrogen/generated/ios/swift/DataBindByName.swift new file mode 100644 index 00000000..4b9db67c --- /dev/null +++ b/nitrogen/generated/ios/swift/DataBindByName.swift @@ -0,0 +1,35 @@ +/// +/// DataBindByName.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Represents an instance of `DataBindByName`, backed by a C++ struct. + */ +public typealias DataBindByName = margelo.nitro.rive.DataBindByName + +public extension DataBindByName { + private typealias bridge = margelo.nitro.rive.bridge.swift + + /** + * Create a new instance of `DataBindByName`. + */ + init(byName: String) { + self.init(std.string(byName)) + } + + var byName: String { + @inline(__always) + get { + return String(self.__byName) + } + @inline(__always) + set { + self.__byName = std.string(newValue) + } + } +} diff --git a/nitrogen/generated/ios/swift/DataBindMode.swift b/nitrogen/generated/ios/swift/DataBindMode.swift new file mode 100644 index 00000000..d098dc20 --- /dev/null +++ b/nitrogen/generated/ios/swift/DataBindMode.swift @@ -0,0 +1,40 @@ +/// +/// DataBindMode.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +/** + * Represents the JS enum `DataBindMode`, backed by a C++ enum. + */ +public typealias DataBindMode = margelo.nitro.rive.DataBindMode + +public extension DataBindMode { + /** + * Get a DataBindMode for the given String value, or + * return `nil` if the given value was invalid/unknown. + */ + init?(fromString string: String) { + switch string { + case "Auto": + self = .auto + case "None": + self = .none + default: + return nil + } + } + + /** + * Get the String value this DataBindMode represents. + */ + var stringValue: String { + switch self { + case .auto: + return "Auto" + case .none: + return "None" + } + } +} diff --git a/nitrogen/generated/ios/swift/HybridRiveViewSpec.swift b/nitrogen/generated/ios/swift/HybridRiveViewSpec.swift index 720ab4b0..8e4520d2 100644 --- a/nitrogen/generated/ios/swift/HybridRiveViewSpec.swift +++ b/nitrogen/generated/ios/swift/HybridRiveViewSpec.swift @@ -14,16 +14,17 @@ public protocol HybridRiveViewSpec_protocol: HybridObject, HybridView { // Properties var artboardName: String? { get set } var stateMachineName: String? { get set } - var autoBind: Bool? { get set } var autoPlay: Bool? { get set } var file: (any HybridRiveFileSpec) { get set } var alignment: Alignment? { get set } var fit: Fit? { get set } var layoutScaleFactor: Double? { get set } + var dataBind: Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName? { get set } // Methods func awaitViewReady() throws -> Promise func bindViewModelInstance(viewModelInstance: (any HybridViewModelInstanceSpec)) throws -> Void + func getViewModelInstance() throws -> (any HybridViewModelInstanceSpec)? func play() throws -> Void func pause() throws -> Void func onEventListener(onEvent: @escaping (_ event: UnifiedRiveEvent) -> Void) throws -> Void diff --git a/nitrogen/generated/ios/swift/HybridRiveViewSpec_cxx.swift b/nitrogen/generated/ios/swift/HybridRiveViewSpec_cxx.swift index fef8e63f..aad03ff9 100644 --- a/nitrogen/generated/ios/swift/HybridRiveViewSpec_cxx.swift +++ b/nitrogen/generated/ios/swift/HybridRiveViewSpec_cxx.swift @@ -163,30 +163,6 @@ open class HybridRiveViewSpec_cxx { } } - public final var autoBind: bridge.std__optional_bool_ { - @inline(__always) - get { - return { () -> bridge.std__optional_bool_ in - if let __unwrappedValue = self.__implementation.autoBind { - return bridge.create_std__optional_bool_(__unwrappedValue) - } else { - return .init() - } - }() - } - @inline(__always) - set { - self.__implementation.autoBind = { () -> Bool? in - if bridge.has_value_std__optional_bool_(newValue) { - let __unwrapped = bridge.get_std__optional_bool_(newValue) - return __unwrapped - } else { - return nil - } - }() - } - } - public final var autoPlay: bridge.std__optional_bool_ { @inline(__always) get { @@ -279,6 +255,61 @@ open class HybridRiveViewSpec_cxx { self.__implementation.layoutScaleFactor = newValue.value } } + + public final var dataBind: bridge.std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__ { + @inline(__always) + get { + return { () -> bridge.std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__ in + if let __unwrappedValue = self.__implementation.dataBind { + return bridge.create_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__({ () -> bridge.std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_ in + switch __unwrappedValue { + case .first(let __value): + return bridge.create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_({ () -> bridge.std__shared_ptr_HybridViewModelInstanceSpec_ in + let __cxxWrapped = __value.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }()) + case .second(let __value): + return bridge.create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(__value) + case .third(let __value): + return bridge.create_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(__value) + } + }().variant) + } else { + return .init() + } + }() + } + @inline(__always) + set { + self.__implementation.dataBind = { () -> Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName? in + if bridge.has_value_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__(newValue) { + let __unwrapped = bridge.get_std__optional_std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName__(newValue) + return { () -> Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName in + let __variant = bridge.std__variant_std__shared_ptr_HybridViewModelInstanceSpec___DataBindMode__DataBindByName_(__unwrapped) + switch __variant.index() { + case 0: + let __actual = __variant.get_0() + return .first({ () -> HybridViewModelInstanceSpec in + let __unsafePointer = bridge.get_std__shared_ptr_HybridViewModelInstanceSpec_(__actual) + let __instance = HybridViewModelInstanceSpec_cxx.fromUnsafe(__unsafePointer) + return __instance.getHybridViewModelInstanceSpec() + }()) + case 1: + let __actual = __variant.get_1() + return .second(__actual) + case 2: + let __actual = __variant.get_2() + return .third(__actual) + default: + fatalError("Variant can never have index \(__variant.index())!") + } + }() + } else { + return nil + } + }() + } + } // Methods @inline(__always) @@ -315,6 +346,27 @@ open class HybridRiveViewSpec_cxx { } } + @inline(__always) + public final func getViewModelInstance() -> bridge.Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___ { + do { + let __result = try self.__implementation.getViewModelInstance() + let __resultCpp = { () -> bridge.std__optional_std__shared_ptr_HybridViewModelInstanceSpec__ in + if let __unwrappedValue = __result { + return bridge.create_std__optional_std__shared_ptr_HybridViewModelInstanceSpec__({ () -> bridge.std__shared_ptr_HybridViewModelInstanceSpec_ in + let __cxxWrapped = __unwrappedValue.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }()) + } else { + return .init() + } + }() + return bridge.create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(__resultCpp) + } catch (let __error) { + let __exceptionPtr = __error.toCpp() + return bridge.create_Result_std__optional_std__shared_ptr_HybridViewModelInstanceSpec___(__exceptionPtr) + } + } + @inline(__always) public final func play() -> bridge.Result_void_ { do { diff --git a/nitrogen/generated/ios/swift/Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName.swift b/nitrogen/generated/ios/swift/Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName.swift new file mode 100644 index 00000000..9b250552 --- /dev/null +++ b/nitrogen/generated/ios/swift/Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName.swift @@ -0,0 +1,19 @@ +/// +/// Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + + + +/** + * An Swift enum with associated values representing a Variant/Union type. + * JS type: `hybrid-object | enum | struct` + */ +@frozen +public indirect enum Variant__any_HybridViewModelInstanceSpec__DataBindMode_DataBindByName { + case first((any HybridViewModelInstanceSpec)) + case second(DataBindMode) + case third(DataBindByName) +} diff --git a/nitrogen/generated/shared/c++/DataBindByName.hpp b/nitrogen/generated/shared/c++/DataBindByName.hpp new file mode 100644 index 00000000..005b8f25 --- /dev/null +++ b/nitrogen/generated/shared/c++/DataBindByName.hpp @@ -0,0 +1,75 @@ +/// +/// DataBindByName.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::rive { + + /** + * A struct which can be represented as a JavaScript object (DataBindByName). + */ + struct DataBindByName { + public: + std::string byName SWIFT_PRIVATE; + + public: + DataBindByName() = default; + explicit DataBindByName(std::string byName): byName(byName) {} + }; + +} // namespace margelo::nitro::rive + +namespace margelo::nitro { + + // C++ DataBindByName <> JS DataBindByName (object) + template <> + struct JSIConverter final { + static inline margelo::nitro::rive::DataBindByName fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return margelo::nitro::rive::DataBindByName( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "byName")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rive::DataBindByName& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "byName", JSIConverter::toJSI(runtime, arg.byName)); + return obj; + } + static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) { + if (!value.isObject()) { + return false; + } + jsi::Object obj = value.getObject(runtime); + if (!nitro::isPlainObject(runtime, obj)) { + return false; + } + if (!JSIConverter::canConvert(runtime, obj.getProperty(runtime, "byName"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/DataBindMode.hpp b/nitrogen/generated/shared/c++/DataBindMode.hpp new file mode 100644 index 00000000..9e9b53c7 --- /dev/null +++ b/nitrogen/generated/shared/c++/DataBindMode.hpp @@ -0,0 +1,62 @@ +/// +/// DataBindMode.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + +namespace margelo::nitro::rive { + + /** + * An enum which can be represented as a JavaScript enum (DataBindMode). + */ + enum class DataBindMode { + AUTO SWIFT_NAME(auto) = 0, + NONE SWIFT_NAME(none) = 1, + } CLOSED_ENUM; + +} // namespace margelo::nitro::rive + +namespace margelo::nitro { + + // C++ DataBindMode <> JS DataBindMode (enum) + template <> + struct JSIConverter final { + static inline margelo::nitro::rive::DataBindMode fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + int enumValue = JSIConverter::fromJSI(runtime, arg); + return static_cast(enumValue); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::rive::DataBindMode arg) { + int enumValue = static_cast(arg); + return JSIConverter::toJSI(runtime, enumValue); + } + static inline bool canConvert(jsi::Runtime&, const jsi::Value& value) { + if (!value.isNumber()) { + return false; + } + double number = value.getNumber(); + int integer = static_cast(number); + if (number != integer) { + // The integer is not the same value as the double - we truncated floating points. + // Enums are all integers, so the input floating point number is obviously invalid. + return false; + } + // Check if we are within the bounds of the enum. + return integer >= 0 && integer <= 1; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/HybridRiveViewSpec.cpp b/nitrogen/generated/shared/c++/HybridRiveViewSpec.cpp index 415df940..5f347257 100644 --- a/nitrogen/generated/shared/c++/HybridRiveViewSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridRiveViewSpec.cpp @@ -18,8 +18,6 @@ namespace margelo::nitro::rive { prototype.registerHybridSetter("artboardName", &HybridRiveViewSpec::setArtboardName); prototype.registerHybridGetter("stateMachineName", &HybridRiveViewSpec::getStateMachineName); prototype.registerHybridSetter("stateMachineName", &HybridRiveViewSpec::setStateMachineName); - prototype.registerHybridGetter("autoBind", &HybridRiveViewSpec::getAutoBind); - prototype.registerHybridSetter("autoBind", &HybridRiveViewSpec::setAutoBind); prototype.registerHybridGetter("autoPlay", &HybridRiveViewSpec::getAutoPlay); prototype.registerHybridSetter("autoPlay", &HybridRiveViewSpec::setAutoPlay); prototype.registerHybridGetter("file", &HybridRiveViewSpec::getFile); @@ -30,8 +28,11 @@ namespace margelo::nitro::rive { prototype.registerHybridSetter("fit", &HybridRiveViewSpec::setFit); prototype.registerHybridGetter("layoutScaleFactor", &HybridRiveViewSpec::getLayoutScaleFactor); prototype.registerHybridSetter("layoutScaleFactor", &HybridRiveViewSpec::setLayoutScaleFactor); + prototype.registerHybridGetter("dataBind", &HybridRiveViewSpec::getDataBind); + prototype.registerHybridSetter("dataBind", &HybridRiveViewSpec::setDataBind); prototype.registerHybridMethod("awaitViewReady", &HybridRiveViewSpec::awaitViewReady); prototype.registerHybridMethod("bindViewModelInstance", &HybridRiveViewSpec::bindViewModelInstance); + prototype.registerHybridMethod("getViewModelInstance", &HybridRiveViewSpec::getViewModelInstance); prototype.registerHybridMethod("play", &HybridRiveViewSpec::play); prototype.registerHybridMethod("pause", &HybridRiveViewSpec::pause); prototype.registerHybridMethod("onEventListener", &HybridRiveViewSpec::onEventListener); diff --git a/nitrogen/generated/shared/c++/HybridRiveViewSpec.hpp b/nitrogen/generated/shared/c++/HybridRiveViewSpec.hpp index cdbef0c5..c4d4b0be 100644 --- a/nitrogen/generated/shared/c++/HybridRiveViewSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridRiveViewSpec.hpp @@ -21,6 +21,10 @@ namespace margelo::nitro::rive { enum class Alignment; } namespace margelo::nitro::rive { enum class Fit; } // Forward declaration of `HybridViewModelInstanceSpec` to properly resolve imports. namespace margelo::nitro::rive { class HybridViewModelInstanceSpec; } +// Forward declaration of `DataBindMode` to properly resolve imports. +namespace margelo::nitro::rive { enum class DataBindMode; } +// Forward declaration of `DataBindByName` to properly resolve imports. +namespace margelo::nitro::rive { struct DataBindByName; } // Forward declaration of `UnifiedRiveEvent` to properly resolve imports. namespace margelo::nitro::rive { struct UnifiedRiveEvent; } @@ -30,8 +34,11 @@ namespace margelo::nitro::rive { struct UnifiedRiveEvent; } #include "HybridRiveFileSpec.hpp" #include "Alignment.hpp" #include "Fit.hpp" -#include #include "HybridViewModelInstanceSpec.hpp" +#include "DataBindMode.hpp" +#include "DataBindByName.hpp" +#include +#include #include "UnifiedRiveEvent.hpp" #include @@ -66,8 +73,6 @@ namespace margelo::nitro::rive { virtual void setArtboardName(const std::optional& artboardName) = 0; virtual std::optional getStateMachineName() = 0; virtual void setStateMachineName(const std::optional& stateMachineName) = 0; - virtual std::optional getAutoBind() = 0; - virtual void setAutoBind(std::optional autoBind) = 0; virtual std::optional getAutoPlay() = 0; virtual void setAutoPlay(std::optional autoPlay) = 0; virtual std::shared_ptr getFile() = 0; @@ -78,11 +83,14 @@ namespace margelo::nitro::rive { virtual void setFit(std::optional fit) = 0; virtual std::optional getLayoutScaleFactor() = 0; virtual void setLayoutScaleFactor(std::optional layoutScaleFactor) = 0; + virtual std::optional, DataBindMode, DataBindByName>> getDataBind() = 0; + virtual void setDataBind(const std::optional, DataBindMode, DataBindByName>>& dataBind) = 0; public: // Methods virtual std::shared_ptr> awaitViewReady() = 0; virtual void bindViewModelInstance(const std::shared_ptr& viewModelInstance) = 0; + virtual std::optional> getViewModelInstance() = 0; virtual void play() = 0; virtual void pause() = 0; virtual void onEventListener(const std::function& onEvent) = 0; diff --git a/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.cpp b/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.cpp index d548cb9f..0d9069d6 100644 --- a/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.cpp +++ b/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.cpp @@ -45,16 +45,6 @@ namespace margelo::nitro::rive::views { throw std::runtime_error(std::string("RiveView.stateMachineName: ") + exc.what()); } }()), - autoBind([&]() -> CachedProp> { - try { - const react::RawValue* rawValue = rawProps.at("autoBind", nullptr, nullptr); - if (rawValue == nullptr) return sourceProps.autoBind; - const auto& [runtime, value] = (std::pair)*rawValue; - return CachedProp>::fromRawValue(*runtime, value, sourceProps.autoBind); - } catch (const std::exception& exc) { - throw std::runtime_error(std::string("RiveView.autoBind: ") + exc.what()); - } - }()), autoPlay([&]() -> CachedProp> { try { const react::RawValue* rawValue = rawProps.at("autoPlay", nullptr, nullptr); @@ -105,6 +95,16 @@ namespace margelo::nitro::rive::views { throw std::runtime_error(std::string("RiveView.layoutScaleFactor: ") + exc.what()); } }()), + dataBind([&]() -> CachedProp, DataBindMode, DataBindByName>>> { + try { + const react::RawValue* rawValue = rawProps.at("dataBind", nullptr, nullptr); + if (rawValue == nullptr) return sourceProps.dataBind; + const auto& [runtime, value] = (std::pair)*rawValue; + return CachedProp, DataBindMode, DataBindByName>>>::fromRawValue(*runtime, value, sourceProps.dataBind); + } catch (const std::exception& exc) { + throw std::runtime_error(std::string("RiveView.dataBind: ") + exc.what()); + } + }()), hybridRef([&]() -> CachedProp& /* ref */)>>> { try { const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr); @@ -120,24 +120,24 @@ namespace margelo::nitro::rive::views { react::ViewProps(), artboardName(other.artboardName), stateMachineName(other.stateMachineName), - autoBind(other.autoBind), autoPlay(other.autoPlay), file(other.file), alignment(other.alignment), fit(other.fit), layoutScaleFactor(other.layoutScaleFactor), + dataBind(other.dataBind), hybridRef(other.hybridRef) { } bool HybridRiveViewProps::filterObjectKeys(const std::string& propName) { switch (hashString(propName)) { case hashString("artboardName"): return true; case hashString("stateMachineName"): return true; - case hashString("autoBind"): return true; case hashString("autoPlay"): return true; case hashString("file"): return true; case hashString("alignment"): return true; case hashString("fit"): return true; case hashString("layoutScaleFactor"): return true; + case hashString("dataBind"): return true; case hashString("hybridRef"): return true; default: return false; } diff --git a/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.hpp b/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.hpp index e8de2ebb..664315c3 100644 --- a/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.hpp +++ b/nitrogen/generated/shared/c++/views/HybridRiveViewComponent.hpp @@ -22,6 +22,10 @@ #include "HybridRiveFileSpec.hpp" #include "Alignment.hpp" #include "Fit.hpp" +#include "HybridViewModelInstanceSpec.hpp" +#include "DataBindMode.hpp" +#include "DataBindByName.hpp" +#include #include "HybridRiveViewSpec.hpp" #include @@ -48,12 +52,12 @@ namespace margelo::nitro::rive::views { public: CachedProp> artboardName; CachedProp> stateMachineName; - CachedProp> autoBind; CachedProp> autoPlay; CachedProp> file; CachedProp> alignment; CachedProp> fit; CachedProp> layoutScaleFactor; + CachedProp, DataBindMode, DataBindByName>>> dataBind; CachedProp& /* ref */)>>> hybridRef; private: diff --git a/nitrogen/generated/shared/json/RiveViewConfig.json b/nitrogen/generated/shared/json/RiveViewConfig.json index a291c796..19eab869 100644 --- a/nitrogen/generated/shared/json/RiveViewConfig.json +++ b/nitrogen/generated/shared/json/RiveViewConfig.json @@ -6,12 +6,12 @@ "validAttributes": { "artboardName": true, "stateMachineName": true, - "autoBind": true, "autoPlay": true, "file": true, "alignment": true, "fit": true, "layoutScaleFactor": true, + "dataBind": true, "hybridRef": true } } diff --git a/src/index.tsx b/src/index.tsx index 659e1301..02d369be 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,11 +4,21 @@ import { type ReactNativeView, } from 'react-native-nitro-modules'; import type { Rive } from './specs/Rive.nitro'; -import type { - RiveViewMethods, - RiveViewTSMethods, - RiveViewProps, +import { + type RiveViewMethods, + type RiveViewTSMethods, + type RiveViewProps, + DataBindMode, + type DataBindByName as DataBindByNameInterface, } from './specs/RiveView.nitro'; + +export class DataBindByName implements DataBindByNameInterface { + byName: string; + constructor(name: string) { + this.byName = name; + } +} + import RiveViewConfig from '../nitrogen/generated/shared/json/RiveViewConfig.json'; const RiveHybridObject = NitroModules.createHybridObject('Rive'); @@ -78,3 +88,4 @@ export { useRiveColor } from './hooks/useRiveColor'; export { useRiveTrigger } from './hooks/useRiveTrigger'; export { useRiveFile } from './hooks/useRiveFile'; export { type RiveFileInput } from './hooks/useRiveFile'; +export { DataBindMode }; diff --git a/src/specs/RiveView.nitro.ts b/src/specs/RiveView.nitro.ts index aaa3a890..9f0a1baf 100644 --- a/src/specs/RiveView.nitro.ts +++ b/src/specs/RiveView.nitro.ts @@ -9,6 +9,14 @@ import type { ViewModelInstance } from './ViewModel.nitro'; import type { Alignment } from '../core/Alignment'; import type { UnifiedRiveEvent, RiveEvent } from '../core/Events'; +export enum DataBindMode { + Auto, + None, +} +export interface DataBindByName { + byName: string; +} + /** * Props interface for the RiveView component. * Extends HybridViewProps to include Rive-specific properties. @@ -18,8 +26,6 @@ export interface RiveViewProps extends HybridViewProps { artboardName?: string; /** Name of the state mgachine to play */ stateMachineName?: string; - /** Whether to automatically bind the state machine and artboard */ - autoBind?: boolean; /** Whether to automatically start playing the state machine */ autoPlay?: boolean; /** The Rive file to be displayed */ @@ -30,6 +36,8 @@ export interface RiveViewProps extends HybridViewProps { fit?: Fit; /** The scale factor to apply to the Rive graphic when using Fit.Layout */ layoutScaleFactor?: number; + /** The view model instance to bind, to the state machine. Defaults to DataBindMode.Auto */ + dataBind?: ViewModelInstance | DataBindMode | DataBindByName; } /** @@ -44,6 +52,11 @@ export interface RiveViewMethods extends HybridViewMethods { awaitViewReady(): Promise; /** Binds the view model instance to the Rive view */ bindViewModelInstance(viewModelInstance: ViewModelInstance): void; + /** + * Gets the currently bound view model instance from the Rive view + * @returns The bound ViewModelInstance, or null if none is bound + */ + getViewModelInstance(): ViewModelInstance | null; /** Starts playing the Rive graphic */ play(): void; /** Pauses the the Rive graphic */