Skip to content

Commit 415804d

Browse files
committed
feat: add data binding lists support
Adds ViewModelListProperty for dynamic list management at runtime. Closes #11
1 parent 1f325e7 commit 415804d

30 files changed

Lines changed: 1086 additions & 0 deletions

android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,13 @@ class HybridViewModelInstance(val viewModelInstance: ViewModelInstance) : Hybrid
7373
return null
7474
}
7575
}
76+
77+
override fun listProperty(path: String): HybridViewModelListPropertySpec? {
78+
try {
79+
val listProperty = viewModelInstance.getListProperty(path)
80+
return HybridViewModelListProperty(listProperty)
81+
} catch (e: ViewModelException) {
82+
return null
83+
}
84+
}
7685
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.margelo.nitro.rive
2+
3+
import androidx.annotation.Keep
4+
import app.rive.runtime.kotlin.core.ViewModelListProperty
5+
import com.facebook.proguard.annotations.DoNotStrip
6+
import kotlinx.coroutines.flow.map
7+
8+
@Keep
9+
@DoNotStrip
10+
class HybridViewModelListProperty(private val listProperty: ViewModelListProperty) :
11+
HybridViewModelListPropertySpec(),
12+
BaseHybridViewModelProperty<Unit> by BaseHybridViewModelPropertyImpl() {
13+
override val length: Double
14+
get() = listProperty.size.toDouble()
15+
16+
override fun instanceAt(index: Double): HybridViewModelInstanceSpec? {
17+
return try {
18+
HybridViewModelInstance(listProperty.elementAt(index.toInt()))
19+
} catch (e: IndexOutOfBoundsException) {
20+
null
21+
}
22+
}
23+
24+
override fun addInstance(instance: HybridViewModelInstanceSpec) {
25+
val hybridInstance = instance as? HybridViewModelInstance ?: return
26+
listProperty.add(hybridInstance.viewModelInstance)
27+
}
28+
29+
override fun insertInstance(instance: HybridViewModelInstanceSpec, index: Double) {
30+
val hybridInstance = instance as? HybridViewModelInstance ?: return
31+
listProperty.add(index.toInt(), hybridInstance.viewModelInstance)
32+
}
33+
34+
override fun removeInstance(instance: HybridViewModelInstanceSpec) {
35+
val hybridInstance = instance as? HybridViewModelInstance ?: return
36+
listProperty.remove(hybridInstance.viewModelInstance)
37+
}
38+
39+
override fun removeInstanceAt(index: Double) {
40+
listProperty.removeAt(index.toInt())
41+
}
42+
43+
override fun swap(index1: Double, index2: Double) {
44+
listProperty.swap(index1.toInt(), index2.toInt())
45+
}
46+
47+
override fun addListener(onChanged: () -> Unit) {
48+
listeners.add { onChanged() }
49+
ensureValueListenerJob(listProperty.valueFlow.map { })
50+
}
51+
}

ios/BaseHybridViewModelProperty.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typealias EnumPropertyType = RiveDataBindingViewModel.Instance.EnumProperty
2323
typealias ColorPropertyType = RiveDataBindingViewModel.Instance.ColorProperty
2424
typealias TriggerPropertyType = RiveDataBindingViewModel.Instance.TriggerProperty
2525
typealias ImagePropertyType = RiveDataBindingViewModel.Instance.ImageProperty
26+
typealias ListPropertyType = RiveDataBindingViewModel.Instance.ListProperty
2627

2728
// Make all Rive property types conform to the protocol
2829
extension BooleanPropertyType: RivePropertyWithListeners {
@@ -56,6 +57,14 @@ extension ImagePropertyType: RivePropertyWithListeners {
5657
}
5758
}
5859

60+
extension ListPropertyType: RivePropertyWithListeners {
61+
typealias ListenerValueType = Void
62+
63+
func addListener(_ callback: @escaping ListenerType) -> UUID {
64+
addListener { callback(()) }
65+
}
66+
}
67+
5968
/// Helper class for managing ViewModel property listeners
6069
class PropertyListenerHelper<PropertyType: RivePropertyWithListeners> {
6170
private var listenerIds: [UUID] = []

ios/HybridViewModelInstance.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,9 @@ class HybridViewModelInstance: HybridViewModelInstanceSpec {
4848
guard let property = viewModelInstance?.imageProperty(fromPath: path) else { return nil }
4949
return HybridViewModelImageProperty(property: property)
5050
}
51+
52+
func listProperty(path: String) throws -> (any HybridViewModelListPropertySpec)? {
53+
guard let property = viewModelInstance?.listProperty(fromPath: path) else { return nil }
54+
return HybridViewModelListProperty(property: property)
55+
}
5156
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import RiveRuntime
2+
3+
class HybridViewModelListProperty: HybridViewModelListPropertySpec, ValuedPropertyProtocol {
4+
var property: ListPropertyType!
5+
lazy var helper = PropertyListenerHelper(property: property!)
6+
7+
init(property: ListPropertyType) {
8+
self.property = property
9+
super.init()
10+
}
11+
12+
override init() {
13+
super.init()
14+
}
15+
16+
var length: Double {
17+
Double(property.count)
18+
}
19+
20+
func instanceAt(index: Double) throws -> (any HybridViewModelInstanceSpec)? {
21+
guard let instance = property.instance(at: Int(index)) else { return nil }
22+
return HybridViewModelInstance(viewModelInstance: instance)
23+
}
24+
25+
func addInstance(instance: any HybridViewModelInstanceSpec) throws {
26+
guard let hybridInstance = instance as? HybridViewModelInstance,
27+
let viewModelInstance = hybridInstance.viewModelInstance else { return }
28+
property.addInstance(viewModelInstance)
29+
}
30+
31+
func insertInstance(instance: any HybridViewModelInstanceSpec, index: Double) throws {
32+
guard let hybridInstance = instance as? HybridViewModelInstance,
33+
let viewModelInstance = hybridInstance.viewModelInstance else { return }
34+
_ = property.insertInstance(viewModelInstance, at: Int(index))
35+
}
36+
37+
func removeInstance(instance: any HybridViewModelInstanceSpec) throws {
38+
guard let hybridInstance = instance as? HybridViewModelInstance,
39+
let viewModelInstance = hybridInstance.viewModelInstance else { return }
40+
property.removeInstance(viewModelInstance)
41+
}
42+
43+
func removeInstanceAt(index: Double) throws {
44+
property.removeInstance(at: Int(index))
45+
}
46+
47+
func swap(index1: Double, index2: Double) throws {
48+
property.swapInstance(at: Int(index1), withInstanceAt: Int(index2))
49+
}
50+
51+
func addListener(onChanged: @escaping () -> Void) throws {
52+
try addListener(onChanged: { _ in onChanged() })
53+
}
54+
}

nitrogen/generated/android/c++/JHybridViewModelImagePropertySpec.cpp

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.cpp

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.hpp

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.cpp

Lines changed: 91 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.hpp

Lines changed: 73 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)