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,260 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Installs `UIGestureRecognizer`s from the `gestures` prop and emits
|
|
4
|
+
/// `gesture:<index>` events with the payload dict Python's
|
|
5
|
+
/// `GestureEvent` expects.
|
|
6
|
+
///
|
|
7
|
+
/// Spec fields:
|
|
8
|
+
/// - `kind`: `tap`, `long_press`, `pan`, `swipe`, `fling`, `pinch`, `rotation`
|
|
9
|
+
/// - per-kind config (`n_taps`, `max_distance`, `min_duration_ms`,
|
|
10
|
+
/// `min_distance`, `direction`, `n_pointers`, `min_velocity`)
|
|
11
|
+
/// - `simultaneous`: indices allowed to recognize together
|
|
12
|
+
/// - `wait_for`: indices that must fail before this one may begin
|
|
13
|
+
public final class PNGestureCoordinator: NSObject, UIGestureRecognizerDelegate {
|
|
14
|
+
public static let shared = PNGestureCoordinator()
|
|
15
|
+
|
|
16
|
+
/// Bookkeeping for one installed recognizer.
|
|
17
|
+
final class Entry {
|
|
18
|
+
let index: Int
|
|
19
|
+
let kind: String
|
|
20
|
+
var simultaneous: Set<Int>
|
|
21
|
+
var minVelocity: CGFloat
|
|
22
|
+
var direction: String
|
|
23
|
+
var lastLocation: CGPoint = .zero
|
|
24
|
+
|
|
25
|
+
init(index: Int, kind: String, simultaneous: Set<Int>, minVelocity: CGFloat, direction: String) {
|
|
26
|
+
self.index = index
|
|
27
|
+
self.kind = kind
|
|
28
|
+
self.simultaneous = simultaneous
|
|
29
|
+
self.minVelocity = minVelocity
|
|
30
|
+
self.direction = direction
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private var entries: [ObjectIdentifier: Entry] = [:]
|
|
35
|
+
|
|
36
|
+
private override init() {
|
|
37
|
+
super.init()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// MARK: - Wiring
|
|
41
|
+
|
|
42
|
+
/// Replace the recognizers on `view` with the ones described by `specs`.
|
|
43
|
+
public func wire(view: UIView, specs: Any?) {
|
|
44
|
+
unwire(view: view)
|
|
45
|
+
guard let state = PNViewState.existing(for: view) else { return }
|
|
46
|
+
let list = ((specs as? [Any]) ?? []).map { ($0 as? [String: Any]) ?? [:] }
|
|
47
|
+
guard !list.isEmpty else { return }
|
|
48
|
+
// A spec may own several recognizers (an any-direction swipe is
|
|
49
|
+
// one `UISwipeGestureRecognizer` per direction), so relationships
|
|
50
|
+
// are wired spec-to-spec across every recognizer on each side.
|
|
51
|
+
var built: [Int: [UIGestureRecognizer]] = [:]
|
|
52
|
+
for (index, spec) in list.enumerated() {
|
|
53
|
+
let recognizers = makeRecognizers(spec)
|
|
54
|
+
if recognizers.isEmpty {
|
|
55
|
+
PNLog.once(PNLog.gestures, key: "kind:\(PNProps.string(spec["kind"]) ?? "")", "unsupported gesture kind '\(PNProps.string(spec["kind"]) ?? "")'")
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
for (recognizer, direction) in recognizers {
|
|
59
|
+
let entry = Entry(
|
|
60
|
+
index: index,
|
|
61
|
+
kind: PNProps.string(spec["kind"]) ?? "",
|
|
62
|
+
simultaneous: Set(((spec["simultaneous"] as? [Any]) ?? []).compactMap { PNProps.int($0) }),
|
|
63
|
+
minVelocity: CGFloat(PNProps.double(spec["min_velocity"]) ?? 300),
|
|
64
|
+
direction: direction ?? PNProps.string(spec["direction"]) ?? "any"
|
|
65
|
+
)
|
|
66
|
+
entries[ObjectIdentifier(recognizer)] = entry
|
|
67
|
+
recognizer.delegate = self
|
|
68
|
+
recognizer.cancelsTouchesInView = false
|
|
69
|
+
recognizer.addTarget(self, action: #selector(handle(_:)))
|
|
70
|
+
view.addGestureRecognizer(recognizer)
|
|
71
|
+
state.gestureRecognizers.append(recognizer)
|
|
72
|
+
built[index, default: []].append(recognizer)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (index, spec) in list.enumerated() {
|
|
76
|
+
guard let recognizers = built[index] else { continue }
|
|
77
|
+
for wait in ((spec["wait_for"] as? [Any]) ?? []).compactMap({ PNProps.int($0) }) {
|
|
78
|
+
guard wait != index, let others = built[wait] else { continue }
|
|
79
|
+
for recognizer in recognizers {
|
|
80
|
+
for other in others {
|
|
81
|
+
recognizer.require(toFail: other)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
view.isUserInteractionEnabled = true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Remove every recognizer previously installed on `view`.
|
|
90
|
+
public func unwire(view: UIView) {
|
|
91
|
+
guard let state = PNViewState.existing(for: view) else { return }
|
|
92
|
+
for recognizer in state.gestureRecognizers {
|
|
93
|
+
recognizer.removeTarget(self, action: nil)
|
|
94
|
+
view.removeGestureRecognizer(recognizer)
|
|
95
|
+
entries.removeValue(forKey: ObjectIdentifier(recognizer))
|
|
96
|
+
}
|
|
97
|
+
state.gestureRecognizers = []
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// MARK: - Recognizer construction
|
|
101
|
+
|
|
102
|
+
/// Build the recognizer(s) for one spec as `(recognizer, direction)`
|
|
103
|
+
/// pairs. `direction` is set only for swipe / fling recognizers: a
|
|
104
|
+
/// `UISwipeGestureRecognizer` reports its configured mask rather than
|
|
105
|
+
/// the direction it resolved, so `direction = "any"` installs one
|
|
106
|
+
/// recognizer per direction and tags each with its own name.
|
|
107
|
+
func makeRecognizers(_ spec: [String: Any]) -> [(UIGestureRecognizer, String?)] {
|
|
108
|
+
switch PNProps.string(spec["kind"]) ?? "" {
|
|
109
|
+
case "tap":
|
|
110
|
+
let tap = UITapGestureRecognizer()
|
|
111
|
+
tap.numberOfTapsRequired = max(1, PNProps.int(spec["n_taps"]) ?? 1)
|
|
112
|
+
return [(tap, nil)]
|
|
113
|
+
case "long_press":
|
|
114
|
+
let press = UILongPressGestureRecognizer()
|
|
115
|
+
press.minimumPressDuration = max(0.01, (PNProps.double(spec["min_duration_ms"]) ?? 500) / 1000)
|
|
116
|
+
press.allowableMovement = CGFloat(PNProps.double(spec["max_distance"]) ?? 12)
|
|
117
|
+
return [(press, nil)]
|
|
118
|
+
case "pan":
|
|
119
|
+
return [(PNPanGestureRecognizer(minDistance: CGFloat(PNProps.double(spec["min_distance"]) ?? 10)), nil)]
|
|
120
|
+
case "swipe", "fling":
|
|
121
|
+
let requested = PNProps.string(spec["direction"]) ?? "any"
|
|
122
|
+
let directions = PNGestureCoordinator.swipeDirectionNames.contains(requested)
|
|
123
|
+
? [requested]
|
|
124
|
+
: PNGestureCoordinator.swipeDirectionNames
|
|
125
|
+
let touches = max(1, PNProps.int(spec["n_pointers"]) ?? 1)
|
|
126
|
+
return directions.map { name in
|
|
127
|
+
let swipe = UISwipeGestureRecognizer()
|
|
128
|
+
swipe.direction = PNGestureCoordinator.swipeDirection(name)
|
|
129
|
+
swipe.numberOfTouchesRequired = touches
|
|
130
|
+
return (swipe, name)
|
|
131
|
+
}
|
|
132
|
+
case "pinch":
|
|
133
|
+
return [(UIPinchGestureRecognizer(), nil)]
|
|
134
|
+
case "rotation":
|
|
135
|
+
return [(UIRotationGestureRecognizer(), nil)]
|
|
136
|
+
default:
|
|
137
|
+
return []
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// The single-direction names a swipe spec may request.
|
|
142
|
+
public static let swipeDirectionNames = ["left", "right", "up", "down"]
|
|
143
|
+
|
|
144
|
+
/// Map a Python direction name to `UISwipeGestureRecognizer.Direction`.
|
|
145
|
+
public static func swipeDirection(_ name: String) -> UISwipeGestureRecognizer.Direction {
|
|
146
|
+
switch name {
|
|
147
|
+
case "left": return .left
|
|
148
|
+
case "right": return .right
|
|
149
|
+
case "up": return .up
|
|
150
|
+
case "down": return .down
|
|
151
|
+
default: return [.left, .right, .up, .down]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// MARK: - Relationship lookups (exposed for tests)
|
|
156
|
+
|
|
157
|
+
/// The spec index recorded for `recognizer`, if it was installed by the coordinator.
|
|
158
|
+
public func index(of recognizer: UIGestureRecognizer) -> Int? {
|
|
159
|
+
entries[ObjectIdentifier(recognizer)]?.index
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Whether the two recognizers are allowed to recognize together.
|
|
163
|
+
public func allowsSimultaneous(_ a: UIGestureRecognizer, _ b: UIGestureRecognizer) -> Bool {
|
|
164
|
+
guard let ea = entries[ObjectIdentifier(a)], let eb = entries[ObjectIdentifier(b)] else {
|
|
165
|
+
// One side isn't ours (Pressable, scroll view): don't block it.
|
|
166
|
+
return true
|
|
167
|
+
}
|
|
168
|
+
if a.view !== b.view { return true }
|
|
169
|
+
return ea.simultaneous.contains(eb.index) || eb.simultaneous.contains(ea.index)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// MARK: - UIGestureRecognizerDelegate
|
|
173
|
+
|
|
174
|
+
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith other: UIGestureRecognizer) -> Bool {
|
|
175
|
+
allowsSimultaneous(gestureRecognizer, other)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// MARK: - Emission
|
|
179
|
+
|
|
180
|
+
@objc private func handle(_ recognizer: UIGestureRecognizer) {
|
|
181
|
+
guard let view = recognizer.view, let entry = entries[ObjectIdentifier(recognizer)] else { return }
|
|
182
|
+
let location = recognizer.location(in: view)
|
|
183
|
+
var payload: [String: Any] = [
|
|
184
|
+
"kind": entry.kind,
|
|
185
|
+
"x": Double(location.x),
|
|
186
|
+
"y": Double(location.y),
|
|
187
|
+
"translation_x": 0.0,
|
|
188
|
+
"translation_y": 0.0,
|
|
189
|
+
"velocity_x": 0.0,
|
|
190
|
+
"velocity_y": 0.0,
|
|
191
|
+
"scale": 1.0,
|
|
192
|
+
"rotation": 0.0,
|
|
193
|
+
"pointer_count": recognizer.numberOfTouches,
|
|
194
|
+
"direction": NSNull(),
|
|
195
|
+
]
|
|
196
|
+
var state = PNGestureCoordinator.stateName(recognizer.state)
|
|
197
|
+
switch recognizer {
|
|
198
|
+
case let pan as UIPanGestureRecognizer:
|
|
199
|
+
let translation = pan.translation(in: view)
|
|
200
|
+
let velocity = pan.velocity(in: view)
|
|
201
|
+
payload["translation_x"] = Double(translation.x)
|
|
202
|
+
payload["translation_y"] = Double(translation.y)
|
|
203
|
+
payload["velocity_x"] = Double(velocity.x)
|
|
204
|
+
payload["velocity_y"] = Double(velocity.y)
|
|
205
|
+
case let pinch as UIPinchGestureRecognizer:
|
|
206
|
+
payload["scale"] = Double(pinch.scale)
|
|
207
|
+
payload["velocity_x"] = Double(pinch.velocity)
|
|
208
|
+
case let rotation as UIRotationGestureRecognizer:
|
|
209
|
+
payload["rotation"] = Double(rotation.rotation)
|
|
210
|
+
payload["velocity_x"] = Double(rotation.velocity)
|
|
211
|
+
case let swipe as UISwipeGestureRecognizer:
|
|
212
|
+
// UIKit reports swipes as a single `.ended`; Python expects `ended`.
|
|
213
|
+
guard swipe.state == .ended || swipe.state == .recognized else { return }
|
|
214
|
+
state = "ended"
|
|
215
|
+
payload["direction"] = entry.direction
|
|
216
|
+
payload["pointer_count"] = max(1, swipe.numberOfTouchesRequired)
|
|
217
|
+
case is UITapGestureRecognizer:
|
|
218
|
+
guard recognizer.state == .ended || recognizer.state == .recognized else { return }
|
|
219
|
+
state = "ended"
|
|
220
|
+
default:
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
guard state != "possible" else { return }
|
|
224
|
+
payload["state"] = state
|
|
225
|
+
PNEvents.emit(view, "gesture:\(entry.index)", [payload])
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static func stateName(_ state: UIGestureRecognizer.State) -> String {
|
|
229
|
+
switch state {
|
|
230
|
+
case .began: return "began"
|
|
231
|
+
case .changed: return "changed"
|
|
232
|
+
case .ended: return "ended"
|
|
233
|
+
case .cancelled: return "cancelled"
|
|
234
|
+
case .failed: return "failed"
|
|
235
|
+
default: return "possible"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// The direction name recorded for a swipe / fling recognizer (exposed for tests).
|
|
240
|
+
public func direction(of recognizer: UIGestureRecognizer) -> String? {
|
|
241
|
+
guard let entry = entries[ObjectIdentifier(recognizer)], entry.kind == "swipe" || entry.kind == "fling" else {
|
|
242
|
+
return nil
|
|
243
|
+
}
|
|
244
|
+
return entry.direction
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// A pan recognizer that records the Python `min_distance` threshold.
|
|
249
|
+
///
|
|
250
|
+
/// UIKit applies its own ~10 pt hysteresis before a pan begins, which
|
|
251
|
+
/// matches the Python default; the value is kept for diagnostics and
|
|
252
|
+
/// for future tuning rather than overriding UIKit's touch pipeline.
|
|
253
|
+
final class PNPanGestureRecognizer: UIPanGestureRecognizer {
|
|
254
|
+
let minDistance: CGFloat
|
|
255
|
+
|
|
256
|
+
init(minDistance: CGFloat) {
|
|
257
|
+
self.minDistance = max(0, minDistance)
|
|
258
|
+
super.init(target: nil, action: nil)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import YogaCore
|
|
3
|
+
|
|
4
|
+
/// The layout tree stays beside UIKit, including intrinsic text measurement.
|
|
5
|
+
final class PNLayoutNode {
|
|
6
|
+
let tag: Int64
|
|
7
|
+
let node: YGNodeRef
|
|
8
|
+
var props: [String: Any] = [:]
|
|
9
|
+
var children: [Int64] = []
|
|
10
|
+
var parent: Int64?
|
|
11
|
+
var frame: [Double] = []
|
|
12
|
+
init(_ tag: Int64) {
|
|
13
|
+
self.tag = tag
|
|
14
|
+
node = YGNodeNew()!
|
|
15
|
+
YGNodeSetContext(node, Unmanaged.passUnretained(self).toOpaque())
|
|
16
|
+
}
|
|
17
|
+
deinit { YGNodeFree(node) }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private let measureLeaf: YGMeasureFunc = { raw, width, widthMode, height, heightMode in
|
|
21
|
+
guard let raw = raw, let context = YGNodeGetContext(raw) else { return YGSize(width: 0, height: 0) }
|
|
22
|
+
let node = Unmanaged<PNLayoutNode>.fromOpaque(context).takeUnretainedValue()
|
|
23
|
+
guard let record = PNViewRegistry.shared.resolve(node.tag) else { return YGSize(width: 0, height: 0) }
|
|
24
|
+
let size = record.manager.measure(view: record.view,
|
|
25
|
+
maxW: widthMode.rawValue == 0 ? 1e6 : CGFloat(width),
|
|
26
|
+
maxH: heightMode.rawValue == 0 ? 1e6 : CGFloat(height))
|
|
27
|
+
return YGSize(width: Float(max(0, size.width)), height: Float(max(0, size.height)))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private let baselineLeaf: YGBaselineFunc = { raw, _, height in
|
|
31
|
+
guard let raw = raw, let context = YGNodeGetContext(raw) else { return height }
|
|
32
|
+
let node = Unmanaged<PNLayoutNode>.fromOpaque(context).takeUnretainedValue()
|
|
33
|
+
if let label = PNViewRegistry.shared.view(for: node.tag) as? UILabel {
|
|
34
|
+
return Float(label.font.ascender)
|
|
35
|
+
}
|
|
36
|
+
return height
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
enum PNLayout {
|
|
40
|
+
static var nodes: [Int64: PNLayoutNode] = [:]
|
|
41
|
+
static var viewport: [String: Any] = [:]
|
|
42
|
+
private static var scheduled = false
|
|
43
|
+
|
|
44
|
+
static func containerDidLayout() {
|
|
45
|
+
guard !scheduled else { return }
|
|
46
|
+
scheduled = true
|
|
47
|
+
DispatchQueue.main.async {
|
|
48
|
+
scheduled = false
|
|
49
|
+
let frames = compute(viewport)
|
|
50
|
+
if !frames.isEmpty { PNBridge.shared.callPython(kind: "layout", tag: 0, name: "", payload: PNJSON.encode(frames)) }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static func reset() {
|
|
55
|
+
for entry in nodes.values { YGNodeRemoveAllChildren(entry.node) }
|
|
56
|
+
nodes.removeAll()
|
|
57
|
+
viewport = [:]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static func observe(_ ops: [PNTransaction.Op]) {
|
|
61
|
+
for op in ops {
|
|
62
|
+
switch op {
|
|
63
|
+
case let .create(tag, _, props):
|
|
64
|
+
let entry = PNLayoutNode(tag)
|
|
65
|
+
nodes[tag] = entry
|
|
66
|
+
update(entry, props)
|
|
67
|
+
case let .update(tag, changed):
|
|
68
|
+
if let entry = nodes[tag] { update(entry, changed) }
|
|
69
|
+
case let .insert(parent, child, index):
|
|
70
|
+
guard let p = nodes[parent], let c = nodes[child] else { continue }
|
|
71
|
+
if let old = c.parent, let previous = nodes[old] {
|
|
72
|
+
previous.children.removeAll { $0 == child }
|
|
73
|
+
YGNodeRemoveChild(previous.node, c.node)
|
|
74
|
+
}
|
|
75
|
+
p.children.insert(child, at: min(index, p.children.count))
|
|
76
|
+
c.parent = parent
|
|
77
|
+
// Detached surfaces keep logical ownership but supply their own viewport.
|
|
78
|
+
let type = PNViewRegistry.shared.resolve(parent)?.typeName ?? ""
|
|
79
|
+
if !["VirtualList", "Modal", "ScreenStack"].contains(type) {
|
|
80
|
+
YGNodeSetMeasureFunc(p.node, nil)
|
|
81
|
+
YGNodeInsertChild(p.node, c.node, min(index, Int(YGNodeGetChildCount(p.node))))
|
|
82
|
+
}
|
|
83
|
+
case let .destroy(tag):
|
|
84
|
+
if let entry = nodes.removeValue(forKey: tag), let parent = entry.parent, let p = nodes[parent] {
|
|
85
|
+
p.children.removeAll { $0 == tag }
|
|
86
|
+
YGNodeRemoveChild(p.node, entry.node)
|
|
87
|
+
}
|
|
88
|
+
case .frame: break
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private static func update(_ entry: PNLayoutNode, _ changed: [String: Any]) {
|
|
94
|
+
for (key, value) in changed {
|
|
95
|
+
if value is NSNull { entry.props.removeValue(forKey: key) }
|
|
96
|
+
else { entry.props[key] = value }
|
|
97
|
+
}
|
|
98
|
+
let fresh = YGNodeNew()!
|
|
99
|
+
for (key, value) in entry.props {
|
|
100
|
+
if let edges = value as? [String: Any], key == "margin" || key == "padding" {
|
|
101
|
+
for (edge, amount) in edges {
|
|
102
|
+
let name = edge == "all" ? key : "\(key)_\(edge)"
|
|
103
|
+
_ = PNYogaSetStyle(fresh, name, String(describing: amount))
|
|
104
|
+
}
|
|
105
|
+
} else { _ = PNYogaSetStyle(fresh, key, String(describing: value)) }
|
|
106
|
+
}
|
|
107
|
+
let type = PNViewRegistry.shared.resolve(entry.tag)?.typeName ?? ""
|
|
108
|
+
if ["ScrollView", "VirtualList", "ScreenStack"].contains(type) {
|
|
109
|
+
YGNodeStyleSetOverflow(fresh, YGOverflow(rawValue: 1)!)
|
|
110
|
+
YGNodeStyleSetFlexShrink(fresh, 1)
|
|
111
|
+
}
|
|
112
|
+
YGNodeCopyStyle(entry.node, fresh)
|
|
113
|
+
YGNodeFree(fresh)
|
|
114
|
+
if YGNodeGetChildCount(entry.node) == 0 && !["View", "Column", "Row", "ScrollView", "Modal", "Portal", "ScreenStack"].contains(type) {
|
|
115
|
+
YGNodeSetMeasureFunc(entry.node, measureLeaf)
|
|
116
|
+
YGNodeSetBaselineFunc(entry.node, baselineLeaf)
|
|
117
|
+
YGNodeMarkDirty(entry.node)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static func compute(_ request: [String: Any]) -> [[Double]] {
|
|
122
|
+
viewport = request
|
|
123
|
+
let width = (request["width"] as? NSNumber)?.floatValue ?? 0
|
|
124
|
+
let height = (request["height"] as? NSNumber)?.floatValue ?? 0
|
|
125
|
+
guard width > 0, height > 0 else { return [] }
|
|
126
|
+
let roots = request["roots"] as? [Int64] ?? []
|
|
127
|
+
for tag in roots {
|
|
128
|
+
if let entry = nodes[tag] { YGNodeCalculateLayout(entry.node, width, height, YGDirection(rawValue: 1)!) }
|
|
129
|
+
}
|
|
130
|
+
// A portal is absent from the screen tree, but remains a Yoga parent
|
|
131
|
+
// so absolute insets and sibling layout resolve against its viewport.
|
|
132
|
+
for entry in nodes.values {
|
|
133
|
+
guard let record = PNViewRegistry.shared.resolve(entry.tag), record.typeName == "Portal" else { continue }
|
|
134
|
+
let size = record.view.bounds.size
|
|
135
|
+
let portalWidth = size.width > 0 ? Float(size.width) : width
|
|
136
|
+
let portalHeight = size.height > 0 ? Float(size.height) : height
|
|
137
|
+
YGNodeStyleSetWidth(entry.node, portalWidth)
|
|
138
|
+
YGNodeStyleSetHeight(entry.node, portalHeight)
|
|
139
|
+
YGNodeCalculateLayout(entry.node, portalWidth, portalHeight, YGDirection(rawValue: 1)!)
|
|
140
|
+
}
|
|
141
|
+
// Every detached root is laid out in its native container's available space.
|
|
142
|
+
for entry in nodes.values where entry.parent != nil && YGNodeGetOwner(entry.node) == nil {
|
|
143
|
+
let parent = entry.parent.flatMap { PNViewRegistry.shared.resolve($0) }
|
|
144
|
+
let screen = PNViewRegistry.shared.resolve(entry.tag)
|
|
145
|
+
let size = (screen?.typeName == "Screen" ? screen?.view.bounds.size : parent?.view.bounds.size)
|
|
146
|
+
?? CGSize(width: CGFloat(width), height: CGFloat(height))
|
|
147
|
+
let isList = parent?.typeName == "VirtualList"
|
|
148
|
+
let horizontal = parent.flatMap { PNViewState.existing(for: $0.view)?.props["horizontal"] as? Bool } ?? false
|
|
149
|
+
YGNodeCalculateLayout(entry.node, isList && horizontal ? Float.nan : Float(size.width > 0 ? size.width : CGFloat(width)),
|
|
150
|
+
isList && !horizontal ? Float.nan : Float(size.height > 0 ? size.height : CGFloat(height)), YGDirection(rawValue: 1)!)
|
|
151
|
+
}
|
|
152
|
+
var frames: [[Double]] = []
|
|
153
|
+
for entry in nodes.values {
|
|
154
|
+
let frame = [Double(YGNodeLayoutGetLeft(entry.node)), Double(YGNodeLayoutGetTop(entry.node)),
|
|
155
|
+
Double(YGNodeLayoutGetWidth(entry.node)), Double(YGNodeLayoutGetHeight(entry.node))]
|
|
156
|
+
if frame == entry.frame { continue }
|
|
157
|
+
entry.frame = frame
|
|
158
|
+
if !roots.contains(entry.tag), let record = PNViewRegistry.shared.resolve(entry.tag) {
|
|
159
|
+
record.manager.setFrame(view: record.view, x: frame[0], y: frame[1], w: frame[2], h: frame[3])
|
|
160
|
+
}
|
|
161
|
+
PNVirtualListManager.measured(entry.tag, CGSize(width: frame[2], height: frame[3]))
|
|
162
|
+
frames.append([Double(entry.tag)] + frame)
|
|
163
|
+
}
|
|
164
|
+
return frames
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static func invalidate(_ tag: Int64) {
|
|
168
|
+
guard let node = nodes[tag], YGNodeHasMeasureFunc(node.node) else { return }
|
|
169
|
+
YGNodeMarkDirty(node.node)
|
|
170
|
+
let frames = compute(viewport)
|
|
171
|
+
if !frames.isEmpty { PNBridge.shared.callPython(kind: "layout", tag: 0, name: "", payload: PNJSON.encode(frames)) }
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Presents `UIAlertController`s from the top view controller.
|
|
4
|
+
public enum PNAlertPresenter {
|
|
5
|
+
/// Show an alert or action sheet. `buttons` are
|
|
6
|
+
/// `{"label", "style": "default"|"cancel"|"destructive"}` dicts;
|
|
7
|
+
/// `completion` receives the tapped index, or `-1` on dismissal.
|
|
8
|
+
public static func present(
|
|
9
|
+
title: String?, message: String?, buttons: [[String: Any]], style: String,
|
|
10
|
+
completion: @escaping (Int) -> Void
|
|
11
|
+
) {
|
|
12
|
+
guard let top = PNWindow.topViewController() else {
|
|
13
|
+
completion(-1)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
let preferred: UIAlertController.Style = style == "action_sheet" ? .actionSheet : .alert
|
|
17
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: preferred)
|
|
18
|
+
var settled = false
|
|
19
|
+
let finish: (Int) -> Void = { index in
|
|
20
|
+
if settled { return }
|
|
21
|
+
settled = true
|
|
22
|
+
completion(index)
|
|
23
|
+
}
|
|
24
|
+
let list = buttons.isEmpty ? [["label": "OK"]] : buttons
|
|
25
|
+
for (index, button) in list.enumerated() {
|
|
26
|
+
let label = PNProps.string(button["label"]) ?? PNProps.string(button["title"]) ?? "OK"
|
|
27
|
+
let actionStyle: UIAlertAction.Style
|
|
28
|
+
switch PNProps.string(button["style"]) {
|
|
29
|
+
case "cancel": actionStyle = .cancel
|
|
30
|
+
case "destructive": actionStyle = .destructive
|
|
31
|
+
default: actionStyle = .default
|
|
32
|
+
}
|
|
33
|
+
alert.addAction(UIAlertAction(title: label, style: actionStyle) { _ in finish(index) })
|
|
34
|
+
}
|
|
35
|
+
if preferred == .actionSheet, let popover = alert.popoverPresentationController {
|
|
36
|
+
popover.sourceView = top.view
|
|
37
|
+
popover.sourceRect = CGRect(x: top.view.bounds.midX, y: top.view.bounds.maxY - 1, width: 1, height: 1)
|
|
38
|
+
popover.permittedArrowDirections = []
|
|
39
|
+
}
|
|
40
|
+
top.present(alert, animated: true)
|
|
41
|
+
// A sheet dismissed by tapping outside (iPad popover) never runs
|
|
42
|
+
// an action; report -1 once it disappears.
|
|
43
|
+
PNAlertDismissWatcher.watch(alert) { finish(-1) }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Observes an alert's disappearance to report an outside-tap dismissal.
|
|
48
|
+
final class PNAlertDismissWatcher: NSObject, UIPopoverPresentationControllerDelegate {
|
|
49
|
+
private let onDismiss: () -> Void
|
|
50
|
+
private static var watchers: [ObjectIdentifier: PNAlertDismissWatcher] = [:]
|
|
51
|
+
|
|
52
|
+
private init(onDismiss: @escaping () -> Void) {
|
|
53
|
+
self.onDismiss = onDismiss
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static func watch(_ alert: UIAlertController, onDismiss: @escaping () -> Void) {
|
|
57
|
+
let watcher = PNAlertDismissWatcher(onDismiss: onDismiss)
|
|
58
|
+
watchers[ObjectIdentifier(alert)] = watcher
|
|
59
|
+
alert.popoverPresentationController?.delegate = watcher
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
|
63
|
+
onDismiss()
|
|
64
|
+
PNAlertDismissWatcher.watchers.removeValue(forKey: ObjectIdentifier(presentationController.presentedViewController))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// `Alert`: `present` (async, resolves the tapped index) and `show` (fire-and-forget).
|
|
69
|
+
public final class AlertModule: PNNativeModule {
|
|
70
|
+
public static let name = "Alert"
|
|
71
|
+
|
|
72
|
+
public init() {}
|
|
73
|
+
|
|
74
|
+
public func call(_ method: String, args: [String: Any], promise: PNPromise) {
|
|
75
|
+
switch method {
|
|
76
|
+
case "present", "show":
|
|
77
|
+
let buttons = ((args["buttons"] as? [Any]) ?? []).compactMap { $0 as? [String: Any] }
|
|
78
|
+
let fireAndForget = method == "show"
|
|
79
|
+
if fireAndForget { promise.resolve(nil) }
|
|
80
|
+
PNAlertPresenter.present(
|
|
81
|
+
title: PNProps.string(args["title"]),
|
|
82
|
+
message: PNProps.string(args["message"]),
|
|
83
|
+
buttons: buttons,
|
|
84
|
+
style: PNProps.string(args["style"]) ?? "alert"
|
|
85
|
+
) { index in
|
|
86
|
+
if !fireAndForget { promise.resolve(index) }
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
promise.reject("Alert has no method '\(method)'", code: "unknown_method")
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// `Host`: screen root attachment, stack navigation, viewport queries,
|
|
4
|
+
/// and the main-queue pump.
|
|
5
|
+
public final class HostModule: PNNativeModule {
|
|
6
|
+
public static let name = "Host"
|
|
7
|
+
|
|
8
|
+
public init() {}
|
|
9
|
+
|
|
10
|
+
public func call(_ method: String, args: [String: Any], promise: PNPromise) {
|
|
11
|
+
switch method {
|
|
12
|
+
case "cache_state":
|
|
13
|
+
guard let controller = screen(args, promise) else { return }
|
|
14
|
+
controller.cachedStateJSON = args["state"] as? String
|
|
15
|
+
promise.resolve(nil)
|
|
16
|
+
case "attach_root":
|
|
17
|
+
guard let controller = screen(args, promise) else { return }
|
|
18
|
+
guard let tag = PNProps.int(args["tag"]), let view = PNViewRegistry.shared.view(for: Int64(tag)) else {
|
|
19
|
+
promise.reject("attach_root: unknown view tag", code: "unknown_tag")
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
controller.attachRoot(view)
|
|
23
|
+
promise.resolve(controller.viewport())
|
|
24
|
+
case "detach_root":
|
|
25
|
+
guard let controller = screen(args, promise) else { return }
|
|
26
|
+
if let tag = PNProps.int(args["tag"]), let view = PNViewRegistry.shared.view(for: Int64(tag)) {
|
|
27
|
+
controller.detachRoot(view)
|
|
28
|
+
}
|
|
29
|
+
promise.resolve(nil)
|
|
30
|
+
case "viewport":
|
|
31
|
+
guard let controller = screen(args, promise) else { return }
|
|
32
|
+
promise.resolve(controller.viewport())
|
|
33
|
+
case "set_options":
|
|
34
|
+
guard let controller = screen(args, promise) else { return }
|
|
35
|
+
HostModule.applyOptions(PNProps.dict(args["options"]) ?? [:], to: controller)
|
|
36
|
+
promise.resolve(nil)
|
|
37
|
+
case "finish":
|
|
38
|
+
promise.resolve(nil)
|
|
39
|
+
default:
|
|
40
|
+
promise.reject("Host has no method '\(method)'", code: "unknown_method")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// MARK: - Helpers
|
|
45
|
+
|
|
46
|
+
private func screen(_ args: [String: Any], _ promise: PNPromise) -> PNViewController? {
|
|
47
|
+
guard let id = PNProps.int(args["screen"]) else {
|
|
48
|
+
promise.reject("missing 'screen' id", code: "bad_args")
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
guard let controller = PNScreenRegistry.shared.controller(for: Int64(id)) else {
|
|
52
|
+
promise.reject("no screen with id \(id)", code: "unknown_screen")
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
return controller
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static func applyOptions(_ options: [String: Any], to controller: UIViewController) {
|
|
59
|
+
if let title = PNProps.string(options["title"]) {
|
|
60
|
+
controller.title = title
|
|
61
|
+
}
|
|
62
|
+
if let hidden = PNProps.bool(options["header_shown"]) {
|
|
63
|
+
controller.navigationController?.setNavigationBarHidden(!hidden, animated: false)
|
|
64
|
+
}
|
|
65
|
+
if let hidesBack = PNProps.bool(options["hide_back_button"]) {
|
|
66
|
+
controller.navigationItem.hidesBackButton = hidesBack
|
|
67
|
+
}
|
|
68
|
+
if let color = PNColor.parse(options["header_tint_color"]) {
|
|
69
|
+
controller.navigationController?.navigationBar.tintColor = color
|
|
70
|
+
}
|
|
71
|
+
if let color = PNColor.parse(options["header_background_color"]) {
|
|
72
|
+
let appearance = UINavigationBarAppearance()
|
|
73
|
+
appearance.configureWithOpaqueBackground()
|
|
74
|
+
appearance.backgroundColor = color
|
|
75
|
+
controller.navigationItem.standardAppearance = appearance
|
|
76
|
+
controller.navigationItem.scrollEdgeAppearance = appearance
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
}
|