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,63 @@
|
|
|
1
|
+
"""Screen hosts: the bridge between a native screen and the reconciler.
|
|
2
|
+
|
|
3
|
+
On every bridge platform (iOS, Android, and the browser preview) the
|
|
4
|
+
native runtime creates screens through the bridge
|
|
5
|
+
(``callback("host", screen_id, "create", {...})``), which lands in
|
|
6
|
+
[`NativeScreenHost`][pythonnative.hosts.native.NativeScreenHost]. Unit
|
|
7
|
+
tests use the headless base [`ScreenHost`][pythonnative.hosts.ScreenHost]
|
|
8
|
+
with a fake backend, created through
|
|
9
|
+
[`create_screen`][pythonnative.hosts.create_screen].
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any, Optional, Sequence, Type
|
|
15
|
+
|
|
16
|
+
from ..utils import IS_NATIVE
|
|
17
|
+
from .base import ScreenHost, import_component
|
|
18
|
+
|
|
19
|
+
__all__ = ["ScreenHost", "create_screen", "host_class", "import_component", "live_hosts"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def host_class() -> Type[ScreenHost]:
|
|
23
|
+
"""The host class for the current runtime."""
|
|
24
|
+
if IS_NATIVE:
|
|
25
|
+
from .native import NativeScreenHost
|
|
26
|
+
|
|
27
|
+
return NativeScreenHost
|
|
28
|
+
return ScreenHost
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def create_screen(component_path: str, native_instance: Any = None, args_json: Optional[str] = None) -> ScreenHost:
|
|
32
|
+
"""Create the screen host for a root component.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
component_path: ``"app.main"`` (the module's ``App`` is used) or a
|
|
36
|
+
dotted path like ``"app.main.RootScreen"``. Imported lazily
|
|
37
|
+
so the dev server can reload it.
|
|
38
|
+
native_instance: The platform object owning the screen (the
|
|
39
|
+
integer screen id on the bridge platforms).
|
|
40
|
+
args_json: Optional JSON launch arguments (pushed screens
|
|
41
|
+
receive their navigation history here).
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A host ready for ``on_create`` and the other lifecycle calls.
|
|
45
|
+
"""
|
|
46
|
+
component = import_component(component_path)
|
|
47
|
+
host = host_class()(native_instance, component_path, component)
|
|
48
|
+
if args_json:
|
|
49
|
+
host.set_args(args_json)
|
|
50
|
+
return host
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def live_hosts() -> Sequence[ScreenHost]:
|
|
54
|
+
"""Every screen host currently mounted by the native runtime.
|
|
55
|
+
|
|
56
|
+
Returns an empty sequence off the bridge platforms; tests hold their
|
|
57
|
+
own host references.
|
|
58
|
+
"""
|
|
59
|
+
if not IS_NATIVE:
|
|
60
|
+
return []
|
|
61
|
+
from .native import live_hosts as native_live_hosts
|
|
62
|
+
|
|
63
|
+
return list(native_live_hosts())
|
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"""Platform-independent application surface host.
|
|
2
|
+
|
|
3
|
+
A host mounts one logical application tree, publishes native lifecycle and
|
|
4
|
+
viewport changes, schedules Python work on the application thread, and caches
|
|
5
|
+
navigation restoration state. Logical screen containers present the tree's
|
|
6
|
+
screen roots through native navigation controllers and fragments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import importlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import traceback
|
|
16
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple
|
|
17
|
+
|
|
18
|
+
from .. import diagnostics
|
|
19
|
+
from ..element import Element
|
|
20
|
+
|
|
21
|
+
__all__ = ["ScreenHost", "import_component", "log_pn", "debug_enabled"]
|
|
22
|
+
|
|
23
|
+
MAX_RENDER_PASSES = 25
|
|
24
|
+
_DEBUG_ENV = "PYTHONNATIVE_DEBUG"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def debug_enabled() -> bool:
|
|
28
|
+
"""Return whether the ``PYTHONNATIVE_DEBUG`` environment variable turns on host diagnostics."""
|
|
29
|
+
return os.environ.get(_DEBUG_ENV, "").lower() in {"1", "true", "yes", "on"}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def log_pn(msg: str) -> None:
|
|
33
|
+
"""Emit optional diagnostics when ``PYTHONNATIVE_DEBUG`` is enabled."""
|
|
34
|
+
if not debug_enabled():
|
|
35
|
+
return
|
|
36
|
+
try:
|
|
37
|
+
print(f"[PN] {msg}", flush=True)
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ======================================================================
|
|
43
|
+
# Component resolution
|
|
44
|
+
# ======================================================================
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _missing_module_is_target(exc: ModuleNotFoundError, dotted: str) -> bool:
|
|
48
|
+
"""Whether ``exc`` means ``dotted`` itself is absent (vs. one of its imports)."""
|
|
49
|
+
missing = exc.name or ""
|
|
50
|
+
return missing == dotted or dotted.startswith(missing + ".")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def import_component(component_path: str) -> Any:
|
|
54
|
+
"""Import a root component by module path or dotted attribute path.
|
|
55
|
+
|
|
56
|
+
``"app.main"`` imports the module and returns its ``App`` attribute;
|
|
57
|
+
``"app.main.RootScreen"`` returns the named attribute. Errors raised
|
|
58
|
+
*inside* a resolvable module (a missing third-party dependency, a
|
|
59
|
+
syntax error) propagate unchanged so the real cause stays visible.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ImportError: When neither form resolves.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
module = importlib.import_module(component_path)
|
|
66
|
+
except ModuleNotFoundError as exc:
|
|
67
|
+
if not _missing_module_is_target(exc, component_path):
|
|
68
|
+
raise
|
|
69
|
+
module = None
|
|
70
|
+
if module is not None:
|
|
71
|
+
component = getattr(module, "App", None)
|
|
72
|
+
if component is not None:
|
|
73
|
+
return component
|
|
74
|
+
|
|
75
|
+
if "." in component_path:
|
|
76
|
+
module_path, attr = component_path.rsplit(".", 1)
|
|
77
|
+
try:
|
|
78
|
+
parent = importlib.import_module(module_path)
|
|
79
|
+
except ModuleNotFoundError as exc:
|
|
80
|
+
if not _missing_module_is_target(exc, module_path):
|
|
81
|
+
raise
|
|
82
|
+
parent = None
|
|
83
|
+
if parent is not None:
|
|
84
|
+
component = getattr(parent, attr, None)
|
|
85
|
+
if component is not None:
|
|
86
|
+
return component
|
|
87
|
+
|
|
88
|
+
raise ImportError(
|
|
89
|
+
f"Could not resolve component {component_path!r}. Define a top-level `App` function in the "
|
|
90
|
+
"module (e.g. `app/main.py`) or pass an explicit dotted path like `app.main.RootScreen`."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ======================================================================
|
|
95
|
+
# Host
|
|
96
|
+
# ======================================================================
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ScreenHost:
|
|
100
|
+
"""Base screen host; see the module docstring.
|
|
101
|
+
|
|
102
|
+
Attributes:
|
|
103
|
+
native_instance: The platform object owning this screen
|
|
104
|
+
(an integer screen id on the bridge platforms).
|
|
105
|
+
component_path: Import path of the root component.
|
|
106
|
+
args: Launch arguments (``set_args``), including the serialized
|
|
107
|
+
navigation state under ``"pn_nav"`` for pushed screens.
|
|
108
|
+
reconciler: The mounted reconciler, or ``None`` before
|
|
109
|
+
``on_create`` / after ``on_destroy``.
|
|
110
|
+
is_focused: Whether the screen is presented (``on_resume`` /
|
|
111
|
+
``on_pause``).
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(self, native_instance: Any, component_path: str, component: Any) -> None:
|
|
115
|
+
self.native_instance = native_instance
|
|
116
|
+
self.component_path = component_path
|
|
117
|
+
self.component = component
|
|
118
|
+
self.args: Dict[str, Any] = {}
|
|
119
|
+
self.reconciler: Any = None
|
|
120
|
+
self.root_native_view: Any = None
|
|
121
|
+
self.is_focused = True
|
|
122
|
+
self._focus_listeners: List[Callable[[bool], None]] = []
|
|
123
|
+
self._is_rendering = False
|
|
124
|
+
self._render_queued = False
|
|
125
|
+
self._render_scheduled = False
|
|
126
|
+
self._redbox_reconciler: Any = None
|
|
127
|
+
self._redbox_root: Any = None
|
|
128
|
+
|
|
129
|
+
# ------------------------------------------------------------------
|
|
130
|
+
# Platform primitives (override)
|
|
131
|
+
# ------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
def _attach_root(self, native_view: Any) -> None:
|
|
134
|
+
"""Place ``native_view`` into the platform container."""
|
|
135
|
+
|
|
136
|
+
def _detach_root(self, native_view: Any) -> None:
|
|
137
|
+
"""Remove ``native_view`` from the platform container."""
|
|
138
|
+
|
|
139
|
+
def _initial_viewport_size(self) -> Optional[Tuple[float, float]]:
|
|
140
|
+
"""A plausible viewport size before the first layout (see ``_seed_viewport``)."""
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def _schedule_render_async(self) -> bool:
|
|
144
|
+
"""Defer a render to the platform's next UI turn; ``False`` renders inline."""
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
def _native_set_options(self, options: Dict[str, Any]) -> None:
|
|
148
|
+
"""Apply header options (``title`` at minimum) to the native chrome."""
|
|
149
|
+
|
|
150
|
+
# ------------------------------------------------------------------
|
|
151
|
+
# HostNavigator protocol
|
|
152
|
+
# ------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
def initial_navigation_state(self) -> Optional[Dict[str, Any]]:
|
|
155
|
+
"""Return the serialized navigation state from ``args["pn_nav"]``, or ``None`` for the first screen."""
|
|
156
|
+
from ..navigation.host import initial_state_from_args
|
|
157
|
+
|
|
158
|
+
return initial_state_from_args(self.args)
|
|
159
|
+
|
|
160
|
+
def cache_navigation_state(self, state: Dict[str, Any]) -> None:
|
|
161
|
+
"""Publish a restorable immutable navigation snapshot to the native host."""
|
|
162
|
+
from ..navigation.host import NAV_STATE_ARG
|
|
163
|
+
|
|
164
|
+
self.args = dict(self.args or {}) | {NAV_STATE_ARG: state}
|
|
165
|
+
|
|
166
|
+
def set_screen_options(self, options: Dict[str, Any]) -> None:
|
|
167
|
+
"""Apply header ``options`` (``title`` and friends) to the native chrome."""
|
|
168
|
+
self._native_set_options(dict(options))
|
|
169
|
+
|
|
170
|
+
def add_focus_listener(self, callback: Callable[[bool], None]) -> Callable[[], None]:
|
|
171
|
+
"""Subscribe to focus changes (``on_resume`` / ``on_pause``); returns an unsubscribe callable."""
|
|
172
|
+
self._focus_listeners.append(callback)
|
|
173
|
+
|
|
174
|
+
def remove() -> None:
|
|
175
|
+
try:
|
|
176
|
+
self._focus_listeners.remove(callback)
|
|
177
|
+
except ValueError:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
return remove
|
|
181
|
+
|
|
182
|
+
# ------------------------------------------------------------------
|
|
183
|
+
# Lifecycle (called by the platform)
|
|
184
|
+
# ------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
def on_create(self) -> None:
|
|
187
|
+
"""Mount the root component (idempotent across native view recreation).
|
|
188
|
+
|
|
189
|
+
Android destroys and recreates a fragment's view when the user
|
|
190
|
+
pops back to it and calls ``on_create`` again; the Python host
|
|
191
|
+
persists, so an already-mounted tree is simply re-attached.
|
|
192
|
+
"""
|
|
193
|
+
self._register_redbox_reporter()
|
|
194
|
+
if self.reconciler is not None and self.root_native_view is not None:
|
|
195
|
+
self._attach_root(self.root_native_view)
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
self.reconciler = self._new_reconciler()
|
|
199
|
+
self._seed_viewport()
|
|
200
|
+
try:
|
|
201
|
+
self._is_rendering = True
|
|
202
|
+
try:
|
|
203
|
+
self.root_native_view = self.reconciler.mount(self._root_element())
|
|
204
|
+
self._attach_root(self.root_native_view)
|
|
205
|
+
self._drain_renders()
|
|
206
|
+
finally:
|
|
207
|
+
self._is_rendering = False
|
|
208
|
+
except Exception as exc:
|
|
209
|
+
if not diagnostics.is_dev():
|
|
210
|
+
raise
|
|
211
|
+
self.show_redbox(exc, phase="mount")
|
|
212
|
+
|
|
213
|
+
def on_start(self) -> None:
|
|
214
|
+
"""Handle the platform's start event (no-op by default)."""
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
def on_resume(self) -> None:
|
|
218
|
+
"""Mark the screen focused and notify focus listeners."""
|
|
219
|
+
self.set_focused(True)
|
|
220
|
+
|
|
221
|
+
def on_layout(self) -> None:
|
|
222
|
+
"""Handle a native layout pass (no-op by default; platforms sync the viewport here)."""
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
def on_pause(self) -> None:
|
|
226
|
+
"""Mark the screen unfocused and notify focus listeners."""
|
|
227
|
+
self.set_focused(False)
|
|
228
|
+
|
|
229
|
+
def on_stop(self) -> None:
|
|
230
|
+
"""Handle the platform's stop event (no-op by default)."""
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
def on_restart(self) -> None:
|
|
234
|
+
"""Handle the platform's restart event (no-op by default)."""
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
def on_save_instance_state(self) -> None:
|
|
238
|
+
"""Handle the platform's save-state request (no-op by default)."""
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
def on_restore_instance_state(self) -> None:
|
|
242
|
+
"""Handle the platform's restore-state event (no-op by default)."""
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
def on_destroy(self) -> None:
|
|
246
|
+
"""Tear down: unmount (running effect cleanups), release native views."""
|
|
247
|
+
self.clear_redbox(reattach=False)
|
|
248
|
+
diagnostics.set_error_reporter(self, None)
|
|
249
|
+
reconciler, self.reconciler = self.reconciler, None
|
|
250
|
+
if reconciler is not None:
|
|
251
|
+
try:
|
|
252
|
+
reconciler.unmount()
|
|
253
|
+
except Exception:
|
|
254
|
+
log_pn("on_destroy: reconciler.unmount() failed")
|
|
255
|
+
root, self.root_native_view = self.root_native_view, None
|
|
256
|
+
if root is not None:
|
|
257
|
+
try:
|
|
258
|
+
self._detach_root(root)
|
|
259
|
+
except Exception:
|
|
260
|
+
pass
|
|
261
|
+
self._focus_listeners = []
|
|
262
|
+
|
|
263
|
+
def on_back_pressed(self) -> bool:
|
|
264
|
+
"""Offer the system back action to ``use_back_handler`` subscribers.
|
|
265
|
+
|
|
266
|
+
Returns ``True`` when a handler consumed the event, in which
|
|
267
|
+
case the platform must not pop the screen.
|
|
268
|
+
"""
|
|
269
|
+
if self.reconciler is None:
|
|
270
|
+
return False
|
|
271
|
+
try:
|
|
272
|
+
return bool(self.reconciler.dispatch_back_press())
|
|
273
|
+
except Exception as exc:
|
|
274
|
+
if not diagnostics.report_error(exc, phase="back handler"):
|
|
275
|
+
traceback.print_exc()
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
def set_args(self, args: Any) -> None:
|
|
279
|
+
"""Record launch arguments (a dict or a JSON string)."""
|
|
280
|
+
if isinstance(args, str):
|
|
281
|
+
try:
|
|
282
|
+
parsed = json.loads(args) or {}
|
|
283
|
+
except Exception:
|
|
284
|
+
parsed = {}
|
|
285
|
+
self.args = parsed if isinstance(parsed, dict) else {}
|
|
286
|
+
return
|
|
287
|
+
self.args = args if isinstance(args, dict) else {}
|
|
288
|
+
|
|
289
|
+
def set_focused(self, focused: bool) -> None:
|
|
290
|
+
"""Update ``is_focused`` and notify focus listeners when the value changes."""
|
|
291
|
+
if self.is_focused == focused:
|
|
292
|
+
return
|
|
293
|
+
self.is_focused = focused
|
|
294
|
+
for callback in list(self._focus_listeners):
|
|
295
|
+
try:
|
|
296
|
+
callback(focused)
|
|
297
|
+
except Exception:
|
|
298
|
+
pass
|
|
299
|
+
|
|
300
|
+
def set_viewport_size(self, width: float, height: float) -> None:
|
|
301
|
+
"""Forward a viewport-size change (in points) to the reconciler."""
|
|
302
|
+
if self.reconciler is None or width <= 0 or height <= 0:
|
|
303
|
+
return
|
|
304
|
+
self.reconciler.set_viewport_size(float(width), float(height))
|
|
305
|
+
if self._redbox_reconciler is not None:
|
|
306
|
+
self._redbox_reconciler.set_viewport_size(float(width), float(height))
|
|
307
|
+
try:
|
|
308
|
+
from .. import platform_metrics
|
|
309
|
+
|
|
310
|
+
platform_metrics.set_window_dimensions(float(width), float(height))
|
|
311
|
+
except Exception:
|
|
312
|
+
pass
|
|
313
|
+
|
|
314
|
+
# ------------------------------------------------------------------
|
|
315
|
+
# Rendering
|
|
316
|
+
# ------------------------------------------------------------------
|
|
317
|
+
|
|
318
|
+
def _root_element(self) -> Element:
|
|
319
|
+
from ..navigation.host import HostRoot
|
|
320
|
+
|
|
321
|
+
self.component = import_component(self.component_path)
|
|
322
|
+
return HostRoot(self.component(), host=self)
|
|
323
|
+
|
|
324
|
+
def _new_reconciler(self) -> Any:
|
|
325
|
+
from ..native_views import get_registry
|
|
326
|
+
from ..reconciler import Reconciler
|
|
327
|
+
|
|
328
|
+
reconciler = Reconciler(get_registry())
|
|
329
|
+
reconciler.on_render_requested = self.request_render
|
|
330
|
+
return reconciler
|
|
331
|
+
|
|
332
|
+
def _seed_viewport(self) -> None:
|
|
333
|
+
"""Give the reconciler a plausible viewport before the first mount.
|
|
334
|
+
|
|
335
|
+
The authoritative size arrives right after attach, but the mount
|
|
336
|
+
commit has already run by then; without a viewport its layout
|
|
337
|
+
pass is skipped and mount-time ``use_layout_effect`` callbacks
|
|
338
|
+
would observe no frames.
|
|
339
|
+
"""
|
|
340
|
+
try:
|
|
341
|
+
size = self._initial_viewport_size()
|
|
342
|
+
except Exception:
|
|
343
|
+
return
|
|
344
|
+
if size and size[0] > 0 and size[1] > 0:
|
|
345
|
+
self.reconciler.set_viewport_size(float(size[0]), float(size[1]))
|
|
346
|
+
|
|
347
|
+
def request_render(self) -> None:
|
|
348
|
+
"""Request a render pass (queued if one is in progress)."""
|
|
349
|
+
if self.reconciler is None:
|
|
350
|
+
return
|
|
351
|
+
if self._is_rendering:
|
|
352
|
+
self._render_queued = True
|
|
353
|
+
return
|
|
354
|
+
if self._schedule_render_async():
|
|
355
|
+
return
|
|
356
|
+
self._re_render()
|
|
357
|
+
|
|
358
|
+
def flush_scheduled_render(self) -> None:
|
|
359
|
+
"""Run a render deferred by ``_schedule_render_async`` (platform UI turn)."""
|
|
360
|
+
self._render_scheduled = False
|
|
361
|
+
if self.reconciler is None:
|
|
362
|
+
return
|
|
363
|
+
if self._is_rendering:
|
|
364
|
+
self._render_queued = True
|
|
365
|
+
self._schedule_render_async()
|
|
366
|
+
return
|
|
367
|
+
self._re_render()
|
|
368
|
+
|
|
369
|
+
def _re_render(self) -> None:
|
|
370
|
+
log_pn("_re_render: starting local render pass")
|
|
371
|
+
try:
|
|
372
|
+
self._is_rendering = True
|
|
373
|
+
try:
|
|
374
|
+
self._render_queued = False
|
|
375
|
+
self._commit_dirty()
|
|
376
|
+
self._drain_renders()
|
|
377
|
+
finally:
|
|
378
|
+
self._is_rendering = False
|
|
379
|
+
except Exception as exc:
|
|
380
|
+
if not diagnostics.is_dev():
|
|
381
|
+
raise
|
|
382
|
+
self.show_redbox(exc, phase="render")
|
|
383
|
+
log_pn("_re_render: done")
|
|
384
|
+
|
|
385
|
+
def _commit_dirty(self) -> None:
|
|
386
|
+
new_root = self.reconciler.flush_dirty()
|
|
387
|
+
if new_root is not self.root_native_view:
|
|
388
|
+
log_pn("_commit_dirty: root view changed; reattaching")
|
|
389
|
+
self._detach_root(self.root_native_view)
|
|
390
|
+
self.root_native_view = new_root
|
|
391
|
+
self._attach_root(new_root)
|
|
392
|
+
|
|
393
|
+
def _drain_renders(self) -> None:
|
|
394
|
+
"""Flush renders queued by effects; capped to break runaway loops."""
|
|
395
|
+
for i in range(MAX_RENDER_PASSES):
|
|
396
|
+
if not self._render_queued:
|
|
397
|
+
break
|
|
398
|
+
log_pn(f"_drain_renders: pass #{i + 1}")
|
|
399
|
+
self._render_queued = False
|
|
400
|
+
self._commit_dirty()
|
|
401
|
+
|
|
402
|
+
# ------------------------------------------------------------------
|
|
403
|
+
# RedBox (dev-mode error overlay)
|
|
404
|
+
# ------------------------------------------------------------------
|
|
405
|
+
|
|
406
|
+
def _register_redbox_reporter(self) -> None:
|
|
407
|
+
if diagnostics.is_dev():
|
|
408
|
+
diagnostics.set_error_reporter(self, lambda exc, phase: self.show_redbox(exc, phase))
|
|
409
|
+
|
|
410
|
+
def show_redbox(self, exc: BaseException, phase: str = "render") -> None:
|
|
411
|
+
"""Mount the dev error overlay over this screen (from any thread)."""
|
|
412
|
+
log_pn(f"show_redbox: {type(exc).__name__} during {phase}")
|
|
413
|
+
try:
|
|
414
|
+
print(f"[PN] {phase} error:", file=sys.stderr)
|
|
415
|
+
traceback.print_exception(type(exc), exc, exc.__traceback__)
|
|
416
|
+
except Exception:
|
|
417
|
+
pass
|
|
418
|
+
|
|
419
|
+
def mount() -> None:
|
|
420
|
+
try:
|
|
421
|
+
self.clear_redbox(reattach=False)
|
|
422
|
+
from ..native_views import get_registry
|
|
423
|
+
from ..reconciler import Reconciler
|
|
424
|
+
|
|
425
|
+
redbox = Reconciler(get_registry())
|
|
426
|
+
element = _redbox_element(exc, phase, lambda: self.clear_redbox())
|
|
427
|
+
root = redbox.mount(element)
|
|
428
|
+
width, height = self.reconciler.viewport_size if self.reconciler is not None else (0.0, 0.0)
|
|
429
|
+
if width <= 0 or height <= 0:
|
|
430
|
+
from .. import platform_metrics
|
|
431
|
+
|
|
432
|
+
dims = platform_metrics.get_window_dimensions()
|
|
433
|
+
width, height = dims.width, dims.height
|
|
434
|
+
if width > 0 and height > 0:
|
|
435
|
+
redbox.set_viewport_size(width, height)
|
|
436
|
+
self._redbox_reconciler = redbox
|
|
437
|
+
self._redbox_root = root
|
|
438
|
+
if self.root_native_view is not None:
|
|
439
|
+
self._detach_root(self.root_native_view)
|
|
440
|
+
self._attach_root(root)
|
|
441
|
+
except Exception:
|
|
442
|
+
print("[PN] RedBox failed to mount:", file=sys.stderr)
|
|
443
|
+
traceback.print_exc()
|
|
444
|
+
|
|
445
|
+
from ..runtime import call_on_application_thread
|
|
446
|
+
|
|
447
|
+
call_on_application_thread(mount)
|
|
448
|
+
|
|
449
|
+
def clear_redbox(self, reattach: bool = True) -> None:
|
|
450
|
+
"""Dismiss the dev error overlay, reattaching the app's root view unless ``reattach`` is ``False``."""
|
|
451
|
+
redbox, self._redbox_reconciler = self._redbox_reconciler, None
|
|
452
|
+
self._redbox_root = None
|
|
453
|
+
if redbox is None:
|
|
454
|
+
return
|
|
455
|
+
try:
|
|
456
|
+
redbox.unmount()
|
|
457
|
+
except Exception:
|
|
458
|
+
pass
|
|
459
|
+
if reattach and self.root_native_view is not None:
|
|
460
|
+
try:
|
|
461
|
+
self._attach_root(self.root_native_view)
|
|
462
|
+
except Exception:
|
|
463
|
+
pass
|
|
464
|
+
|
|
465
|
+
# ------------------------------------------------------------------
|
|
466
|
+
# Hot reload
|
|
467
|
+
# ------------------------------------------------------------------
|
|
468
|
+
|
|
469
|
+
def reload(self, changed_modules: Optional[Sequence[str]] = None) -> str:
|
|
470
|
+
"""Reload ``changed_modules`` (and their dependents) and refresh this tree.
|
|
471
|
+
|
|
472
|
+
A convenience for a single host (tests, scripts). The dev client
|
|
473
|
+
reloads modules once per process and then calls
|
|
474
|
+
[`refresh`][pythonnative.hosts.ScreenHost.refresh] on every live
|
|
475
|
+
host; see ``pythonnative.hot_reload.apply_reload``.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
``"fast_refresh"``, ``"remount"``, ``"error"``, or ``"none"``
|
|
479
|
+
(nothing was reloaded).
|
|
480
|
+
"""
|
|
481
|
+
from ..hot_reload import ModuleReloader
|
|
482
|
+
|
|
483
|
+
requested = list(changed_modules or [])
|
|
484
|
+
targets = ModuleReloader.expand_reload_targets(requested, self.component_path)
|
|
485
|
+
reloaded = ModuleReloader.reload_modules(targets)
|
|
486
|
+
if not reloaded:
|
|
487
|
+
log_pn(f"reload: no modules could be reloaded from {targets!r}")
|
|
488
|
+
return "none"
|
|
489
|
+
return self.refresh(reloaded)
|
|
490
|
+
|
|
491
|
+
def refresh(self, reloaded_modules: Sequence[str], *, force_remount: bool = False) -> str:
|
|
492
|
+
"""Refresh the mounted tree against already-reloaded modules.
|
|
493
|
+
|
|
494
|
+
Tries Fast Refresh first (swap component functions in place so
|
|
495
|
+
hook state survives) and falls back to a full remount.
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
``"fast_refresh"``, ``"remount"``, or ``"error"`` (the error
|
|
499
|
+
is shown in the RedBox in dev mode, re-raised otherwise).
|
|
500
|
+
"""
|
|
501
|
+
reloaded = list(reloaded_modules)
|
|
502
|
+
try:
|
|
503
|
+
self.component = import_component(self.component_path)
|
|
504
|
+
except Exception as exc:
|
|
505
|
+
if not diagnostics.is_dev():
|
|
506
|
+
raise
|
|
507
|
+
self.show_redbox(exc, phase="hot reload import")
|
|
508
|
+
return "error"
|
|
509
|
+
if self.reconciler is None:
|
|
510
|
+
return "none"
|
|
511
|
+
self.clear_redbox()
|
|
512
|
+
if not force_remount and self._try_fast_refresh(reloaded):
|
|
513
|
+
return "fast_refresh"
|
|
514
|
+
try:
|
|
515
|
+
self._full_remount(reloaded)
|
|
516
|
+
except Exception as exc:
|
|
517
|
+
if not diagnostics.is_dev():
|
|
518
|
+
raise
|
|
519
|
+
self.show_redbox(exc, phase="hot reload")
|
|
520
|
+
return "error"
|
|
521
|
+
return "remount"
|
|
522
|
+
|
|
523
|
+
def _try_fast_refresh(self, reloaded_modules: Sequence[str]) -> bool:
|
|
524
|
+
from ..hot_reload import ModuleReloader
|
|
525
|
+
|
|
526
|
+
reconciler = self.reconciler
|
|
527
|
+
if reconciler is None or reconciler.root is None:
|
|
528
|
+
return False
|
|
529
|
+
if not ModuleReloader.refresh_in_place(reconciler, reloaded_modules):
|
|
530
|
+
return False
|
|
531
|
+
self._is_rendering = True
|
|
532
|
+
try:
|
|
533
|
+
new_root = reconciler.reconcile(self._root_element())
|
|
534
|
+
if new_root is not self.root_native_view:
|
|
535
|
+
self._detach_root(self.root_native_view)
|
|
536
|
+
self.root_native_view = new_root
|
|
537
|
+
self._attach_root(new_root)
|
|
538
|
+
except Exception as exc:
|
|
539
|
+
log_pn(f"fast refresh: render failed after swap: {exc!r}; falling back to remount")
|
|
540
|
+
return False
|
|
541
|
+
finally:
|
|
542
|
+
self._is_rendering = False
|
|
543
|
+
self._drain_renders()
|
|
544
|
+
return True
|
|
545
|
+
|
|
546
|
+
def _full_remount(self, reloaded_modules: Sequence[str]) -> None:
|
|
547
|
+
old_reconciler, old_root = self.reconciler, self.root_native_view
|
|
548
|
+
new_reconciler = self._new_reconciler()
|
|
549
|
+
self.reconciler = new_reconciler
|
|
550
|
+
self._is_rendering = True
|
|
551
|
+
try:
|
|
552
|
+
new_root = new_reconciler.mount(self._root_element())
|
|
553
|
+
except Exception:
|
|
554
|
+
self.reconciler = old_reconciler
|
|
555
|
+
raise
|
|
556
|
+
finally:
|
|
557
|
+
self._is_rendering = False
|
|
558
|
+
if old_reconciler is not None:
|
|
559
|
+
old_reconciler.unmount()
|
|
560
|
+
if old_root is not None:
|
|
561
|
+
self._detach_root(old_root)
|
|
562
|
+
self.root_native_view = new_root
|
|
563
|
+
self._attach_root(new_root)
|
|
564
|
+
self._drain_renders()
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _redbox_element(exc: BaseException, phase: str, on_dismiss: Callable[[], None]) -> Element:
|
|
568
|
+
from ..components import Button, Column, ScrollView, Text
|
|
569
|
+
|
|
570
|
+
trace = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
|
571
|
+
message = str(exc) or "(no message)"
|
|
572
|
+
return Column(
|
|
573
|
+
Column(
|
|
574
|
+
Text(f"{type(exc).__name__} in {phase}", style={"color": "#FFD3DA", "font_size": 13, "bold": True}),
|
|
575
|
+
Text(message, style={"color": "#FFFFFF", "font_size": 17, "bold": True}),
|
|
576
|
+
style={"background_color": "#C4283C", "padding": 16, "padding_top": 56, "spacing": 6},
|
|
577
|
+
),
|
|
578
|
+
ScrollView(
|
|
579
|
+
Text(trace, style={"color": "#FF9AA8", "font_size": 12}),
|
|
580
|
+
style={"flex": 1, "padding": 12},
|
|
581
|
+
),
|
|
582
|
+
Column(
|
|
583
|
+
Button("Dismiss", on_press=on_dismiss, style={"color": "#FFFFFF"}),
|
|
584
|
+
Text(
|
|
585
|
+
"Fix the error and save to reload.", style={"color": "#8E8E93", "font_size": 12, "text_align": "center"}
|
|
586
|
+
),
|
|
587
|
+
style={"padding": 12, "padding_bottom": 32, "spacing": 4},
|
|
588
|
+
),
|
|
589
|
+
style={"flex": 1, "background_color": "#1C1C1E"},
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def flush_hosts(hosts: Sequence[ScreenHost]) -> None:
|
|
594
|
+
"""Run deferred renders for ``hosts`` (platform UI-thread drains call this)."""
|
|
595
|
+
for host in hosts:
|
|
596
|
+
host.flush_scheduled_render()
|