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,237 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Hosts one PythonNative screen.
|
|
4
|
+
///
|
|
5
|
+
/// Lifecycle is forwarded to Python as `callback("host", screenId,
|
|
6
|
+
/// event, payload)`: `create` (viewDidLoad), `start`, `layout`,
|
|
7
|
+
/// `resume`, `pause`, `stop`, `destroy`, `save_state`, and
|
|
8
|
+
/// `restore_state`. Python attaches its root view with
|
|
9
|
+
/// `Host.attach_root`; the controller keeps that view below the top
|
|
10
|
+
/// safe-area inset and full-bleed at the bottom. Fast Refresh needs no
|
|
11
|
+
/// help from here: the Python dev client reloads modules and refreshes
|
|
12
|
+
/// the mounted screens itself.
|
|
13
|
+
open class PNViewController: UIViewController {
|
|
14
|
+
/// Dotted path of the screen component (`nil` = the app entry module).
|
|
15
|
+
public var requestedScreenPath: String?
|
|
16
|
+
/// JSON-encoded screen args, or `nil`.
|
|
17
|
+
public var requestedScreenArgsJSON: String?
|
|
18
|
+
/// State JSON to hand to Python right after `create` (from a prior `saveState`).
|
|
19
|
+
public var restoredStateJSON: String?
|
|
20
|
+
public var cachedStateJSON: String?
|
|
21
|
+
|
|
22
|
+
/// The id Python uses to address this screen.
|
|
23
|
+
public private(set) var screenId: Int64 = 0
|
|
24
|
+
/// Whether `create` has been sent (and not refused by a bootstrap error).
|
|
25
|
+
public private(set) var isScreenCreated = false
|
|
26
|
+
|
|
27
|
+
private(set) var rootView: UIView?
|
|
28
|
+
private var lastLayoutPayload: String?
|
|
29
|
+
|
|
30
|
+
/// The application host can instantiate its controller subclass dynamically.
|
|
31
|
+
public required override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
|
32
|
+
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
|
33
|
+
screenId = PNScreenRegistry.shared.register(self)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public required init?(coder: NSCoder) {
|
|
37
|
+
super.init(coder: coder)
|
|
38
|
+
screenId = PNScreenRegistry.shared.register(self)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
deinit {
|
|
42
|
+
if isScreenCreated {
|
|
43
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: "destroy", payload: "{}")
|
|
44
|
+
}
|
|
45
|
+
PNScreenRegistry.shared.unregister(screenId)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// MARK: - Subclass hooks
|
|
49
|
+
|
|
50
|
+
/// Called from `viewDidLoad` before the screen is created. Return
|
|
51
|
+
/// `false` (after showing an error) to skip creating the screen.
|
|
52
|
+
open func prepareRuntime() -> Bool {
|
|
53
|
+
true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// The screen path used when `requestedScreenPath` is `nil`.
|
|
57
|
+
open var defaultScreenPath: String {
|
|
58
|
+
(Bundle.main.object(forInfoDictionaryKey: "PNEntryModule") as? String) ?? "app.main"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// MARK: - Lifecycle
|
|
62
|
+
|
|
63
|
+
open override func viewDidLoad() {
|
|
64
|
+
super.viewDidLoad()
|
|
65
|
+
view.backgroundColor = .systemBackground
|
|
66
|
+
guard prepareRuntime() else { return }
|
|
67
|
+
guard PNBridge.shared.hasCallback else {
|
|
68
|
+
showBootstrapError("The Python bridge callback is not registered.\n\nImport pythonnative.bridge after starting the interpreter.")
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
isScreenCreated = true
|
|
72
|
+
var payload: [String: Any] = [
|
|
73
|
+
"path": requestedScreenPath ?? defaultScreenPath,
|
|
74
|
+
"args": requestedScreenArgsJSON ?? NSNull(),
|
|
75
|
+
]
|
|
76
|
+
if let restored = restoredStateJSON {
|
|
77
|
+
payload["restored_state"] = restored
|
|
78
|
+
}
|
|
79
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: "create", payload: PNJSON.encode(payload))
|
|
80
|
+
if let restored = restoredStateJSON {
|
|
81
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: "restore_state", payload: restored)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
open override func viewWillAppear(_ animated: Bool) {
|
|
86
|
+
super.viewWillAppear(animated)
|
|
87
|
+
forward("start")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
open override func viewDidLayoutSubviews() {
|
|
91
|
+
super.viewDidLayoutSubviews()
|
|
92
|
+
syncRootFrame()
|
|
93
|
+
let payload = viewportJSON()
|
|
94
|
+
// Every pass forwards (rotation, multitasking, keyboard), but a
|
|
95
|
+
// pass that changed nothing about the viewport is skipped.
|
|
96
|
+
if payload != lastLayoutPayload {
|
|
97
|
+
lastLayoutPayload = payload
|
|
98
|
+
forward("layout", payload)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
open override func viewDidAppear(_ animated: Bool) {
|
|
103
|
+
super.viewDidAppear(animated)
|
|
104
|
+
syncRootFrame()
|
|
105
|
+
forward("resume", viewportJSON())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
open override func viewWillDisappear(_ animated: Bool) {
|
|
109
|
+
super.viewWillDisappear(animated)
|
|
110
|
+
forward("pause")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
open override func viewDidDisappear(_ animated: Bool) {
|
|
114
|
+
super.viewDidDisappear(animated)
|
|
115
|
+
forward("stop")
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
119
|
+
super.traitCollectionDidChange(previousTraitCollection)
|
|
120
|
+
if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
|
|
121
|
+
lastLayoutPayload = nil
|
|
122
|
+
view.setNeedsLayout()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Ask Python for the screen's serialized state (`save_state`).
|
|
127
|
+
@discardableResult
|
|
128
|
+
public func saveState() -> String? {
|
|
129
|
+
guard isScreenCreated else { return nil }
|
|
130
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: "save_state", payload: "{}")
|
|
131
|
+
return cachedStateJSON
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Hand previously saved state back to Python (`restore_state`).
|
|
135
|
+
public func restoreState(_ json: String) {
|
|
136
|
+
guard isScreenCreated else {
|
|
137
|
+
restoredStateJSON = json
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: "restore_state", payload: json)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
open override var prefersStatusBarHidden: Bool { PNStatusBarState.hidden }
|
|
144
|
+
open override var preferredStatusBarStyle: UIStatusBarStyle { PNStatusBarState.style }
|
|
145
|
+
open override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { PNStatusBarState.animation }
|
|
146
|
+
|
|
147
|
+
private func forward(_ event: String, _ payload: String = "{}") {
|
|
148
|
+
guard isScreenCreated else { return }
|
|
149
|
+
PNBridge.shared.callPython(kind: "host", tag: screenId, name: event, payload: payload)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// MARK: - Root view
|
|
153
|
+
|
|
154
|
+
/// Attach Python's root view (`Host.attach_root`).
|
|
155
|
+
func attachRoot(_ root: UIView) {
|
|
156
|
+
if rootView !== root {
|
|
157
|
+
rootView?.removeFromSuperview()
|
|
158
|
+
}
|
|
159
|
+
rootView = root
|
|
160
|
+
root.translatesAutoresizingMaskIntoConstraints = true
|
|
161
|
+
root.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
162
|
+
view.addSubview(root)
|
|
163
|
+
syncRootFrame()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Detach the root view (`Host.detach_root`).
|
|
167
|
+
func detachRoot(_ root: UIView) {
|
|
168
|
+
if root.superview === view {
|
|
169
|
+
root.removeFromSuperview()
|
|
170
|
+
}
|
|
171
|
+
if rootView === root {
|
|
172
|
+
rootView = nil
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// The frame the root occupies: below the top inset, full-bleed at
|
|
177
|
+
/// the bottom so a tab bar can reach the home indicator.
|
|
178
|
+
public var rootFrame: CGRect {
|
|
179
|
+
let bounds = view.bounds
|
|
180
|
+
let insets = view.safeAreaInsets
|
|
181
|
+
let w = max(0, bounds.width - insets.left - insets.right)
|
|
182
|
+
let h = max(0, bounds.height - insets.top)
|
|
183
|
+
if w > 0, h > 0 {
|
|
184
|
+
return CGRect(x: insets.left, y: insets.top, width: w, height: h)
|
|
185
|
+
}
|
|
186
|
+
return bounds
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private func syncRootFrame() {
|
|
190
|
+
guard let root = rootView else { return }
|
|
191
|
+
let frame = rootFrame
|
|
192
|
+
if root.frame != frame {
|
|
193
|
+
root.frame = frame
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// The `{"width","height","insets","color_scheme"}` payload shared by
|
|
198
|
+
/// `layout`, `resume`, and `Host.viewport`.
|
|
199
|
+
public func viewport() -> [String: Any] {
|
|
200
|
+
var frame = rootFrame
|
|
201
|
+
if frame.width <= 0 || frame.height <= 0 {
|
|
202
|
+
frame = PNWindow.screenBounds()
|
|
203
|
+
}
|
|
204
|
+
let insets = view.safeAreaInsets
|
|
205
|
+
return [
|
|
206
|
+
"width": Double(frame.width),
|
|
207
|
+
"height": Double(frame.height),
|
|
208
|
+
"insets": [
|
|
209
|
+
"top": 0.0,
|
|
210
|
+
"left": Double(insets.left),
|
|
211
|
+
"bottom": Double(insets.bottom),
|
|
212
|
+
"right": Double(insets.right),
|
|
213
|
+
],
|
|
214
|
+
"color_scheme": PNWindow.colorScheme(for: view),
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func viewportJSON() -> String {
|
|
219
|
+
PNJSON.encode(viewport())
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// MARK: - Bootstrap error UI
|
|
223
|
+
|
|
224
|
+
/// Replace the screen with a full-bleed error report.
|
|
225
|
+
public func showBootstrapError(_ message: String) {
|
|
226
|
+
PNLog.screens.error("\(message)")
|
|
227
|
+
let text = UITextView(frame: view.bounds)
|
|
228
|
+
text.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
229
|
+
text.isEditable = false
|
|
230
|
+
text.backgroundColor = UIColor(red: 0.75, green: 0.10, blue: 0.10, alpha: 1.0)
|
|
231
|
+
text.textColor = .white
|
|
232
|
+
text.font = UIFont.monospacedSystemFont(ofSize: 13, weight: .regular)
|
|
233
|
+
text.textContainerInset = UIEdgeInsets(top: 60, left: 16, bottom: 40, right: 16)
|
|
234
|
+
text.text = "PythonNative could not start\n\n\(message)"
|
|
235
|
+
view.addSubview(text)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import CoreLocation
|
|
2
|
+
import Photos
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
/// Back-deployment shims for APIs that moved or appeared after iOS 13, so
|
|
6
|
+
/// the kit compiles at the lowest deployment target `pn` accepts.
|
|
7
|
+
extension CLLocationManager {
|
|
8
|
+
/// `authorizationStatus` (instance, iOS 14+) with the class-method
|
|
9
|
+
/// fallback used on iOS 13.
|
|
10
|
+
var pnAuthorizationStatus: CLAuthorizationStatus {
|
|
11
|
+
if #available(iOS 14.0, *) {
|
|
12
|
+
return authorizationStatus
|
|
13
|
+
}
|
|
14
|
+
return CLLocationManager.authorizationStatus()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
extension PHPhotoLibrary {
|
|
19
|
+
/// Read/write library access status on iOS 14+, the legacy status before.
|
|
20
|
+
static func pnReadWriteAuthorizationStatus() -> PHAuthorizationStatus {
|
|
21
|
+
if #available(iOS 14.0, *) {
|
|
22
|
+
return authorizationStatus(for: .readWrite)
|
|
23
|
+
}
|
|
24
|
+
return authorizationStatus()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Request read/write library access with the API available on this OS.
|
|
28
|
+
static func pnRequestReadWriteAuthorization(_ handler: @escaping (PHAuthorizationStatus) -> Void) {
|
|
29
|
+
if #available(iOS 14.0, *) {
|
|
30
|
+
requestAuthorization(for: .readWrite, handler: handler)
|
|
31
|
+
} else {
|
|
32
|
+
requestAuthorization(handler)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
extension UIDatePicker {
|
|
38
|
+
/// Prefer the compact inline style where the OS offers it.
|
|
39
|
+
func pnApplyCompactStyle() {
|
|
40
|
+
if #available(iOS 14.0, *) {
|
|
41
|
+
preferredDatePickerStyle = .compact
|
|
42
|
+
} else if #available(iOS 13.4, *) {
|
|
43
|
+
preferredDatePickerStyle = .wheels
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// JSON helpers shared by the bridge, event emitters, and modules.
|
|
4
|
+
///
|
|
5
|
+
/// Encoding tolerates values `JSONSerialization` rejects (non-finite
|
|
6
|
+
/// doubles, `Optional.none`, `CGFloat`, `URL`) by normalizing them
|
|
7
|
+
/// first; decoding turns the Python-side `"inf"` marker back into
|
|
8
|
+
/// `Double.infinity`.
|
|
9
|
+
public enum PNJSON {
|
|
10
|
+
/// Decode a JSON document into Foundation containers.
|
|
11
|
+
public static func decode(_ text: String) -> Any? {
|
|
12
|
+
guard let data = text.data(using: .utf8) else { return nil }
|
|
13
|
+
return try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Decode a JSON object, returning an empty dictionary for anything else.
|
|
17
|
+
public static func decodeObject(_ text: String?) -> [String: Any] {
|
|
18
|
+
guard let text = text, let value = decode(text) as? [String: Any] else { return [:] }
|
|
19
|
+
return value
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Encode any JSON-compatible value (a fragment is allowed).
|
|
23
|
+
public static func encode(_ value: Any?) -> String {
|
|
24
|
+
let normalized = normalize(value)
|
|
25
|
+
guard JSONSerialization.isValidJSONObject([normalized]) else { return "null" }
|
|
26
|
+
guard let data = try? JSONSerialization.data(withJSONObject: normalized, options: [.fragmentsAllowed]),
|
|
27
|
+
let text = String(data: data, encoding: .utf8)
|
|
28
|
+
else {
|
|
29
|
+
return "null"
|
|
30
|
+
}
|
|
31
|
+
return text
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Replace every `"inf"` string in a decoded prop tree with `Double.infinity`.
|
|
35
|
+
public static func resolveInfinity(_ value: Any) -> Any {
|
|
36
|
+
if let string = value as? String {
|
|
37
|
+
return string == "inf" ? Double.infinity : string
|
|
38
|
+
}
|
|
39
|
+
if let dict = value as? [String: Any] {
|
|
40
|
+
var out: [String: Any] = [:]
|
|
41
|
+
out.reserveCapacity(dict.count)
|
|
42
|
+
for (key, item) in dict {
|
|
43
|
+
out[key] = resolveInfinity(item)
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
47
|
+
if let array = value as? [Any] {
|
|
48
|
+
return array.map { resolveInfinity($0) }
|
|
49
|
+
}
|
|
50
|
+
return value
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Convert arbitrary values to something `JSONSerialization` accepts.
|
|
54
|
+
static func normalize(_ value: Any?) -> Any {
|
|
55
|
+
guard let value = value else { return NSNull() }
|
|
56
|
+
switch value {
|
|
57
|
+
case let optional as Optional<Any>:
|
|
58
|
+
guard let unwrapped = optional else { return NSNull() }
|
|
59
|
+
return normalizeConcrete(unwrapped)
|
|
60
|
+
default:
|
|
61
|
+
return normalizeConcrete(value)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private static func normalizeConcrete(_ value: Any) -> Any {
|
|
66
|
+
switch value {
|
|
67
|
+
case is NSNull:
|
|
68
|
+
return value
|
|
69
|
+
case let bool as Bool:
|
|
70
|
+
return bool
|
|
71
|
+
case let double as Double:
|
|
72
|
+
return double.isFinite ? double : NSNull()
|
|
73
|
+
case let float as Float:
|
|
74
|
+
return float.isFinite ? Double(float) : NSNull()
|
|
75
|
+
case let cg as CGFloat:
|
|
76
|
+
return cg.isFinite ? Double(cg) : NSNull()
|
|
77
|
+
case let int as Int:
|
|
78
|
+
return int
|
|
79
|
+
case let int64 as Int64:
|
|
80
|
+
return int64
|
|
81
|
+
case let number as NSNumber:
|
|
82
|
+
return number
|
|
83
|
+
case let string as String:
|
|
84
|
+
return string
|
|
85
|
+
case let url as URL:
|
|
86
|
+
return url.absoluteString
|
|
87
|
+
case let dict as [String: Any]:
|
|
88
|
+
var out: [String: Any] = [:]
|
|
89
|
+
out.reserveCapacity(dict.count)
|
|
90
|
+
for (key, item) in dict {
|
|
91
|
+
out[key] = normalize(item)
|
|
92
|
+
}
|
|
93
|
+
return out
|
|
94
|
+
case let array as [Any]:
|
|
95
|
+
return array.map { normalize($0) }
|
|
96
|
+
default:
|
|
97
|
+
return String(describing: value)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Typed accessors for loosely typed prop dictionaries.
|
|
103
|
+
public enum PNProps {
|
|
104
|
+
/// Coerce numbers, numeric strings, and the `"inf"` marker to `Double`.
|
|
105
|
+
public static func double(_ value: Any?) -> Double? {
|
|
106
|
+
switch value {
|
|
107
|
+
case let d as Double: return d
|
|
108
|
+
case let f as Float: return Double(f)
|
|
109
|
+
case let cg as CGFloat: return Double(cg)
|
|
110
|
+
case let i as Int: return Double(i)
|
|
111
|
+
case let n as NSNumber: return n.doubleValue
|
|
112
|
+
case let s as String:
|
|
113
|
+
if s == "inf" { return Double.infinity }
|
|
114
|
+
if s == "-inf" { return -Double.infinity }
|
|
115
|
+
return Double(s)
|
|
116
|
+
default: return nil
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Like `double(_:)` but non-finite values fall back to `fallback`.
|
|
121
|
+
public static func finite(_ value: Any?, _ fallback: Double = 0) -> Double {
|
|
122
|
+
guard let d = double(value), d.isFinite else { return fallback }
|
|
123
|
+
return d
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public static func int(_ value: Any?) -> Int? {
|
|
127
|
+
switch value {
|
|
128
|
+
case let i as Int: return i
|
|
129
|
+
case let n as NSNumber: return n.intValue
|
|
130
|
+
case let d as Double: return d.isFinite ? Int(d) : nil
|
|
131
|
+
case let s as String: return Int(s) ?? Double(s).map { Int($0) }
|
|
132
|
+
default: return nil
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public static func bool(_ value: Any?) -> Bool? {
|
|
137
|
+
switch value {
|
|
138
|
+
case let b as Bool: return b
|
|
139
|
+
case let n as NSNumber: return n.boolValue
|
|
140
|
+
case let s as String: return s.lowercased() == "true"
|
|
141
|
+
default: return nil
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public static func string(_ value: Any?) -> String? {
|
|
146
|
+
switch value {
|
|
147
|
+
case nil: return nil
|
|
148
|
+
case is NSNull: return nil
|
|
149
|
+
case let s as String: return s
|
|
150
|
+
case let n as NSNumber: return n.stringValue
|
|
151
|
+
default: return String(describing: value as Any)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public static func dict(_ value: Any?) -> [String: Any]? {
|
|
156
|
+
value as? [String: Any]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public static func array(_ value: Any?) -> [Any]? {
|
|
160
|
+
value as? [Any]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/// True when the key is present, even if its value is `null`.
|
|
164
|
+
public static func has(_ props: [String: Any], _ key: String) -> Bool {
|
|
165
|
+
props.index(forKey: key) != nil
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// The value for `key`, treating JSON `null` as absent.
|
|
169
|
+
public static func value(_ props: [String: Any], _ key: String) -> Any? {
|
|
170
|
+
guard let v = props[key], !(v is NSNull) else { return nil }
|
|
171
|
+
return v
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import os.log
|
|
3
|
+
|
|
4
|
+
/// A thin `os_log` wrapper with the subset of the `Logger` API the kit
|
|
5
|
+
/// uses. `Logger` itself is iOS 14+, and apps may set a deployment target
|
|
6
|
+
/// as low as iOS 13, so this keeps the kit buildable at the app's minimum.
|
|
7
|
+
struct PNLogger {
|
|
8
|
+
let log: OSLog
|
|
9
|
+
|
|
10
|
+
init(category: String) {
|
|
11
|
+
log = OSLog(subsystem: "com.pythonnative", category: category)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
func error(_ message: String) { os_log("%{public}@", log: log, type: .error, message) }
|
|
15
|
+
func warning(_ message: String) { os_log("%{public}@", log: log, type: .default, message) }
|
|
16
|
+
func info(_ message: String) { os_log("%{public}@", log: log, type: .info, message) }
|
|
17
|
+
func debug(_ message: String) { os_log("%{public}@", log: log, type: .debug, message) }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Loggers for every PythonNativeKit subsystem, plus a rate limiter for
|
|
21
|
+
/// messages that could otherwise fire at frame rate.
|
|
22
|
+
enum PNLog {
|
|
23
|
+
static let bridge = PNLogger(category: "bridge")
|
|
24
|
+
static let components = PNLogger(category: "components")
|
|
25
|
+
static let gestures = PNLogger(category: "gestures")
|
|
26
|
+
static let animation = PNLogger(category: "animation")
|
|
27
|
+
static let modules = PNLogger(category: "modules")
|
|
28
|
+
static let screens = PNLogger(category: "screens")
|
|
29
|
+
|
|
30
|
+
private static var lastEmitted: [String: TimeInterval] = [:]
|
|
31
|
+
private static var onceKeys: Set<String> = []
|
|
32
|
+
|
|
33
|
+
/// Log `message` at most once per `interval` seconds for a given `key`.
|
|
34
|
+
static func rateLimited(_ logger: PNLogger, key: String, interval: TimeInterval = 1.0, _ message: @autoclosure () -> String) {
|
|
35
|
+
let now = Date().timeIntervalSinceReferenceDate
|
|
36
|
+
if let last = lastEmitted[key], now - last < interval { return }
|
|
37
|
+
lastEmitted[key] = now
|
|
38
|
+
logger.error(message())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Log `message` exactly once per process for a given `key`.
|
|
42
|
+
static func once(_ logger: PNLogger, key: String, _ message: @autoclosure () -> String) {
|
|
43
|
+
if onceKeys.contains(key) { return }
|
|
44
|
+
onceKeys.insert(key)
|
|
45
|
+
logger.warning(message())
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Window and presenter lookups shared by modals, alerts, pickers, and portals.
|
|
4
|
+
public enum PNWindow {
|
|
5
|
+
/// The app's key window (scene-based lookup, no deprecated APIs).
|
|
6
|
+
public static func keyWindow() -> UIWindow? {
|
|
7
|
+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
|
|
8
|
+
for scene in scenes where scene.activationState == .foregroundActive {
|
|
9
|
+
if let window = scene.windows.first(where: { $0.isKeyWindow }) { return window }
|
|
10
|
+
}
|
|
11
|
+
for scene in scenes {
|
|
12
|
+
if let window = scene.windows.first(where: { $0.isKeyWindow }) { return window }
|
|
13
|
+
}
|
|
14
|
+
return scenes.first?.windows.first
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Bounds of the main screen (or `.zero` when no scene is connected).
|
|
18
|
+
public static func screenBounds() -> CGRect {
|
|
19
|
+
if let window = keyWindow() {
|
|
20
|
+
return window.windowScene?.screen.bounds ?? window.bounds
|
|
21
|
+
}
|
|
22
|
+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
|
|
23
|
+
return scenes.first?.screen.bounds ?? .zero
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// The display scale of the main screen.
|
|
27
|
+
public static func screenScale() -> CGFloat {
|
|
28
|
+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
|
|
29
|
+
return scenes.first?.screen.scale ?? 2.0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// The topmost view controller suitable for presenting alerts and sheets.
|
|
33
|
+
public static func topViewController() -> UIViewController? {
|
|
34
|
+
guard var top = keyWindow()?.rootViewController else { return nil }
|
|
35
|
+
if let nav = top as? UINavigationController, let visible = nav.visibleViewController {
|
|
36
|
+
top = visible
|
|
37
|
+
} else if let tabs = top as? UITabBarController, let selected = tabs.selectedViewController {
|
|
38
|
+
top = selected
|
|
39
|
+
}
|
|
40
|
+
while let presented = top.presentedViewController {
|
|
41
|
+
top = presented
|
|
42
|
+
}
|
|
43
|
+
return top
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Current system appearance as `"light"` or `"dark"`.
|
|
47
|
+
public static func colorScheme(for view: UIView? = nil) -> String {
|
|
48
|
+
let traits = view?.traitCollection ?? keyWindow()?.traitCollection ?? UITraitCollection.current
|
|
49
|
+
return traits.userInterfaceStyle == .dark ? "dark" : "light"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import PythonNativeKit
|
|
3
|
+
|
|
4
|
+
final class PNAnimationGraphTests: XCTestCase {
|
|
5
|
+
override func tearDown() {
|
|
6
|
+
PNAnimationGraph.forget(9401)
|
|
7
|
+
super.tearDown()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
func testDerivedGraphReplacementKeepsDriverAndReleasesItOnUnmount() {
|
|
11
|
+
let nodes: [[String: Any]] = [
|
|
12
|
+
["id": 101, "kind": "value", "value": 0.0],
|
|
13
|
+
["id": 102, "kind": "multiply", "inputs": [["node": 101], ["constant": 2.0]]],
|
|
14
|
+
]
|
|
15
|
+
PNAnimationGraph.install(["id": 101, "nodes": nodes, "bindings": [[9401, "opacity", 102]]])
|
|
16
|
+
PNAnimationGraph.set(101, 0.25)
|
|
17
|
+
XCTAssertEqual(PNAnimationGraph.values[102], 0.5)
|
|
18
|
+
XCTAssertTrue(PNAnimationGraph.start(9501, node: 101, spec: ["kind": "timing", "to": 1.0]))
|
|
19
|
+
PNAnimationGraph.install(["id": 100, "nodes": nodes, "bindings": [[9401, "opacity", 102]]])
|
|
20
|
+
XCTAssertNotNil(PNAnimationGraph.drivers[9501])
|
|
21
|
+
XCTAssertEqual(PNAnimationGraph.values[101], 0.25)
|
|
22
|
+
PNAnimationGraph.forget(9401)
|
|
23
|
+
XCTAssertNil(PNAnimationGraph.drivers[9501])
|
|
24
|
+
XCTAssertNil(PNAnimationGraph.values[101])
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import PythonNativeKit
|
|
3
|
+
|
|
4
|
+
final class PNColorTests: XCTestCase {
|
|
5
|
+
private func rgba(_ color: UIColor?) -> [Int] {
|
|
6
|
+
guard let color = color else { return [] }
|
|
7
|
+
let c = PNColor.components(color)
|
|
8
|
+
return [c.r, c.g, c.b, c.a].map { Int(round($0 * 255)) }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
func testHexForms() {
|
|
12
|
+
XCTAssertEqual(rgba(PNColor.parse("#f00")), [255, 0, 0, 255])
|
|
13
|
+
XCTAssertEqual(rgba(PNColor.parse("#00ff00")), [0, 255, 0, 255])
|
|
14
|
+
XCTAssertEqual(rgba(PNColor.parse("#800000ff")), [0, 0, 255, 128])
|
|
15
|
+
XCTAssertEqual(rgba(PNColor.parse("#0000FF")), [0, 0, 255, 255])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func testFunctionalForms() {
|
|
19
|
+
XCTAssertEqual(rgba(PNColor.parse("rgb(10, 20, 30)")), [10, 20, 30, 255])
|
|
20
|
+
XCTAssertEqual(rgba(PNColor.parse("rgba(10, 20, 30, 0.5)")), [10, 20, 30, 128])
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func testNamedColorsAndTransparent() {
|
|
24
|
+
XCTAssertEqual(rgba(PNColor.parse("red")), [255, 0, 0, 255])
|
|
25
|
+
XCTAssertEqual(rgba(PNColor.parse("white")), [255, 255, 255, 255])
|
|
26
|
+
XCTAssertEqual(rgba(PNColor.parse("transparent")), [0, 0, 0, 0])
|
|
27
|
+
XCTAssertNil(PNColor.parse(nil))
|
|
28
|
+
XCTAssertNil(PNColor.parse(""))
|
|
29
|
+
XCTAssertNil(PNColor.parse("not-a-color"))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func testIntegerARGB() {
|
|
33
|
+
XCTAssertEqual(rgba(PNColor.parse(0xFF112233 as Int64)), [0x11, 0x22, 0x33, 255])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func testDynamicLightDark() {
|
|
37
|
+
let color = PNColor.parse(["light": "#ffffff", "dark": "#000000"])
|
|
38
|
+
XCTAssertNotNil(color)
|
|
39
|
+
let light = color?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .light))
|
|
40
|
+
let dark = color?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))
|
|
41
|
+
XCTAssertEqual(rgba(light), [255, 255, 255, 255])
|
|
42
|
+
XCTAssertEqual(rgba(dark), [0, 0, 0, 255])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func testHexRoundTrip() {
|
|
46
|
+
let hex = PNColor.hexString(UIColor(red: 1, green: 0, blue: 0, alpha: 1))
|
|
47
|
+
XCTAssertEqual(hex.lowercased(), "#ffff0000", "ARGB, matching the Python animated color format")
|
|
48
|
+
XCTAssertEqual(rgba(PNColor.parse(hex)), [255, 0, 0, 255])
|
|
49
|
+
}
|
|
50
|
+
}
|