pythonnative 0.40.0__cp313-cp313-win_amd64.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 +411 -0
- pythonnative/_ios_log.py +92 -0
- pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
- pythonnative/alerts.py +236 -0
- pythonnative/animated.py +1687 -0
- pythonnative/animation_graph.py +78 -0
- pythonnative/appearance.py +124 -0
- pythonnative/bootstrap.py +100 -0
- pythonnative/bridge/__init__.py +302 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +170 -0
- pythonnative/bridge/commits.py +114 -0
- pythonnative/bridge/fake.py +243 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/bridge/web.py +489 -0
- pythonnative/cli/__init__.py +7 -0
- pythonnative/cli/pn.py +1214 -0
- pythonnative/component.py +271 -0
- pythonnative/components/__init__.py +111 -0
- pythonnative/components/_base.py +166 -0
- pythonnative/components/controls.py +521 -0
- pythonnative/components/layout.py +518 -0
- pythonnative/components/lists.py +785 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/devclient.py +824 -0
- pythonnative/devserver/__init__.py +34 -0
- pythonnative/devserver/server.py +718 -0
- pythonnative/devserver/static/animation_graph.js +82 -0
- pythonnative/devserver/static/bridge.js +147 -0
- pythonnative/devserver/static/colors.js +70 -0
- pythonnative/devserver/static/host.js +552 -0
- pythonnative/devserver/static/index.html +60 -0
- pythonnative/devserver/static/layout.js +85 -0
- pythonnative/devserver/static/preview.css +415 -0
- pythonnative/devserver/static/renderer.js +1994 -0
- pythonnative/devserver/static/shell.js +345 -0
- pythonnative/devserver/static/yoga/LICENSE +21 -0
- pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
- pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
- pythonnative/devserver/static/yoga/src/index.js +16 -0
- pythonnative/devserver/static/yoga/src/index.js.map +1 -0
- pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
- pythonnative/devserver/static/yoga/src/load.js +17 -0
- pythonnative/devserver/static/yoga/src/load.js.map +1 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
- pythonnative/devserver/watcher.py +267 -0
- pythonnative/devserver/ws.py +421 -0
- pythonnative/diagnostics.py +241 -0
- pythonnative/element.py +159 -0
- pythonnative/equality.py +19 -0
- pythonnative/events.py +231 -0
- pythonnative/gestures.py +1333 -0
- pythonnative/hooks.py +1621 -0
- pythonnative/hosts/__init__.py +63 -0
- pythonnative/hosts/base.py +596 -0
- pythonnative/hosts/native.py +302 -0
- pythonnative/hot_reload.py +585 -0
- pythonnative/layout.py +328 -0
- pythonnative/mutations.py +142 -0
- pythonnative/native/android/build.gradle +52 -0
- pythonnative/native/android/gradle.properties +3 -0
- pythonnative/native/android/settings.gradle +12 -0
- pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
- pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
- pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
- pythonnative/native/ios/Package.swift +23 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/native/yoga/LICENSE +21 -0
- pythonnative/native/yoga/Package.swift +9 -0
- pythonnative/native/yoga/VENDORED.md +2 -0
- pythonnative/native/yoga/include/PNStyle.h +10 -0
- pythonnative/native/yoga/include/module.modulemap +5 -0
- pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/python.cpp +16 -0
- pythonnative/native/yoga/style.cpp +272 -0
- pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
- pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
- pythonnative/native/yoga/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
- pythonnative/native/yoga/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
- pythonnative/native/yoga/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
- pythonnative/native/yoga/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
- pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
- pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
- pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
- pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
- pythonnative/native/yoga/yoga/config/Config.h +92 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
- pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
- pythonnative/native/yoga/yoga/debug/Log.h +34 -0
- pythonnative/native/yoga/yoga/enums/Align.h +47 -0
- pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
- pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
- pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
- pythonnative/native/yoga/yoga/enums/Display.h +41 -0
- pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
- pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
- pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
- pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
- pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
- pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
- pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
- pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
- pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
- pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
- pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
- pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
- pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
- pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
- pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
- pythonnative/native/yoga/yoga/event/event.cpp +87 -0
- pythonnative/native/yoga/yoga/event/event.h +130 -0
- pythonnative/native/yoga/yoga/module.modulemap +21 -0
- pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
- pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
- pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
- pythonnative/native/yoga/yoga/node/Node.h +337 -0
- pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
- pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
- pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
- pythonnative/native/yoga/yoga/style/Style.h +757 -0
- pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
- pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
- pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
- pythonnative/native_modules/__init__.py +124 -0
- pythonnative/native_modules/app_state.py +99 -0
- pythonnative/native_modules/battery.py +75 -0
- pythonnative/native_modules/biometrics.py +42 -0
- pythonnative/native_modules/camera.py +65 -0
- pythonnative/native_modules/clipboard.py +46 -0
- pythonnative/native_modules/fallback.py +324 -0
- pythonnative/native_modules/file_system.py +145 -0
- pythonnative/native_modules/haptics.py +75 -0
- pythonnative/native_modules/linking.py +123 -0
- pythonnative/native_modules/location.py +75 -0
- pythonnative/native_modules/net_info.py +112 -0
- pythonnative/native_modules/notifications.py +129 -0
- pythonnative/native_modules/permissions.py +108 -0
- pythonnative/native_modules/registry.py +526 -0
- pythonnative/native_modules/secure_store.py +52 -0
- pythonnative/native_modules/share.py +51 -0
- pythonnative/native_views/__init__.py +375 -0
- pythonnative/native_views/base.py +316 -0
- pythonnative/native_views/bridge_backend.py +277 -0
- pythonnative/navigation/__init__.py +103 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +540 -0
- pythonnative/navigation/hooks.py +133 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +659 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +269 -0
- pythonnative/net.py +248 -0
- pythonnative/platform.py +166 -0
- pythonnative/platform_metrics.py +252 -0
- pythonnative/preview.py +287 -0
- pythonnative/profiling.py +101 -0
- pythonnative/project/__init__.py +68 -0
- pythonnative/project/android.py +557 -0
- pythonnative/project/builder.py +817 -0
- pythonnative/project/config.py +773 -0
- pythonnative/project/deps.py +719 -0
- pythonnative/project/devices.py +304 -0
- pythonnative/project/doctor.py +283 -0
- pythonnative/project/fingerprint.py +171 -0
- pythonnative/project/icons.py +247 -0
- pythonnative/project/ios.py +325 -0
- pythonnative/project/lockfile.py +100 -0
- pythonnative/project/permissions.py +361 -0
- pythonnative/project/plugins.py +519 -0
- pythonnative/project/runtime_assets.py +183 -0
- pythonnative/py.typed +0 -0
- pythonnative/query.py +153 -0
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +375 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1165 -0
- pythonnative/reconciler/layout_pass.py +393 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/refresh.py +55 -0
- pythonnative/runtime.py +306 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/__init__.py +153 -0
- pythonnative/sdk/_components.py +427 -0
- pythonnative/sdk/builtins.py +91 -0
- pythonnative/sdk/codegen.py +149 -0
- pythonnative/sdk/module_codegen.py +184 -0
- pythonnative/sdk/schema.py +224 -0
- pythonnative/sdk/templates/contracts.kt +46 -0
- pythonnative/sdk/templates/contracts.swift +46 -0
- pythonnative/storage.py +184 -0
- pythonnative/style.py +841 -0
- pythonnative/suspense.py +183 -0
- pythonnative/templates/android_template/app/build.gradle +75 -0
- pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
- pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
- pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
- pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
- pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
- pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
- pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
- pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
- pythonnative/templates/android_template/build.gradle +10 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
- pythonnative/templates/android_template/gradle.properties +23 -0
- pythonnative/templates/android_template/gradlew +185 -0
- pythonnative/templates/android_template/gradlew.bat +89 -0
- pythonnative/templates/android_template/settings.gradle +17 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
- pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
- pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +369 -0
- pythonnative/utils.py +145 -0
- pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
- pythonnative-0.40.0.dist-info/METADATA +150 -0
- pythonnative-0.40.0.dist-info/RECORD +418 -0
- pythonnative-0.40.0.dist-info/WHEEL +5 -0
- pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
- pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
- pythonnative-0.40.0.dist-info/top_level.txt +1 -0
- pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"""The view backend the reconciler commits to.
|
|
2
|
+
|
|
3
|
+
The reconciler talks to exactly one object, obtained from
|
|
4
|
+
[`get_registry`][pythonnative.native_views.get_registry], through a
|
|
5
|
+
small protocol: ``apply_mutations`` (one ordered batch of
|
|
6
|
+
create/update/insert/destroy/frame ops per commit, see
|
|
7
|
+
`pythonnative.mutations`), ``resolve_view``, ``measure_intrinsic``,
|
|
8
|
+
``command``, and the animation hooks. Two implementations exist:
|
|
9
|
+
|
|
10
|
+
- [`BridgeBackend`][pythonnative.native_views.bridge_backend.BridgeBackend]
|
|
11
|
+
(iOS, Android, and the browser preview): serializes each commit and
|
|
12
|
+
hands it to the native runtime through the bridge; the Swift and
|
|
13
|
+
Kotlin component managers, or the preview page's DOM applier, own
|
|
14
|
+
every platform view. Python holds no native objects.
|
|
15
|
+
- [`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
|
|
16
|
+
(tests and SDK handler introspection): maps element type names to
|
|
17
|
+
Python [`ViewHandler`][pythonnative.native_views.base.ViewHandler]
|
|
18
|
+
implementations and owns the tag table itself.
|
|
19
|
+
|
|
20
|
+
Platform selection happens lazily on first use, so this package
|
|
21
|
+
imports on any platform. Tests install a mock with
|
|
22
|
+
[`set_registry`][pythonnative.native_views.set_registry].
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import math
|
|
26
|
+
import sys
|
|
27
|
+
import threading
|
|
28
|
+
import time
|
|
29
|
+
from typing import Any, Dict, Optional, Sequence, Tuple
|
|
30
|
+
|
|
31
|
+
from ..mutations import CreateOp, DestroyOp, InsertOp, Mutation, SetFrameOp, UpdateOp
|
|
32
|
+
from .base import ViewHandler
|
|
33
|
+
|
|
34
|
+
# ======================================================================
|
|
35
|
+
# Tripwire log rate limiter
|
|
36
|
+
# ======================================================================
|
|
37
|
+
#
|
|
38
|
+
# Defensive NaN/Inf guards in ``set_frame`` and ``_apply_transform`` log
|
|
39
|
+
# a single line per occurrence. That's fine for one-off events, but
|
|
40
|
+
# ``Animated.View`` drives transforms at ~60 Hz; once an
|
|
41
|
+
# ``Animated.Value`` enters a stuck NaN state (e.g., a spring tick
|
|
42
|
+
# corrupted across a Fast Refresh), the tripwire would otherwise emit
|
|
43
|
+
# thousands of identical lines per second and drown the dev console.
|
|
44
|
+
#
|
|
45
|
+
# We instead log the first occurrence immediately, then suppress
|
|
46
|
+
# further messages with the same ``label`` for
|
|
47
|
+
# ``_TRIPWIRE_RATE_LIMIT_S`` seconds, and append a
|
|
48
|
+
# ``(+N similar in last Xs)`` suffix to the next message that escapes
|
|
49
|
+
# the window. The first sample plus a count is enough to diagnose; the
|
|
50
|
+
# bounded log keeps the dev console usable.
|
|
51
|
+
|
|
52
|
+
_TRIPWIRE_RATE_LIMIT_S: float = 1.0
|
|
53
|
+
_TRIPWIRE_LOG_LOCK = threading.Lock()
|
|
54
|
+
_TRIPWIRE_LAST_LOG_TIME: Dict[str, float] = {}
|
|
55
|
+
_TRIPWIRE_SUPPRESSED_COUNT: Dict[str, int] = {}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _tripwire_log(label: str, message: str) -> None:
|
|
59
|
+
"""Emit ``message`` to stderr, rate-limited per ``label``.
|
|
60
|
+
|
|
61
|
+
The first call for a given ``label`` always emits. Calls within
|
|
62
|
+
``_TRIPWIRE_RATE_LIMIT_S`` seconds are silently counted. The next
|
|
63
|
+
call after the window appends ``(+N similar in last Xs)`` and
|
|
64
|
+
resets the counter.
|
|
65
|
+
"""
|
|
66
|
+
now = time.monotonic()
|
|
67
|
+
write = False
|
|
68
|
+
suppressed = 0
|
|
69
|
+
with _TRIPWIRE_LOG_LOCK:
|
|
70
|
+
last = _TRIPWIRE_LAST_LOG_TIME.get(label)
|
|
71
|
+
if last is None or now - last >= _TRIPWIRE_RATE_LIMIT_S:
|
|
72
|
+
write = True
|
|
73
|
+
suppressed = _TRIPWIRE_SUPPRESSED_COUNT.get(label, 0)
|
|
74
|
+
_TRIPWIRE_SUPPRESSED_COUNT[label] = 0
|
|
75
|
+
_TRIPWIRE_LAST_LOG_TIME[label] = now
|
|
76
|
+
else:
|
|
77
|
+
_TRIPWIRE_SUPPRESSED_COUNT[label] = _TRIPWIRE_SUPPRESSED_COUNT.get(label, 0) + 1
|
|
78
|
+
if not write:
|
|
79
|
+
return
|
|
80
|
+
if suppressed > 0:
|
|
81
|
+
message = f"{message} (+{suppressed} similar in last {_TRIPWIRE_RATE_LIMIT_S:g}s)"
|
|
82
|
+
try:
|
|
83
|
+
print(message, file=sys.stderr, flush=True)
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ViewRecord:
|
|
89
|
+
"""One live native view tracked by the tag table."""
|
|
90
|
+
|
|
91
|
+
__slots__ = ("tag", "type_name", "view", "handler")
|
|
92
|
+
|
|
93
|
+
def __init__(self, tag: int, type_name: str, view: Any, handler: ViewHandler) -> None:
|
|
94
|
+
self.tag = tag
|
|
95
|
+
self.type_name = type_name
|
|
96
|
+
self.view = view
|
|
97
|
+
self.handler = handler
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class NativeViewRegistry:
|
|
101
|
+
"""Map element type names to handlers and tags to live native views.
|
|
102
|
+
|
|
103
|
+
The reconciler depends only on this protocol: ``apply_mutations``,
|
|
104
|
+
``resolve_view``, ``measure_intrinsic``, and ``command``.
|
|
105
|
+
Implementations may host real platform handlers (Android/iOS/
|
|
106
|
+
browser) or mocks for tests.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(self) -> None:
|
|
110
|
+
self._handlers: Dict[str, ViewHandler] = {}
|
|
111
|
+
self._records: Dict[int, ViewRecord] = {}
|
|
112
|
+
|
|
113
|
+
def register(self, type_name: str, handler: ViewHandler) -> None:
|
|
114
|
+
"""Register `handler` to service elements of type `type_name`.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
type_name: The element type name (e.g., `"Text"`).
|
|
118
|
+
handler: A `ViewHandler` instance for the active platform.
|
|
119
|
+
"""
|
|
120
|
+
self._handlers[type_name] = handler
|
|
121
|
+
|
|
122
|
+
def handler_for(self, type_name: str) -> Optional[ViewHandler]:
|
|
123
|
+
"""Return the handler registered for ``type_name``, if any."""
|
|
124
|
+
return self._handlers.get(type_name)
|
|
125
|
+
|
|
126
|
+
# ------------------------------------------------------------------
|
|
127
|
+
# Tag table
|
|
128
|
+
# ------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
def resolve_view(self, tag: int) -> Any:
|
|
131
|
+
"""Return the native view registered under ``tag``, or ``None``."""
|
|
132
|
+
record = self._records.get(tag)
|
|
133
|
+
return record.view if record is not None else None
|
|
134
|
+
|
|
135
|
+
def record_for(self, tag: int) -> Optional[ViewRecord]:
|
|
136
|
+
"""Return the full [`ViewRecord`][pythonnative.native_views.ViewRecord] for ``tag``."""
|
|
137
|
+
return self._records.get(tag)
|
|
138
|
+
|
|
139
|
+
def live_view_count(self) -> int:
|
|
140
|
+
"""Number of views currently tracked (test/diagnostic helper)."""
|
|
141
|
+
return len(self._records)
|
|
142
|
+
|
|
143
|
+
# ------------------------------------------------------------------
|
|
144
|
+
# The commit channel
|
|
145
|
+
# ------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
def apply_mutations(self, ops: Sequence[Mutation]) -> None:
|
|
148
|
+
"""Apply one commit transaction.
|
|
149
|
+
|
|
150
|
+
Ops are applied strictly in order. Failures are isolated per
|
|
151
|
+
op: a handler exception is logged (rate-limited) and the
|
|
152
|
+
remaining ops still apply, so one bad prop can't desync the
|
|
153
|
+
whole native tree.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
ops: Ordered mutations emitted by the reconciler.
|
|
157
|
+
"""
|
|
158
|
+
for op in ops:
|
|
159
|
+
try:
|
|
160
|
+
self._apply_one(op)
|
|
161
|
+
except Exception as exc:
|
|
162
|
+
_tripwire_log(
|
|
163
|
+
f"apply:{type(op).__name__}",
|
|
164
|
+
f"[PN] apply_mutations: {type(op).__name__} failed: {type(exc).__name__}: {exc!r}",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _apply_one(self, op: Mutation) -> None:
|
|
168
|
+
if isinstance(op, CreateOp):
|
|
169
|
+
handler = self._handlers.get(op.type_name)
|
|
170
|
+
if handler is None:
|
|
171
|
+
raise ValueError(f"Unknown element type: {op.type_name!r}")
|
|
172
|
+
view = handler.create(op.tag, op.props)
|
|
173
|
+
self._records[op.tag] = ViewRecord(op.tag, op.type_name, view, handler)
|
|
174
|
+
return
|
|
175
|
+
if isinstance(op, UpdateOp):
|
|
176
|
+
record = self._records.get(op.tag)
|
|
177
|
+
if record is not None:
|
|
178
|
+
record.handler.update(record.view, op.changed_props)
|
|
179
|
+
return
|
|
180
|
+
if isinstance(op, InsertOp):
|
|
181
|
+
parent = self._records.get(op.parent_tag)
|
|
182
|
+
child = self._records.get(op.child_tag)
|
|
183
|
+
if parent is not None and child is not None:
|
|
184
|
+
parent.handler.insert_child(parent.view, child.view, op.index)
|
|
185
|
+
return
|
|
186
|
+
if isinstance(op, DestroyOp):
|
|
187
|
+
record = self._records.pop(op.tag, None)
|
|
188
|
+
if record is not None:
|
|
189
|
+
record.handler.destroy(record.view)
|
|
190
|
+
return
|
|
191
|
+
if isinstance(op, SetFrameOp):
|
|
192
|
+
self._apply_frame(op)
|
|
193
|
+
return
|
|
194
|
+
raise TypeError(f"Unknown mutation op: {op!r}")
|
|
195
|
+
|
|
196
|
+
def _apply_frame(self, op: SetFrameOp) -> None:
|
|
197
|
+
record = self._records.get(op.tag)
|
|
198
|
+
if record is None:
|
|
199
|
+
return
|
|
200
|
+
# Tripwire: log non-finite layout values so we can diagnose
|
|
201
|
+
# crashes like iOS `CALayerInvalidGeometry` without losing the
|
|
202
|
+
# repro. Handlers are responsible for clamping before applying.
|
|
203
|
+
# Rate-limited via ``_tripwire_log`` to avoid floods when an
|
|
204
|
+
# animated value is stuck at NaN.
|
|
205
|
+
try:
|
|
206
|
+
finite = (
|
|
207
|
+
math.isfinite(op.x) and math.isfinite(op.y) and math.isfinite(op.width) and math.isfinite(op.height)
|
|
208
|
+
)
|
|
209
|
+
except (TypeError, ValueError):
|
|
210
|
+
finite = False
|
|
211
|
+
if not finite:
|
|
212
|
+
_tripwire_log(
|
|
213
|
+
"set_frame:nan",
|
|
214
|
+
f"[set_frame:nan] type={record.type_name!r} x={op.x!r} y={op.y!r} w={op.width!r} h={op.height!r}",
|
|
215
|
+
)
|
|
216
|
+
record.handler.set_frame(record.view, op.x, op.y, op.width, op.height)
|
|
217
|
+
|
|
218
|
+
# ------------------------------------------------------------------
|
|
219
|
+
# Imperative escape hatches (resolved through the tag table)
|
|
220
|
+
# ------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
def measure_intrinsic(
|
|
223
|
+
self,
|
|
224
|
+
tag: int,
|
|
225
|
+
max_width: float,
|
|
226
|
+
max_height: float,
|
|
227
|
+
) -> Tuple[float, float]:
|
|
228
|
+
"""Return the natural ``(width, height)`` of a content-sized view.
|
|
229
|
+
|
|
230
|
+
Used by the layout engine for leaves whose intrinsic size
|
|
231
|
+
depends on their content (text, buttons, images).
|
|
232
|
+
"""
|
|
233
|
+
record = self._records.get(tag)
|
|
234
|
+
if record is None:
|
|
235
|
+
return (0.0, 0.0)
|
|
236
|
+
return record.handler.measure_intrinsic(record.view, max_width, max_height)
|
|
237
|
+
|
|
238
|
+
def command(self, tag: int, name: str, args: Optional[Dict[str, Any]] = None) -> Any:
|
|
239
|
+
"""Execute an imperative command against the view for ``tag``.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
tag: Target view tag.
|
|
243
|
+
name: Command name (handler-specific, e.g.
|
|
244
|
+
``"scroll_to_offset"``).
|
|
245
|
+
args: Optional command arguments.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
The handler's command result, or ``None`` when the tag is
|
|
249
|
+
unknown.
|
|
250
|
+
"""
|
|
251
|
+
record = self._records.get(tag)
|
|
252
|
+
if record is None:
|
|
253
|
+
return None
|
|
254
|
+
return record.handler.command(record.view, name, args or {})
|
|
255
|
+
|
|
256
|
+
def set_animated_property(self, tag: int, prop_name: str, value: Any) -> None:
|
|
257
|
+
"""Apply one Python-driven animation frame to the view for ``tag``."""
|
|
258
|
+
record = self._records.get(tag)
|
|
259
|
+
if record is not None:
|
|
260
|
+
record.handler.set_animated_property(record.view, prop_name, value)
|
|
261
|
+
|
|
262
|
+
def start_animation(self, tag: int, anim_id: int, prop_name: str, spec: Dict[str, Any]) -> bool:
|
|
263
|
+
"""Start a natively-driven animation on the view for ``tag``.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Whether the platform accepted the animation (``False``
|
|
267
|
+
means the caller should drive it from the Python ticker).
|
|
268
|
+
"""
|
|
269
|
+
record = self._records.get(tag)
|
|
270
|
+
if record is None:
|
|
271
|
+
return False
|
|
272
|
+
return bool(record.handler.start_animation(record.view, anim_id, prop_name, spec))
|
|
273
|
+
|
|
274
|
+
def cancel_animation(self, tag: int, anim_id: int) -> Any:
|
|
275
|
+
"""Cancel a natively-driven animation; returns the presentation value if known."""
|
|
276
|
+
record = self._records.get(tag)
|
|
277
|
+
if record is None:
|
|
278
|
+
return None
|
|
279
|
+
return record.handler.cancel_animation(record.view, anim_id)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# ======================================================================
|
|
283
|
+
# Singleton registry
|
|
284
|
+
# ======================================================================
|
|
285
|
+
|
|
286
|
+
_registry: Any = None
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _install_sdk_handlers(registry: Any) -> None:
|
|
290
|
+
"""Copy decorator-registered SDK handlers + entry-point plugins.
|
|
291
|
+
|
|
292
|
+
Imported lazily so unit tests that never touch the SDK don't pay the
|
|
293
|
+
entry-point discovery cost. Only meaningful for the Python-handler
|
|
294
|
+
registry (tests); on the bridge platforms the native runtime owns
|
|
295
|
+
component managers and Python handlers are recorded but never run.
|
|
296
|
+
"""
|
|
297
|
+
try:
|
|
298
|
+
from ..sdk._components import install_into_registry as _sdk_install
|
|
299
|
+
except Exception:
|
|
300
|
+
return
|
|
301
|
+
try:
|
|
302
|
+
_sdk_install(registry)
|
|
303
|
+
except Exception:
|
|
304
|
+
# A misbehaving plugin must not break PythonNative's startup.
|
|
305
|
+
pass
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _create_backend() -> Any:
|
|
309
|
+
"""Build the backend for the active platform.
|
|
310
|
+
|
|
311
|
+
iOS, Android, and the browser preview (``PN_PLATFORM=web``) get the
|
|
312
|
+
bridge backend. Anywhere else (plain unit tests) there is no
|
|
313
|
+
renderer at all, so the registry is empty: tests inject a
|
|
314
|
+
``pythonnative.testing.FakeBackend`` with ``set_registry`` and a
|
|
315
|
+
stray commit raises a clear "unknown element type" rather than
|
|
316
|
+
silently importing a platform module.
|
|
317
|
+
"""
|
|
318
|
+
from ..utils import IS_NATIVE
|
|
319
|
+
|
|
320
|
+
if IS_NATIVE:
|
|
321
|
+
from .bridge_backend import BridgeBackend
|
|
322
|
+
|
|
323
|
+
return BridgeBackend()
|
|
324
|
+
registry = NativeViewRegistry()
|
|
325
|
+
_install_sdk_handlers(registry)
|
|
326
|
+
return registry
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_registry() -> Any:
|
|
330
|
+
"""Return the process-wide view backend, creating it on first use.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
A [`BridgeBackend`][pythonnative.native_views.bridge_backend.BridgeBackend]
|
|
334
|
+
on every bridge platform, otherwise a `NativeViewRegistry`
|
|
335
|
+
holding every decorator-registered SDK handler and any handlers
|
|
336
|
+
exposed by third-party packages via the
|
|
337
|
+
[`pythonnative.handlers`][pythonnative.sdk.ENTRY_POINT_GROUP]
|
|
338
|
+
entry point group.
|
|
339
|
+
"""
|
|
340
|
+
global _registry
|
|
341
|
+
if _registry is not None:
|
|
342
|
+
return _registry
|
|
343
|
+
_registry = _create_backend()
|
|
344
|
+
return _registry
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def refresh_registry() -> Any:
|
|
348
|
+
"""Re-run SDK handler installation against the existing backend.
|
|
349
|
+
|
|
350
|
+
Call this after registering a new component at runtime if the
|
|
351
|
+
registry has already been instantiated. This is mostly useful in
|
|
352
|
+
REPL sessions and tests; the normal flow is "register, then call
|
|
353
|
+
[`get_registry`][pythonnative.native_views.get_registry]" and the
|
|
354
|
+
handlers come along automatically.
|
|
355
|
+
"""
|
|
356
|
+
registry = get_registry()
|
|
357
|
+
_install_sdk_handlers(registry)
|
|
358
|
+
return registry
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def set_registry(registry: Any) -> None:
|
|
362
|
+
"""Install a custom backend (primarily for testing).
|
|
363
|
+
|
|
364
|
+
Replaces the lazy singleton so subsequent
|
|
365
|
+
[`get_registry`][pythonnative.native_views.get_registry] calls
|
|
366
|
+
return `registry`. Pass a mock to drive the reconciler from
|
|
367
|
+
unit tests without touching real native APIs. Pass ``None`` to
|
|
368
|
+
reset the singleton; the next ``get_registry`` call will then
|
|
369
|
+
rebuild it from scratch.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
registry: The replacement backend, or ``None`` to clear.
|
|
373
|
+
"""
|
|
374
|
+
global _registry
|
|
375
|
+
_registry = registry
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""Shared base classes and utilities for native-view handlers.
|
|
2
|
+
|
|
3
|
+
Provides the [`ViewHandler`][pythonnative.native_views.base.ViewHandler]
|
|
4
|
+
protocol implemented by Android and iOS handlers, plus the
|
|
5
|
+
[`parse_color_int`][pythonnative.native_views.base.parse_color_int]
|
|
6
|
+
helper shared across platforms.
|
|
7
|
+
|
|
8
|
+
Layout itself is *not* a handler responsibility. The pure-Python flex
|
|
9
|
+
engine in ``pythonnative.layout`` owns sizing and positioning;
|
|
10
|
+
handlers receive computed frames via
|
|
11
|
+
[`set_frame`][pythonnative.native_views.base.ViewHandler.set_frame] and
|
|
12
|
+
optionally expose an intrinsic-size hook via
|
|
13
|
+
[`measure_intrinsic`][pythonnative.native_views.base.ViewHandler.measure_intrinsic]
|
|
14
|
+
for content-sized leaves (text, buttons, images).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import math
|
|
18
|
+
import re
|
|
19
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ViewHandler:
|
|
23
|
+
"""Protocol implemented by every native-view handler.
|
|
24
|
+
|
|
25
|
+
A `ViewHandler` knows how to create, update, re-parent, and destroy
|
|
26
|
+
native views of one element type. The reconciler never calls a
|
|
27
|
+
handler directly; it emits a batch of mutation ops
|
|
28
|
+
(`pythonnative.mutations`) that the
|
|
29
|
+
[`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
|
|
30
|
+
applies by dispatching to handlers. Handlers never need to know
|
|
31
|
+
about `Element` or `VNode`.
|
|
32
|
+
|
|
33
|
+
Event contract: props delivered to
|
|
34
|
+
[`create`][pythonnative.native_views.base.ViewHandler.create] /
|
|
35
|
+
[`update`][pythonnative.native_views.base.ViewHandler.update]
|
|
36
|
+
contain **no Python callables**. The set of event names wired on
|
|
37
|
+
the element arrives under the ``_pn_events`` key (see
|
|
38
|
+
[`event_names`][pythonnative.events.event_names]); handlers wire
|
|
39
|
+
platform listeners once at create time and forward firings through
|
|
40
|
+
[`dispatch_event`][pythonnative.events.dispatch_event] using the
|
|
41
|
+
tag passed to ``create``.
|
|
42
|
+
|
|
43
|
+
Subclasses must override ``create`` and ``update``. Container
|
|
44
|
+
handlers override the child-management methods; leaf handlers can
|
|
45
|
+
leave them as no-ops. Handlers whose intrinsic size depends on
|
|
46
|
+
content (text, buttons, images) override
|
|
47
|
+
[`measure_intrinsic`][pythonnative.native_views.base.ViewHandler.measure_intrinsic].
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def create(self, tag: int, props: Dict[str, Any]) -> Any:
|
|
51
|
+
"""Create a fresh native view and apply initial *visual* props.
|
|
52
|
+
|
|
53
|
+
Layout-related props (``width``, ``height``, ``flex``, ``padding``,
|
|
54
|
+
etc.) are consumed by the layout engine and applied via
|
|
55
|
+
[`set_frame`][pythonnative.native_views.base.ViewHandler.set_frame],
|
|
56
|
+
so handlers should ignore them here.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
tag: The reconciler-assigned identity for this view. Used
|
|
60
|
+
when dispatching events back into Python.
|
|
61
|
+
props: Initial props dict (callable-free; event names under
|
|
62
|
+
``_pn_events``).
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The platform-native view object.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
NotImplementedError: Subclasses must override.
|
|
69
|
+
"""
|
|
70
|
+
raise NotImplementedError
|
|
71
|
+
|
|
72
|
+
def update(self, native_view: Any, changed_props: Dict[str, Any]) -> None:
|
|
73
|
+
"""Apply only the *visual* props that changed since the last render.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
native_view: The platform-native view to mutate.
|
|
77
|
+
changed_props: Props whose values changed (a value of
|
|
78
|
+
`None` indicates the prop was removed).
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
NotImplementedError: Subclasses must override.
|
|
82
|
+
"""
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
def insert_child(self, parent: Any, child: Any, index: int) -> None:
|
|
86
|
+
"""Ensure `child` sits at `index` among `parent`'s children.
|
|
87
|
+
|
|
88
|
+
Must be **move-aware**: when `child` is already attached to
|
|
89
|
+
`parent`, reposition it instead of attaching twice. Handlers
|
|
90
|
+
should clamp `index` to the current child count. No-op for
|
|
91
|
+
leaf handlers.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def remove_child(self, parent: Any, child: Any) -> None:
|
|
95
|
+
"""Remove `child` from `parent` without destroying it. No-op for leaf handlers."""
|
|
96
|
+
|
|
97
|
+
def destroy(self, native_view: Any) -> None:
|
|
98
|
+
"""Release platform resources owned by ``native_view``.
|
|
99
|
+
|
|
100
|
+
Called exactly once when the reconciler unmounts the view.
|
|
101
|
+
The default is a no-op; override to detach listeners, cancel
|
|
102
|
+
in-flight work, or destroy widgets that the platform doesn't
|
|
103
|
+
garbage-collect.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def set_frame(self, native_view: Any, x: float, y: float, width: float, height: float) -> None:
|
|
107
|
+
"""Position and size ``native_view`` relative to its parent.
|
|
108
|
+
|
|
109
|
+
Coordinates are in points and relative to the parent's content
|
|
110
|
+
origin. Default no-op so handlers that don't need explicit
|
|
111
|
+
positioning (e.g., `Modal`) can opt out.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
native_view: The platform-native view.
|
|
115
|
+
x: X-coordinate (points) of the view's top-left corner
|
|
116
|
+
relative to its parent's content origin.
|
|
117
|
+
y: Y-coordinate (points) of the view's top-left corner.
|
|
118
|
+
width: View width in points.
|
|
119
|
+
height: View height in points.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
def measure_intrinsic(
|
|
123
|
+
self,
|
|
124
|
+
native_view: Any,
|
|
125
|
+
max_width: float,
|
|
126
|
+
max_height: float,
|
|
127
|
+
) -> Tuple[float, float]:
|
|
128
|
+
"""Return the natural ``(width, height)`` of a content-sized view.
|
|
129
|
+
|
|
130
|
+
Used by the layout engine for leaves whose size depends on
|
|
131
|
+
their content (text, buttons, images). Either ``max_width`` or
|
|
132
|
+
``max_height`` may be `math.inf` to indicate no constraint.
|
|
133
|
+
|
|
134
|
+
The default implementation returns ``(0, 0)``; override for
|
|
135
|
+
leaves whose size depends on their content. Container handlers
|
|
136
|
+
leave this alone; the engine sizes containers by laying out
|
|
137
|
+
their children.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
native_view: The platform-native view to measure.
|
|
141
|
+
max_width: Maximum width in points (or `math.inf`).
|
|
142
|
+
max_height: Maximum height in points (or `math.inf`).
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
``(width, height)`` in points.
|
|
146
|
+
"""
|
|
147
|
+
return (0.0, 0.0)
|
|
148
|
+
|
|
149
|
+
def command(self, native_view: Any, name: str, args: Dict[str, Any]) -> Any:
|
|
150
|
+
"""Execute an imperative command (e.g. ``"scroll_to_offset"``).
|
|
151
|
+
|
|
152
|
+
Commands are the escape hatch for one-shot imperative actions
|
|
153
|
+
that don't fit declarative props: scrolling, focusing,
|
|
154
|
+
flashing indicators. Unknown commands should be ignored.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
native_view: The platform-native view.
|
|
158
|
+
name: Command name.
|
|
159
|
+
args: Command arguments.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
An optional command-specific result.
|
|
163
|
+
"""
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
def set_animated_property(self, native_view: Any, prop_name: str, value: Any) -> None:
|
|
167
|
+
"""Apply one frame of a Python-driven animation immediately.
|
|
168
|
+
|
|
169
|
+
This is the fallback path used by
|
|
170
|
+
animations the platform cannot drive natively. ``prop_name``
|
|
171
|
+
is one of ``opacity``, ``background_color``, ``translate_x``,
|
|
172
|
+
``translate_y``, ``scale``, ``scale_x``, ``scale_y``,
|
|
173
|
+
``rotate``.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def start_animation(
|
|
177
|
+
self,
|
|
178
|
+
native_view: Any,
|
|
179
|
+
anim_id: int,
|
|
180
|
+
prop_name: str,
|
|
181
|
+
spec: Dict[str, Any],
|
|
182
|
+
) -> bool:
|
|
183
|
+
"""Start a natively-driven animation, if the platform supports it.
|
|
184
|
+
|
|
185
|
+
``spec`` describes the animation::
|
|
186
|
+
|
|
187
|
+
{"kind": "timing", "from": 0.0, "to": 1.0,
|
|
188
|
+
"duration_ms": 300.0, "easing": "ease_in_out"}
|
|
189
|
+
{"kind": "spring", "from": ..., "to": ...,
|
|
190
|
+
"stiffness": 100.0, "damping": 10.0, "mass": 1.0,
|
|
191
|
+
"initial_velocity": 0.0}
|
|
192
|
+
|
|
193
|
+
Implementations must invoke
|
|
194
|
+
``pythonnative.animated.native_animation_completed(anim_id, finished)``
|
|
195
|
+
when the animation completes or is cancelled.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
``True`` when the animation was started natively. ``False``
|
|
199
|
+
tells the caller to fall back to the Python ticker (the
|
|
200
|
+
default).
|
|
201
|
+
"""
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
def cancel_animation(self, native_view: Any, anim_id: int) -> Any:
|
|
205
|
+
"""Cancel a natively-driven animation.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
The property's current (presentation) value when the
|
|
209
|
+
platform can read it, else ``None``.
|
|
210
|
+
"""
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# ======================================================================
|
|
215
|
+
# Color parsing
|
|
216
|
+
# ======================================================================
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def parse_color_int(color: Union[str, int]) -> int:
|
|
220
|
+
"""Parse a color value into a signed 32-bit ARGB int.
|
|
221
|
+
|
|
222
|
+
Accepts `"#RRGGBB"`, `"#AARRGGBB"`, or a raw integer. Java APIs
|
|
223
|
+
such as `setBackgroundColor` expect a signed 32-bit int, so values
|
|
224
|
+
with a high alpha byte (e.g., `0xFF......`) must be converted to
|
|
225
|
+
their negative two's-complement equivalent.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
color: Hex string (with or without leading `#`) or an int.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Signed 32-bit ARGB int suitable for Android's color APIs.
|
|
232
|
+
"""
|
|
233
|
+
if isinstance(color, int):
|
|
234
|
+
val = color
|
|
235
|
+
else:
|
|
236
|
+
c = color.strip().lstrip("#")
|
|
237
|
+
if len(c) == 6:
|
|
238
|
+
c = "FF" + c
|
|
239
|
+
val = int(c, 16)
|
|
240
|
+
if val > 0x7FFFFFFF:
|
|
241
|
+
val -= 0x100000000
|
|
242
|
+
return val
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# ======================================================================
|
|
246
|
+
# Text helpers shared by every backend's Text handler
|
|
247
|
+
# ======================================================================
|
|
248
|
+
|
|
249
|
+
_WORD_START = re.compile(r"(?<!\S)(\S)")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def transform_text(text: Any, mode: Optional[str]) -> str:
|
|
253
|
+
"""Apply a ``text_transform`` style value to ``text``.
|
|
254
|
+
|
|
255
|
+
Runs in Python before the string is handed to the native label, so
|
|
256
|
+
the displayed text and the intrinsic measurement agree on every
|
|
257
|
+
backend. ``"capitalize"`` upper-cases the first character of each
|
|
258
|
+
whitespace-separated word and leaves the rest untouched (matching
|
|
259
|
+
CSS / React Native rather than ``str.title()``, which would
|
|
260
|
+
lower-case the remainder). Unknown modes and ``"none"`` return the
|
|
261
|
+
text unchanged.
|
|
262
|
+
"""
|
|
263
|
+
s = "" if text is None else str(text)
|
|
264
|
+
if mode == "uppercase":
|
|
265
|
+
return s.upper()
|
|
266
|
+
if mode == "lowercase":
|
|
267
|
+
return s.lower()
|
|
268
|
+
if mode == "capitalize":
|
|
269
|
+
return _WORD_START.sub(lambda m: m.group(1).upper(), s)
|
|
270
|
+
return s
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def transform_spans(spans: Any, mode: Optional[str]) -> Any:
|
|
274
|
+
"""Return ``spans`` with `transform_text` applied to each span's ``text``.
|
|
275
|
+
|
|
276
|
+
Rich text is flattened into one native string, so a run-by-run
|
|
277
|
+
transform matches what a single-string transform would produce for
|
|
278
|
+
``uppercase`` / ``lowercase``. ``capitalize`` is evaluated per span,
|
|
279
|
+
which is also how CSS treats inline boxes.
|
|
280
|
+
"""
|
|
281
|
+
if not mode or mode == "none" or not isinstance(spans, list):
|
|
282
|
+
return spans
|
|
283
|
+
out = []
|
|
284
|
+
for span in spans:
|
|
285
|
+
if isinstance(span, dict) and "text" in span:
|
|
286
|
+
copy = dict(span)
|
|
287
|
+
copy["text"] = transform_text(span.get("text"), mode)
|
|
288
|
+
out.append(copy)
|
|
289
|
+
else:
|
|
290
|
+
out.append(span)
|
|
291
|
+
return out
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def shadow_offset_xy(value: Any) -> Tuple[float, float]:
|
|
295
|
+
"""Coerce a ``shadow_offset`` / ``text_shadow_offset`` value to ``(dx, dy)``."""
|
|
296
|
+
if isinstance(value, dict):
|
|
297
|
+
return (float(value.get("width", 0) or 0), float(value.get("height", 0) or 0))
|
|
298
|
+
if isinstance(value, (tuple, list)) and len(value) >= 2:
|
|
299
|
+
return (float(value[0]), float(value[1]))
|
|
300
|
+
return (0.0, 0.0)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
TEXT_SHADOW_STYLE_KEYS = ("text_shadow_color", "text_shadow_offset", "text_shadow_radius")
|
|
304
|
+
"""Style keys that together describe a text shadow."""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ======================================================================
|
|
308
|
+
# Helpers shared by Android and iOS measure callbacks
|
|
309
|
+
# ======================================================================
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _safe_max(value: float, fallback: float = 1e6) -> float:
|
|
313
|
+
"""Clamp ``math.inf`` to a large finite value for native measure calls."""
|
|
314
|
+
if not math.isfinite(value):
|
|
315
|
+
return fallback
|
|
316
|
+
return max(0.0, value)
|