pythonnative 0.35.0__py3-none-any.whl → 0.37.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonnative/__init__.py +6 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +34 -88
- pythonnative/bootstrap.py +90 -0
- pythonnative/bridge/__init__.py +306 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +164 -0
- pythonnative/bridge/fake.py +236 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/cli/pn.py +24 -6
- pythonnative/components/layout.py +65 -3
- pythonnative/gestures.py +13 -10
- pythonnative/hooks.py +41 -26
- pythonnative/hosts/__init__.py +13 -36
- pythonnative/hosts/native.py +348 -0
- pythonnative/images.py +6 -5
- pythonnative/mutations.py +21 -0
- pythonnative/native_modules/__init__.py +30 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +20 -84
- pythonnative/native_modules/biometrics.py +11 -124
- pythonnative/native_modules/camera.py +15 -291
- pythonnative/native_modules/clipboard.py +14 -109
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +15 -21
- pythonnative/native_modules/haptics.py +19 -129
- pythonnative/native_modules/linking.py +24 -137
- pythonnative/native_modules/location.py +28 -180
- pythonnative/native_modules/net_info.py +25 -135
- pythonnative/native_modules/notifications.py +36 -257
- pythonnative/native_modules/permissions.py +20 -184
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +18 -155
- pythonnative/native_modules/share.py +8 -113
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +0 -3
- pythonnative/project/builder.py +38 -16
- pythonnative/project/config.py +12 -2
- pythonnative/project/plugins.py +366 -0
- pythonnative/py.typed +0 -0
- pythonnative/runtime.py +35 -203
- pythonnative/sdk/__init__.py +48 -33
- pythonnative/sdk/_components.py +57 -76
- pythonnative/storage.py +18 -243
- pythonnative/templates/android_template/app/build.gradle +6 -9
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
- pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
- pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +88 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
- pythonnative/templates/android_template/settings.gradle +1 -0
- pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
- pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +38 -46
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
- pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +47 -1
- pythonnative/utils.py +4 -82
- pythonnative/virtual_rows.py +3 -3
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
- pythonnative-0.37.0.dist-info/RECORD +263 -0
- pythonnative/hosts/android.py +0 -269
- pythonnative/hosts/ios.py +0 -404
- pythonnative/native_views/android.py +0 -3702
- pythonnative/native_views/ios.py +0 -5253
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
- pythonnative-0.35.0.dist-info/RECORD +0 -155
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Drives the `animate(tag, request)` protocol for animatable props
|
|
4
|
+
/// (`opacity`, `background_color`, `color`, `translate_x`, `translate_y`,
|
|
5
|
+
/// `scale`, `scale_x`, `scale_y`, `rotate`).
|
|
6
|
+
///
|
|
7
|
+
/// - `set` applies one Python-driven frame immediately.
|
|
8
|
+
/// - `start` runs `timing` / `spring` with `UIViewPropertyAnimator` and
|
|
9
|
+
/// `decay` with a display-link integrator; completion posts
|
|
10
|
+
/// `callback("animation", 0, "", {"id": n, "finished": bool})`.
|
|
11
|
+
/// - `cancel` stops the animation and returns the presentation value.
|
|
12
|
+
public final class PNAnimator {
|
|
13
|
+
public static let shared = PNAnimator()
|
|
14
|
+
|
|
15
|
+
/// Prop names accepted by `set` and `start`.
|
|
16
|
+
public static let animatableProps: Set<String> = [
|
|
17
|
+
"opacity", "background_color", "color", "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
final class Running {
|
|
21
|
+
weak var view: UIView?
|
|
22
|
+
let prop: String
|
|
23
|
+
var animator: UIViewPropertyAnimator?
|
|
24
|
+
var decay: PNDecayDriver?
|
|
25
|
+
init(view: UIView, prop: String) {
|
|
26
|
+
self.view = view
|
|
27
|
+
self.prop = prop
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private var running: [Int64: Running] = [:]
|
|
32
|
+
|
|
33
|
+
private init() {}
|
|
34
|
+
|
|
35
|
+
// MARK: - Request routing
|
|
36
|
+
|
|
37
|
+
/// Handle one decoded `animate` request for the view with `tag`.
|
|
38
|
+
public func handle(tag: Int64, request: [String: Any]) -> Any? {
|
|
39
|
+
let op = PNProps.string(request["op"]) ?? ""
|
|
40
|
+
switch op {
|
|
41
|
+
case "set":
|
|
42
|
+
guard let record = PNViewRegistry.shared.resolve(tag), let prop = PNProps.string(request["prop"]) else { return nil }
|
|
43
|
+
record.manager.setAnimatedProperty(view: record.view, prop: prop, value: request["value"])
|
|
44
|
+
return nil
|
|
45
|
+
case "start":
|
|
46
|
+
guard let record = PNViewRegistry.shared.resolve(tag), let prop = PNProps.string(request["prop"]),
|
|
47
|
+
let id = PNProps.int(request["id"])
|
|
48
|
+
else { return ["ok": false] }
|
|
49
|
+
let spec = PNProps.dict(request["spec"]) ?? [:]
|
|
50
|
+
let ok = record.manager.startAnimation(view: record.view, id: Int64(id), prop: prop, spec: spec)
|
|
51
|
+
return ["ok": ok]
|
|
52
|
+
case "cancel":
|
|
53
|
+
guard let id = PNProps.int(request["id"]) else { return nil }
|
|
54
|
+
let value: Any?
|
|
55
|
+
if let record = PNViewRegistry.shared.resolve(tag) {
|
|
56
|
+
value = record.manager.cancelAnimation(view: record.view, id: Int64(id))
|
|
57
|
+
} else {
|
|
58
|
+
value = cancel(id: Int64(id))
|
|
59
|
+
}
|
|
60
|
+
guard let value = value else { return nil }
|
|
61
|
+
return ["value": value]
|
|
62
|
+
default:
|
|
63
|
+
PNLog.rateLimited(PNLog.animation, key: "op:\(op)", "unknown animate op '\(op)'")
|
|
64
|
+
return nil
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// MARK: - Frame application
|
|
69
|
+
|
|
70
|
+
/// Apply one value of `prop` to `view` immediately.
|
|
71
|
+
public func applyValue(view: UIView, prop: String, value: Any?) {
|
|
72
|
+
switch prop {
|
|
73
|
+
case "opacity":
|
|
74
|
+
if let v = PNProps.double(value) { view.alpha = CGFloat(v) }
|
|
75
|
+
case "background_color":
|
|
76
|
+
if let color = PNColor.parse(value) { view.backgroundColor = color }
|
|
77
|
+
case "color":
|
|
78
|
+
guard let color = PNColor.parse(value) else { return }
|
|
79
|
+
if let label = view as? UILabel {
|
|
80
|
+
label.textColor = color
|
|
81
|
+
} else if let field = view as? UITextField {
|
|
82
|
+
field.textColor = color
|
|
83
|
+
} else if let textView = view as? UITextView {
|
|
84
|
+
textView.textColor = color
|
|
85
|
+
} else if let button = view as? UIButton {
|
|
86
|
+
button.setTitleColor(color, for: .normal)
|
|
87
|
+
}
|
|
88
|
+
case "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate":
|
|
89
|
+
guard let v = PNProps.double(value), v.isFinite else { return }
|
|
90
|
+
PNTransform.apply(view, spec: [[prop: v]])
|
|
91
|
+
default:
|
|
92
|
+
PNLog.once(PNLog.animation, key: "prop:\(prop)", "ignoring unknown animated prop '\(prop)'")
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Read the value of `prop` currently on screen (presentation layer when animating).
|
|
97
|
+
public func presentationValue(view: UIView, prop: String) -> Any? {
|
|
98
|
+
let layer = view.layer.presentation() ?? view.layer
|
|
99
|
+
switch prop {
|
|
100
|
+
case "opacity":
|
|
101
|
+
return Double(layer.opacity)
|
|
102
|
+
case "background_color":
|
|
103
|
+
return layer.backgroundColor.map { PNColor.hexString(UIColor(cgColor: $0)) }
|
|
104
|
+
case "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate":
|
|
105
|
+
let t = layer.affineTransform()
|
|
106
|
+
let parts = PNTransform.decompose(t)
|
|
107
|
+
switch prop {
|
|
108
|
+
case "translate_x": return Double(parts.translateX)
|
|
109
|
+
case "translate_y": return Double(parts.translateY)
|
|
110
|
+
case "scale", "scale_x": return Double(parts.scaleX)
|
|
111
|
+
case "scale_y": return Double(parts.scaleY)
|
|
112
|
+
default: return Double(parts.rotateDegrees)
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return nil
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// MARK: - Start / cancel
|
|
120
|
+
|
|
121
|
+
/// Start a native animation. Returns `false` when the spec must be
|
|
122
|
+
/// ticked by Python instead.
|
|
123
|
+
public func start(view: UIView, id: Int64, prop: String, spec: [String: Any]) -> Bool {
|
|
124
|
+
guard PNAnimator.animatableProps.contains(prop), Thread.isMainThread else { return false }
|
|
125
|
+
let kind = PNProps.string(spec["kind"]) ?? ""
|
|
126
|
+
let entry = Running(view: view, prop: prop)
|
|
127
|
+
switch kind {
|
|
128
|
+
case "timing", "spring":
|
|
129
|
+
guard spec["to"] != nil else { return false }
|
|
130
|
+
if let from = spec["from"] { applyValue(view: view, prop: prop, value: from) }
|
|
131
|
+
let timing = PNAnimator.timingParameters(kind: kind, spec: spec)
|
|
132
|
+
let animator = UIViewPropertyAnimator(duration: timing.duration, timingParameters: timing.parameters)
|
|
133
|
+
animator.isUserInteractionEnabled = true
|
|
134
|
+
animator.addAnimations { [weak self, weak view] in
|
|
135
|
+
guard let self = self, let view = view else { return }
|
|
136
|
+
self.applyValue(view: view, prop: prop, value: spec["to"])
|
|
137
|
+
}
|
|
138
|
+
animator.addCompletion { [weak self] position in
|
|
139
|
+
guard let self = self, self.running[id] != nil else { return }
|
|
140
|
+
self.running.removeValue(forKey: id)
|
|
141
|
+
PNAnimator.reportCompletion(id: id, finished: position == .end)
|
|
142
|
+
}
|
|
143
|
+
entry.animator = animator
|
|
144
|
+
running[id] = entry
|
|
145
|
+
let delay = max(0, (PNProps.double(spec["delay_ms"]) ?? 0) / 1000)
|
|
146
|
+
animator.startAnimation(afterDelay: delay)
|
|
147
|
+
return true
|
|
148
|
+
case "decay":
|
|
149
|
+
guard let from = PNProps.double(spec["from"]), let velocity = PNProps.double(spec["velocity"]) else { return false }
|
|
150
|
+
let deceleration = max(1e-6, PNProps.double(spec["deceleration"]) ?? 0.997)
|
|
151
|
+
let driver = PNDecayDriver(from: from, velocity: velocity, deceleration: deceleration) { [weak self, weak view] value in
|
|
152
|
+
guard let self = self, let view = view else { return }
|
|
153
|
+
self.applyValue(view: view, prop: prop, value: value)
|
|
154
|
+
} completion: { [weak self] in
|
|
155
|
+
guard let self = self, self.running[id] != nil else { return }
|
|
156
|
+
self.running.removeValue(forKey: id)
|
|
157
|
+
PNAnimator.reportCompletion(id: id, finished: true)
|
|
158
|
+
}
|
|
159
|
+
entry.decay = driver
|
|
160
|
+
running[id] = entry
|
|
161
|
+
driver.start()
|
|
162
|
+
return true
|
|
163
|
+
default:
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// Stop animation `id`, leaving the view at its presentation value.
|
|
169
|
+
/// Returns that value (or `nil` when the animation isn't running).
|
|
170
|
+
public func cancel(id: Int64) -> Any? {
|
|
171
|
+
guard let entry = running.removeValue(forKey: id) else { return nil }
|
|
172
|
+
var value: Any?
|
|
173
|
+
if let view = entry.view {
|
|
174
|
+
value = presentationValue(view: view, prop: entry.prop)
|
|
175
|
+
}
|
|
176
|
+
if let animator = entry.animator {
|
|
177
|
+
animator.stopAnimation(true)
|
|
178
|
+
}
|
|
179
|
+
if let decay = entry.decay {
|
|
180
|
+
value = decay.currentValue
|
|
181
|
+
decay.stop()
|
|
182
|
+
}
|
|
183
|
+
if let view = entry.view, let value = value {
|
|
184
|
+
view.layer.removeAllAnimations()
|
|
185
|
+
applyValue(view: view, prop: entry.prop, value: value)
|
|
186
|
+
}
|
|
187
|
+
return value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Drop bookkeeping for animations targeting `view` (on destroy).
|
|
191
|
+
public func forget(view: UIView) {
|
|
192
|
+
for (id, entry) in running where entry.view === view || entry.view == nil {
|
|
193
|
+
entry.animator?.stopAnimation(true)
|
|
194
|
+
entry.decay?.stop()
|
|
195
|
+
running.removeValue(forKey: id)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// MARK: - Helpers
|
|
200
|
+
|
|
201
|
+
static func reportCompletion(id: Int64, finished: Bool) {
|
|
202
|
+
PNBridge.shared.callPython(kind: "animation", tag: 0, name: "", payload: PNJSON.encode(["id": id, "finished": finished]))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/// Resolve `timing` / `spring` specs to UIKit timing parameters.
|
|
206
|
+
static func timingParameters(kind: String, spec: [String: Any]) -> (duration: TimeInterval, parameters: UITimingCurveProvider) {
|
|
207
|
+
if kind == "spring" {
|
|
208
|
+
let stiffness = max(1e-3, PNProps.double(spec["stiffness"]) ?? 100)
|
|
209
|
+
let damping = max(1e-3, PNProps.double(spec["damping"]) ?? 10)
|
|
210
|
+
let mass = max(1e-3, PNProps.double(spec["mass"]) ?? 1)
|
|
211
|
+
let omega0 = sqrt(stiffness / mass)
|
|
212
|
+
let zeta = damping / (2 * sqrt(stiffness * mass))
|
|
213
|
+
let duration = zeta < 1 ? min(10, max(0.15, 4 / max(0.05, zeta * omega0))) : min(10, max(0.15, 4 / omega0))
|
|
214
|
+
let distance = abs((PNProps.double(spec["to"]) ?? 0) - (PNProps.double(spec["from"]) ?? 0))
|
|
215
|
+
let v0 = PNProps.double(spec["initial_velocity"]) ?? 0
|
|
216
|
+
let normalized = distance > 1e-9 ? v0 / distance : 0
|
|
217
|
+
let parameters = UISpringTimingParameters(
|
|
218
|
+
mass: CGFloat(mass), stiffness: CGFloat(stiffness), damping: CGFloat(damping),
|
|
219
|
+
initialVelocity: CGVector(dx: normalized, dy: normalized)
|
|
220
|
+
)
|
|
221
|
+
return (duration, parameters)
|
|
222
|
+
}
|
|
223
|
+
let duration = max(0, (PNProps.double(spec["duration_ms"]) ?? 300) / 1000)
|
|
224
|
+
return (duration, curve(for: spec["easing"]))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// Map an easing name (or `[x1, y1, x2, y2]` control points) to a timing curve.
|
|
228
|
+
static func curve(for easing: Any?) -> UITimingCurveProvider {
|
|
229
|
+
if let points = easing as? [Any], points.count == 4 {
|
|
230
|
+
let values = points.compactMap { PNProps.double($0) }
|
|
231
|
+
if values.count == 4 {
|
|
232
|
+
return UICubicTimingParameters(
|
|
233
|
+
controlPoint1: CGPoint(x: values[0], y: values[1]), controlPoint2: CGPoint(x: values[2], y: values[3])
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
switch PNProps.string(easing) ?? "ease_in_out" {
|
|
238
|
+
case "linear": return UICubicTimingParameters(animationCurve: .linear)
|
|
239
|
+
case "ease_in", "ease_in_quad": return UICubicTimingParameters(animationCurve: .easeIn)
|
|
240
|
+
case "ease_out", "ease_out_quad": return UICubicTimingParameters(animationCurve: .easeOut)
|
|
241
|
+
case "ease", "ease_in_out": return UICubicTimingParameters(animationCurve: .easeInOut)
|
|
242
|
+
case "bounce":
|
|
243
|
+
// No CoreAnimation equivalent; approximate with an overshoot curve.
|
|
244
|
+
return UICubicTimingParameters(controlPoint1: CGPoint(x: 0.34, y: 1.56), controlPoint2: CGPoint(x: 0.64, y: 1))
|
|
245
|
+
default: return UICubicTimingParameters(animationCurve: .easeInOut)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// Integrates the `decay` model on a `CADisplayLink`, mirroring the
|
|
251
|
+
/// Python ticker: `v(t) = v0 * e^(-k * 1000 * t)` with `t` in seconds and
|
|
252
|
+
/// `v0` in units per second, so the closed-form position is
|
|
253
|
+
/// `from + v0 / (k * 1000) * (1 - e^(-k * 1000 * t))`.
|
|
254
|
+
final class PNDecayDriver {
|
|
255
|
+
private(set) var currentValue: Double
|
|
256
|
+
private let from: Double
|
|
257
|
+
private let velocity0: Double
|
|
258
|
+
private let k: Double
|
|
259
|
+
private let frame: (Double) -> Void
|
|
260
|
+
private let completion: () -> Void
|
|
261
|
+
private var link: CADisplayLink?
|
|
262
|
+
private var startTime: CFTimeInterval = 0
|
|
263
|
+
private let restVelocity = 0.001
|
|
264
|
+
|
|
265
|
+
init(from: Double, velocity: Double, deceleration: Double, frame: @escaping (Double) -> Void, completion: @escaping () -> Void) {
|
|
266
|
+
self.from = from
|
|
267
|
+
currentValue = from
|
|
268
|
+
velocity0 = velocity
|
|
269
|
+
k = deceleration
|
|
270
|
+
self.frame = frame
|
|
271
|
+
self.completion = completion
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func start() {
|
|
275
|
+
startTime = CACurrentMediaTime()
|
|
276
|
+
if abs(velocity0) < restVelocity {
|
|
277
|
+
completion()
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
let link = CADisplayLink(target: self, selector: #selector(tick(_:)))
|
|
281
|
+
link.add(to: .main, forMode: .common)
|
|
282
|
+
self.link = link
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
func stop() {
|
|
286
|
+
link?.invalidate()
|
|
287
|
+
link = nil
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@objc private func tick(_ link: CADisplayLink) {
|
|
291
|
+
let elapsed = max(0, link.targetTimestamp - startTime)
|
|
292
|
+
let decayFactor = exp(-k * 1000 * elapsed)
|
|
293
|
+
let velocity = velocity0 * decayFactor
|
|
294
|
+
currentValue = from + velocity0 / (k * 1000) * (1 - decayFactor)
|
|
295
|
+
frame(currentValue)
|
|
296
|
+
if abs(velocity) < restVelocity {
|
|
297
|
+
stop()
|
|
298
|
+
completion()
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// The version of the wire protocol compiled into this library. Python
|
|
5
|
+
/// refuses to start when `pythonnative.bridge.PROTOCOL_VERSION` differs.
|
|
6
|
+
public let PNProtocolVersion: Int32 = 1
|
|
7
|
+
|
|
8
|
+
/// The C signature Python registers through `pn_bridge_set_callback`.
|
|
9
|
+
///
|
|
10
|
+
/// `(kind, tag, name, payload_json) -> optional JSON string owned by
|
|
11
|
+
/// Python and valid until the next callback returns`.
|
|
12
|
+
public typealias PNCallbackFn = @convention(c) (
|
|
13
|
+
UnsafePointer<CChar>?, Int64, UnsafePointer<CChar>?, UnsafePointer<CChar>?
|
|
14
|
+
) -> UnsafePointer<CChar>?
|
|
15
|
+
|
|
16
|
+
/// Swift-side entry point for the reverse direction (native -> Python) and
|
|
17
|
+
/// the owner of the registered C callback.
|
|
18
|
+
public final class PNBridge {
|
|
19
|
+
public static let shared = PNBridge()
|
|
20
|
+
|
|
21
|
+
private var callback: PNCallbackFn?
|
|
22
|
+
private var registrationWaiters: [() -> Void] = []
|
|
23
|
+
|
|
24
|
+
private init() {
|
|
25
|
+
// Reference every exported entry point so the linker never
|
|
26
|
+
// dead-strips them out of the host executable; Python resolves
|
|
27
|
+
// them through `dlsym` at runtime.
|
|
28
|
+
_ = PNBridge.exportedSymbols
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Whether Python has registered its callback yet.
|
|
32
|
+
public var hasCallback: Bool { callback != nil }
|
|
33
|
+
|
|
34
|
+
/// Install (or clear) the Python callback. Runs any pending
|
|
35
|
+
/// `whenCallbackRegistered` blocks when a callback becomes available.
|
|
36
|
+
public func setCallback(_ fn: PNCallbackFn?) {
|
|
37
|
+
callback = fn
|
|
38
|
+
guard fn != nil else { return }
|
|
39
|
+
let waiters = registrationWaiters
|
|
40
|
+
registrationWaiters = []
|
|
41
|
+
for waiter in waiters { waiter() }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Run `block` immediately if the callback is registered, otherwise
|
|
45
|
+
/// once it is. Used to buffer deep links and other early events.
|
|
46
|
+
public func whenCallbackRegistered(_ block: @escaping () -> Void) {
|
|
47
|
+
if callback != nil {
|
|
48
|
+
block()
|
|
49
|
+
} else {
|
|
50
|
+
registrationWaiters.append(block)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Invoke the Python callback. Returns the JSON string Python returned
|
|
55
|
+
/// (copied immediately), or `nil` when Python returned NULL or no
|
|
56
|
+
/// callback is registered.
|
|
57
|
+
@discardableResult
|
|
58
|
+
public func callPython(kind: String, tag: Int64, name: String, payload: String) -> String? {
|
|
59
|
+
PNBridge.ensureMainThread("callPython(\(kind))")
|
|
60
|
+
guard let callback = callback else {
|
|
61
|
+
PNLog.rateLimited(PNLog.bridge, key: "no-callback", "callPython(\(kind), \(name)) dropped: no Python callback registered")
|
|
62
|
+
return nil
|
|
63
|
+
}
|
|
64
|
+
let result: UnsafePointer<CChar>? = kind.withCString { kindPtr in
|
|
65
|
+
name.withCString { namePtr in
|
|
66
|
+
payload.withCString { payloadPtr in
|
|
67
|
+
callback(kindPtr, tag, namePtr, payloadPtr)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
guard let result = result else { return nil }
|
|
72
|
+
return String(cString: result)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Emit a view event: `callback("event", tag, name, args_array_json)`.
|
|
76
|
+
@discardableResult
|
|
77
|
+
public func emitEvent(tag: Int64, name: String, args: [Any?]) -> String? {
|
|
78
|
+
callPython(kind: "event", tag: tag, name: name, payload: PNJSON.encode(args))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Log when an entry point is reached off the main thread. Python
|
|
82
|
+
/// always calls from the main thread, so this only guards misuse.
|
|
83
|
+
static func ensureMainThread(_ context: String) {
|
|
84
|
+
if !Thread.isMainThread {
|
|
85
|
+
PNLog.rateLimited(PNLog.bridge, key: "off-main:\(context)", "\(context) called off the main thread; running inline")
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Copy a Swift string into a `strdup`'d buffer Python frees via `pn_bridge_free`.
|
|
90
|
+
static func duplicate(_ string: String?) -> UnsafeMutablePointer<CChar>? {
|
|
91
|
+
guard let string = string else { return nil }
|
|
92
|
+
return strdup(string)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private static let exportedSymbols: [Any] = [
|
|
96
|
+
pn_bridge_apply as Any,
|
|
97
|
+
pn_bridge_measure as Any,
|
|
98
|
+
pn_bridge_command as Any,
|
|
99
|
+
pn_bridge_animate as Any,
|
|
100
|
+
pn_bridge_call as Any,
|
|
101
|
+
pn_bridge_free as Any,
|
|
102
|
+
pn_bridge_set_callback as Any,
|
|
103
|
+
pn_bridge_protocol_version as Any,
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// MARK: - C exports
|
|
108
|
+
|
|
109
|
+
/// Apply one serialized transaction (a JSON array of ops).
|
|
110
|
+
@_cdecl("pn_bridge_apply")
|
|
111
|
+
public func pn_bridge_apply(_ transactionJSON: UnsafePointer<CChar>?) {
|
|
112
|
+
PNBridge.ensureMainThread("pn_bridge_apply")
|
|
113
|
+
guard let transactionJSON = transactionJSON else { return }
|
|
114
|
+
PNTransaction.apply(String(cString: transactionJSON))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Measure the view with `tag` under the given constraints (`1e6` means unconstrained).
|
|
118
|
+
@_cdecl("pn_bridge_measure")
|
|
119
|
+
public func pn_bridge_measure(
|
|
120
|
+
_ tag: Int64, _ maxW: Double, _ maxH: Double,
|
|
121
|
+
_ outW: UnsafeMutablePointer<Double>?, _ outH: UnsafeMutablePointer<Double>?
|
|
122
|
+
) {
|
|
123
|
+
PNBridge.ensureMainThread("pn_bridge_measure")
|
|
124
|
+
var size = CGSize.zero
|
|
125
|
+
if let record = PNViewRegistry.shared.resolve(tag) {
|
|
126
|
+
size = record.manager.measure(view: record.view, maxW: CGFloat(maxW), maxH: CGFloat(maxH))
|
|
127
|
+
}
|
|
128
|
+
outW?.pointee = size.width.isFinite ? Double(size.width) : 0
|
|
129
|
+
outH?.pointee = size.height.isFinite ? Double(size.height) : 0
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// Run an imperative command on one view; returns an optional JSON result.
|
|
133
|
+
@_cdecl("pn_bridge_command")
|
|
134
|
+
public func pn_bridge_command(
|
|
135
|
+
_ tag: Int64, _ name: UnsafePointer<CChar>?, _ argsJSON: UnsafePointer<CChar>?
|
|
136
|
+
) -> UnsafeMutablePointer<CChar>? {
|
|
137
|
+
PNBridge.ensureMainThread("pn_bridge_command")
|
|
138
|
+
guard let name = name, let record = PNViewRegistry.shared.resolve(tag) else { return nil }
|
|
139
|
+
let args = PNJSON.decodeObject(argsJSON.map { String(cString: $0) })
|
|
140
|
+
let result = record.manager.command(view: record.view, name: String(cString: name), args: args)
|
|
141
|
+
guard let result = result else { return nil }
|
|
142
|
+
return PNBridge.duplicate(PNJSON.encode(result))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Drive a view's animatable properties (`set` / `start` / `cancel`).
|
|
146
|
+
@_cdecl("pn_bridge_animate")
|
|
147
|
+
public func pn_bridge_animate(
|
|
148
|
+
_ tag: Int64, _ requestJSON: UnsafePointer<CChar>?
|
|
149
|
+
) -> UnsafeMutablePointer<CChar>? {
|
|
150
|
+
PNBridge.ensureMainThread("pn_bridge_animate")
|
|
151
|
+
let request = PNJSON.decodeObject(requestJSON.map { String(cString: $0) })
|
|
152
|
+
let result = PNAnimator.shared.handle(tag: tag, request: request)
|
|
153
|
+
guard let result = result else { return nil }
|
|
154
|
+
return PNBridge.duplicate(PNJSON.encode(result))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// Call a native module method. The result is `{"ok":..}`, `{"pending":true}`, or an error envelope.
|
|
158
|
+
@_cdecl("pn_bridge_call")
|
|
159
|
+
public func pn_bridge_call(
|
|
160
|
+
_ module: UnsafePointer<CChar>?, _ method: UnsafePointer<CChar>?, _ argsJSON: UnsafePointer<CChar>?
|
|
161
|
+
) -> UnsafeMutablePointer<CChar>? {
|
|
162
|
+
PNBridge.ensureMainThread("pn_bridge_call")
|
|
163
|
+
guard let module = module, let method = method else {
|
|
164
|
+
return PNBridge.duplicate(PNJSON.encode(["ok": false, "error": "missing module or method"]))
|
|
165
|
+
}
|
|
166
|
+
let envelope = PNJSON.decodeObject(argsJSON.map { String(cString: $0) })
|
|
167
|
+
let result = PNModuleDispatcher.shared.call(
|
|
168
|
+
module: String(cString: module), method: String(cString: method), envelope: envelope
|
|
169
|
+
)
|
|
170
|
+
return PNBridge.duplicate(PNJSON.encode(result))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Free a string previously returned by a `pn_bridge_*` function.
|
|
174
|
+
@_cdecl("pn_bridge_free")
|
|
175
|
+
public func pn_bridge_free(_ ptr: UnsafeMutablePointer<CChar>?) {
|
|
176
|
+
free(ptr)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// Register the Python callback used for every native -> Python message.
|
|
180
|
+
@_cdecl("pn_bridge_set_callback")
|
|
181
|
+
public func pn_bridge_set_callback(_ fn: PNCallbackFn?) {
|
|
182
|
+
PNBridge.ensureMainThread("pn_bridge_set_callback")
|
|
183
|
+
PNRegistry.shared.ensureBuiltins()
|
|
184
|
+
PNBridge.shared.setCallback(fn)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// The protocol version this library speaks.
|
|
188
|
+
@_cdecl("pn_bridge_protocol_version")
|
|
189
|
+
public func pn_bridge_protocol_version() -> Int32 {
|
|
190
|
+
PNProtocolVersion
|
|
191
|
+
}
|
pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// A native plugin bundled into the app by `pn build`. The generated
|
|
4
|
+
/// `PNGeneratedPlugins.registerAll(into:)` calls each entry's `register`.
|
|
5
|
+
public protocol PNPlugin {
|
|
6
|
+
/// Register component managers and native modules.
|
|
7
|
+
static func register(into registry: PNRegistry)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Component-manager and native-module registry.
|
|
11
|
+
///
|
|
12
|
+
/// Built-ins register lazily on first use; plugins register through
|
|
13
|
+
/// `PNPlugin.register(into:)`. Component factories are invoked once per
|
|
14
|
+
/// element type and the resulting manager is shared by every view of
|
|
15
|
+
/// that type (managers are stateless; per-view state lives in
|
|
16
|
+
/// `PNViewState`).
|
|
17
|
+
public final class PNRegistry {
|
|
18
|
+
public static let shared = PNRegistry()
|
|
19
|
+
|
|
20
|
+
private var componentFactories: [String: () -> PNComponentManager] = [:]
|
|
21
|
+
private var componentManagers: [String: PNComponentManager] = [:]
|
|
22
|
+
private var moduleTypes: [String: PNNativeModule.Type] = [:]
|
|
23
|
+
private var moduleInstances: [String: PNNativeModule] = [:]
|
|
24
|
+
private var builtinsRegistered = false
|
|
25
|
+
private let placeholder = PNPlaceholderManager()
|
|
26
|
+
|
|
27
|
+
private init() {}
|
|
28
|
+
|
|
29
|
+
// MARK: - Registration
|
|
30
|
+
|
|
31
|
+
/// Register (or replace) the manager factory for element type `name`.
|
|
32
|
+
public func registerComponent(_ name: String, factory: @escaping () -> PNComponentManager) {
|
|
33
|
+
componentFactories[name] = factory
|
|
34
|
+
componentManagers.removeValue(forKey: name)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Register (or replace) a native module by its `name`.
|
|
38
|
+
public func registerModule(_ type: PNNativeModule.Type) {
|
|
39
|
+
moduleTypes[type.name] = type
|
|
40
|
+
moduleInstances.removeValue(forKey: type.name)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Element type names currently registered.
|
|
44
|
+
public var componentNames: [String] {
|
|
45
|
+
ensureBuiltins()
|
|
46
|
+
return Array(componentFactories.keys).sorted()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Module names currently registered.
|
|
50
|
+
public var moduleNames: [String] {
|
|
51
|
+
ensureBuiltins()
|
|
52
|
+
return Array(moduleTypes.keys).sorted()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// MARK: - Lookup
|
|
56
|
+
|
|
57
|
+
/// The shared manager for `type`, or a placeholder manager for unknown types.
|
|
58
|
+
public func manager(for type: String) -> PNComponentManager {
|
|
59
|
+
ensureBuiltins()
|
|
60
|
+
if let existing = componentManagers[type] { return existing }
|
|
61
|
+
if let factory = componentFactories[type] {
|
|
62
|
+
let manager = factory()
|
|
63
|
+
componentManagers[type] = manager
|
|
64
|
+
return manager
|
|
65
|
+
}
|
|
66
|
+
PNLog.once(PNLog.components, key: "unknown-type:\(type)", "unknown element type '\(type)'; using a placeholder UIView")
|
|
67
|
+
return placeholder
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// The module instance registered under `name`, created on first use.
|
|
71
|
+
public func module(named name: String) -> PNNativeModule? {
|
|
72
|
+
ensureBuiltins()
|
|
73
|
+
if let existing = moduleInstances[name] { return existing }
|
|
74
|
+
guard let type = moduleTypes[name] else { return nil }
|
|
75
|
+
let instance = type.init()
|
|
76
|
+
moduleInstances[name] = instance
|
|
77
|
+
return instance
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// MARK: - Built-ins
|
|
81
|
+
|
|
82
|
+
/// Register every built-in manager and module, then the generated plugins.
|
|
83
|
+
public func ensureBuiltins() {
|
|
84
|
+
if builtinsRegistered { return }
|
|
85
|
+
builtinsRegistered = true
|
|
86
|
+
PNBuiltins.register(into: self)
|
|
87
|
+
PNGeneratedPlugins.registerAll(into: self)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Registration of the components and modules that ship with PythonNativeKit.
|
|
92
|
+
enum PNBuiltins {
|
|
93
|
+
static func register(into registry: PNRegistry) {
|
|
94
|
+
let flex: () -> PNComponentManager = { PNViewManager() }
|
|
95
|
+
registry.registerComponent("View", factory: flex)
|
|
96
|
+
registry.registerComponent("Column", factory: flex)
|
|
97
|
+
registry.registerComponent("Row", factory: flex)
|
|
98
|
+
registry.registerComponent("Text") { PNTextManager() }
|
|
99
|
+
registry.registerComponent("Button") { PNButtonManager() }
|
|
100
|
+
registry.registerComponent("TextInput") { PNTextInputManager() }
|
|
101
|
+
registry.registerComponent("Image") { PNImageManager() }
|
|
102
|
+
registry.registerComponent("Switch") { PNSwitchManager() }
|
|
103
|
+
registry.registerComponent("ProgressBar") { PNProgressBarManager() }
|
|
104
|
+
registry.registerComponent("ActivityIndicator") { PNActivityIndicatorManager() }
|
|
105
|
+
registry.registerComponent("WebView") { PNWebViewManager() }
|
|
106
|
+
registry.registerComponent("Spacer") { PNSpacerManager() }
|
|
107
|
+
registry.registerComponent("ScrollView") { PNScrollViewManager() }
|
|
108
|
+
registry.registerComponent("SafeAreaView") { PNSafeAreaViewManager() }
|
|
109
|
+
registry.registerComponent("Modal") { PNModalManager() }
|
|
110
|
+
registry.registerComponent("Portal") { PNPortalManager() }
|
|
111
|
+
registry.registerComponent("Slider") { PNSliderManager() }
|
|
112
|
+
registry.registerComponent("TabBar") { PNTabBarManager() }
|
|
113
|
+
registry.registerComponent("Pressable") { PNPressableManager() }
|
|
114
|
+
registry.registerComponent("StatusBar") { PNStatusBarManager() }
|
|
115
|
+
registry.registerComponent("KeyboardAvoidingView") { PNKeyboardAvoidingViewManager() }
|
|
116
|
+
registry.registerComponent("Picker") { PNPickerManager() }
|
|
117
|
+
registry.registerComponent("Checkbox") { PNCheckboxManager() }
|
|
118
|
+
registry.registerComponent("SegmentedControl") { PNSegmentedControlManager() }
|
|
119
|
+
registry.registerComponent("DatePicker") { PNDatePickerManager() }
|
|
120
|
+
registry.registerComponent("VirtualList") { PNVirtualListManager() }
|
|
121
|
+
|
|
122
|
+
registry.registerModule(HostModule.self)
|
|
123
|
+
registry.registerModule(DeviceModule.self)
|
|
124
|
+
registry.registerModule(AlertModule.self)
|
|
125
|
+
registry.registerModule(StorageModule.self)
|
|
126
|
+
registry.registerModule(SecureStoreModule.self)
|
|
127
|
+
registry.registerModule(ClipboardModule.self)
|
|
128
|
+
registry.registerModule(ShareModule.self)
|
|
129
|
+
registry.registerModule(LinkingModule.self)
|
|
130
|
+
registry.registerModule(HapticsModule.self)
|
|
131
|
+
registry.registerModule(BatteryModule.self)
|
|
132
|
+
registry.registerModule(NetInfoModule.self)
|
|
133
|
+
registry.registerModule(AppStateModule.self)
|
|
134
|
+
registry.registerModule(PermissionsModule.self)
|
|
135
|
+
registry.registerModule(NotificationsModule.self)
|
|
136
|
+
registry.registerModule(CameraModule.self)
|
|
137
|
+
registry.registerModule(LocationModule.self)
|
|
138
|
+
registry.registerModule(BiometricsModule.self)
|
|
139
|
+
}
|
|
140
|
+
}
|