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,540 @@
|
|
|
1
|
+
"""The [`Navigation`][pythonnative.Navigation] handle and the navigator core behind it.
|
|
2
|
+
|
|
3
|
+
Every screen rendered by a navigator receives a ``Navigation`` object
|
|
4
|
+
through [`use_navigation`][pythonnative.use_navigation]. The handle is
|
|
5
|
+
scoped to the screen's route (so ``add_listener("focus", ...)`` fires
|
|
6
|
+
for *that* screen) and forwards every action to the
|
|
7
|
+
[`NavigatorCore`][pythonnative.navigation.handle.NavigatorCore] that
|
|
8
|
+
owns the navigator's state.
|
|
9
|
+
|
|
10
|
+
Actions the core can't satisfy (an unknown route, popping past the
|
|
11
|
+
first screen) bubble to the parent navigator, so a stack nested in a
|
|
12
|
+
tab still pops correctly and ``navigate("Settings")`` from deep inside
|
|
13
|
+
one tab can switch to another.
|
|
14
|
+
|
|
15
|
+
When the core belongs to the **root stack of a native host** it
|
|
16
|
+
mutates its own state for pushes and pops: it asks the host to push or
|
|
17
|
+
pop a real native screen carrying the serialized next state, and the
|
|
18
|
+
new screen's navigator boots from that state.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Any, Callable, Dict, List, Literal, Mapping, Optional, Protocol, Sequence, Tuple, Union
|
|
24
|
+
|
|
25
|
+
from .. import diagnostics
|
|
26
|
+
from ..hooks import Context, create_context
|
|
27
|
+
from .screen import ScreenDef
|
|
28
|
+
from .state import NavigationState, Route
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"DrawerNavigation",
|
|
32
|
+
"FocusContext",
|
|
33
|
+
"HostNavigator",
|
|
34
|
+
"Navigation",
|
|
35
|
+
"NavigationContext",
|
|
36
|
+
"NavigationEvent",
|
|
37
|
+
"NavigatorCore",
|
|
38
|
+
"TabNavigation",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
NavigatorKind = Literal["stack", "tab", "drawer"]
|
|
42
|
+
EventName = Literal["focus", "blur", "before_remove", "state"]
|
|
43
|
+
Listener = Callable[["NavigationEvent"], None]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class HostNavigator(Protocol):
|
|
47
|
+
"""What a root stack needs from the native screen host.
|
|
48
|
+
|
|
49
|
+
Hosts and FakeHost publish application focus and cached navigation state.
|
|
50
|
+
Screen presentation belongs to the logical tree and its native containers.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
is_focused: bool
|
|
54
|
+
|
|
55
|
+
def initial_navigation_state(self) -> Optional[Dict[str, Any]]:
|
|
56
|
+
"""The serialized state this screen was pushed with, if any."""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def set_screen_options(self, options: Dict[str, Any]) -> None:
|
|
60
|
+
"""Apply header options (``title`` and friends) to the native bar."""
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
def add_focus_listener(self, callback: Callable[[bool], None]) -> Callable[[], None]:
|
|
64
|
+
"""Subscribe to the host covering / revealing this screen; returns an unsubscribe."""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class NavigationEvent:
|
|
69
|
+
"""Payload delivered to ``add_listener`` callbacks.
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
type: ``"focus"``, ``"blur"``, ``"before_remove"``, or ``"state"``.
|
|
73
|
+
route: The route the event concerns.
|
|
74
|
+
data: Extra event data (``before_remove`` carries the
|
|
75
|
+
``action`` that caused it).
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
__slots__ = ("type", "route", "data", "default_prevented")
|
|
79
|
+
|
|
80
|
+
def __init__(self, type_: str, route: Route, data: Optional[Mapping[str, Any]] = None) -> None:
|
|
81
|
+
self.type = type_
|
|
82
|
+
self.route = route
|
|
83
|
+
self.data: Dict[str, Any] = dict(data or {})
|
|
84
|
+
self.default_prevented = False
|
|
85
|
+
|
|
86
|
+
def prevent_default(self) -> None:
|
|
87
|
+
"""Cancel the action (only meaningful for ``before_remove``)."""
|
|
88
|
+
self.default_prevented = True
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return f"NavigationEvent({self.type!r}, {self.route.name!r})"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class NavigatorCore:
|
|
95
|
+
"""State machine shared by every ``Navigation`` handle a navigator hands out.
|
|
96
|
+
|
|
97
|
+
Owned by the navigator component: created once per mount, updated
|
|
98
|
+
every render with the latest state and setter (see ``update``).
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
kind: NavigatorKind,
|
|
104
|
+
screens: Mapping[str, ScreenDef],
|
|
105
|
+
state: NavigationState,
|
|
106
|
+
set_state: Callable[[Any], None],
|
|
107
|
+
parent: Optional["Navigation"] = None,
|
|
108
|
+
host: Optional[HostNavigator] = None,
|
|
109
|
+
request_render: Optional[Callable[[], None]] = None,
|
|
110
|
+
) -> None:
|
|
111
|
+
self.kind = kind
|
|
112
|
+
self.screens: Dict[str, ScreenDef] = dict(screens)
|
|
113
|
+
self.state = state
|
|
114
|
+
self._set_state = set_state
|
|
115
|
+
self.parent = parent
|
|
116
|
+
self.host = host
|
|
117
|
+
self._request_render = request_render
|
|
118
|
+
self._listeners: Dict[Tuple[str, str], List[Listener]] = {}
|
|
119
|
+
# ``set_options`` results, keyed by route key.
|
|
120
|
+
self.runtime_options: Dict[str, Dict[str, Any]] = {}
|
|
121
|
+
self._handles: Dict[str, Navigation] = {}
|
|
122
|
+
self.drawer_open = False
|
|
123
|
+
self._set_drawer_open: Optional[Callable[[bool], None]] = None
|
|
124
|
+
self.on_state_change: Optional[Callable[[NavigationState], None]] = None
|
|
125
|
+
|
|
126
|
+
# ------------------------------------------------------------------
|
|
127
|
+
# Wiring from the owning component
|
|
128
|
+
# ------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
def update(
|
|
131
|
+
self,
|
|
132
|
+
screens: Mapping[str, ScreenDef],
|
|
133
|
+
state: NavigationState,
|
|
134
|
+
set_state: Callable[[Any], None],
|
|
135
|
+
parent: Optional["Navigation"],
|
|
136
|
+
host: Optional[HostNavigator],
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Sync the core with the owning component's latest render.
|
|
139
|
+
|
|
140
|
+
Handles and ``set_options`` overrides for routes no longer in ``state`` are dropped.
|
|
141
|
+
"""
|
|
142
|
+
self.screens = dict(screens)
|
|
143
|
+
self.state = state
|
|
144
|
+
self._set_state = set_state
|
|
145
|
+
self.parent = parent
|
|
146
|
+
self.host = host
|
|
147
|
+
live = {r.key for r in state.routes}
|
|
148
|
+
for key in list(self._handles):
|
|
149
|
+
if key not in live:
|
|
150
|
+
del self._handles[key]
|
|
151
|
+
for key in list(self.runtime_options):
|
|
152
|
+
if key not in live:
|
|
153
|
+
del self.runtime_options[key]
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def is_native_root(self) -> bool:
|
|
157
|
+
"""Whether this core drives a native screen stack through the host."""
|
|
158
|
+
return self.kind == "stack" and self.host is not None and self.parent is None
|
|
159
|
+
|
|
160
|
+
def handle_for(self, route: Route) -> "Navigation":
|
|
161
|
+
"""The (cached) handle scoped to ``route``."""
|
|
162
|
+
handle = self._handles.get(route.key)
|
|
163
|
+
if handle is None:
|
|
164
|
+
cls = {"tab": TabNavigation, "drawer": DrawerNavigation}.get(self.kind, Navigation)
|
|
165
|
+
handle = cls(self, route.key)
|
|
166
|
+
self._handles[route.key] = handle
|
|
167
|
+
return handle
|
|
168
|
+
|
|
169
|
+
def route_by_key(self, key: str) -> Route:
|
|
170
|
+
"""Return the route with ``key``, falling back to the active route once it has left the state."""
|
|
171
|
+
for route in self.state.routes:
|
|
172
|
+
if route.key == key:
|
|
173
|
+
return route
|
|
174
|
+
return self.state.current
|
|
175
|
+
|
|
176
|
+
def options_for(self, route: Route) -> Dict[str, Any]:
|
|
177
|
+
"""Static screen options merged with any ``set_options`` overrides."""
|
|
178
|
+
screen = self.screens.get(route.name)
|
|
179
|
+
options = screen.resolve_options(route) if screen is not None else {}
|
|
180
|
+
options.update(self.runtime_options.get(route.key, {}))
|
|
181
|
+
return options
|
|
182
|
+
|
|
183
|
+
# ------------------------------------------------------------------
|
|
184
|
+
# Events
|
|
185
|
+
# ------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def add_listener(self, route_key: str, event: str, listener: Listener) -> Callable[[], None]:
|
|
188
|
+
"""Subscribe ``listener`` to ``event`` for the route with ``route_key``; returns an unsubscribe callable."""
|
|
189
|
+
bucket = self._listeners.setdefault((route_key, event), [])
|
|
190
|
+
bucket.append(listener)
|
|
191
|
+
|
|
192
|
+
def remove() -> None:
|
|
193
|
+
try:
|
|
194
|
+
bucket.remove(listener)
|
|
195
|
+
except ValueError:
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
return remove
|
|
199
|
+
|
|
200
|
+
def emit(self, route: Route, event: str, data: Optional[Mapping[str, Any]] = None) -> NavigationEvent:
|
|
201
|
+
"""Deliver ``event`` to the listeners registered for ``route`` and return the event.
|
|
202
|
+
|
|
203
|
+
Check ``default_prevented`` on the result to see whether a ``before_remove`` listener cancelled the action.
|
|
204
|
+
"""
|
|
205
|
+
evt = NavigationEvent(event, route, data)
|
|
206
|
+
for listener in list(self._listeners.get((route.key, event), ())):
|
|
207
|
+
try:
|
|
208
|
+
listener(evt)
|
|
209
|
+
except Exception as exc:
|
|
210
|
+
if not diagnostics.report_error(exc, phase=f"navigation {event} listener"):
|
|
211
|
+
raise
|
|
212
|
+
return evt
|
|
213
|
+
|
|
214
|
+
# ------------------------------------------------------------------
|
|
215
|
+
# State transitions
|
|
216
|
+
# ------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
def _commit(self, new_state: NavigationState) -> None:
|
|
219
|
+
if new_state == self.state:
|
|
220
|
+
return
|
|
221
|
+
self._set_state(new_state)
|
|
222
|
+
if self.on_state_change is not None:
|
|
223
|
+
self.on_state_change(new_state)
|
|
224
|
+
|
|
225
|
+
def _validate(self, name: str) -> bool:
|
|
226
|
+
return name in self.screens
|
|
227
|
+
|
|
228
|
+
def _with_initial_params(self, name: str, params: Mapping[str, Any]) -> Dict[str, Any]:
|
|
229
|
+
screen = self.screens.get(name)
|
|
230
|
+
base = dict(screen.initial_params) if screen is not None else {}
|
|
231
|
+
base.update(params)
|
|
232
|
+
return base
|
|
233
|
+
|
|
234
|
+
def navigate(self, name: str, params: Mapping[str, Any], nested: Optional[NavigationState] = None) -> None:
|
|
235
|
+
"""Go to the screen ``name``: stacks pop back to it or push it; tabs and drawers jump to it.
|
|
236
|
+
|
|
237
|
+
Unknown routes bubble to the parent navigator. On a native root stack, popping back is delegated to the host.
|
|
238
|
+
"""
|
|
239
|
+
if not self._validate(name):
|
|
240
|
+
if self.parent is not None:
|
|
241
|
+
self.parent._core.navigate(name, params, nested)
|
|
242
|
+
return
|
|
243
|
+
raise ValueError(f"Unknown route {name!r}. Known routes: {list(self.screens)}")
|
|
244
|
+
if self.kind == "stack":
|
|
245
|
+
existing = self.state.find(name)
|
|
246
|
+
if existing is None:
|
|
247
|
+
self.push(name, params, nested)
|
|
248
|
+
return
|
|
249
|
+
if existing == self.state.index:
|
|
250
|
+
new_state = self.state.set_params(params) if params else self.state
|
|
251
|
+
if nested is not None:
|
|
252
|
+
routes = list(new_state.routes)
|
|
253
|
+
routes[new_state.index] = routes[new_state.index].with_state(nested)
|
|
254
|
+
new_state = NavigationState(routes, new_state.index)
|
|
255
|
+
if new_state is not self.state:
|
|
256
|
+
self._commit(new_state)
|
|
257
|
+
return
|
|
258
|
+
new_state = self.state.pop_to(name, params, nested)
|
|
259
|
+
self._commit(new_state)
|
|
260
|
+
return
|
|
261
|
+
self._commit(self.state.jump_to(name, params, nested))
|
|
262
|
+
|
|
263
|
+
def push(self, name: str, params: Mapping[str, Any], nested: Optional[NavigationState] = None) -> None:
|
|
264
|
+
"""Push a new instance of ``name`` with its ``initial_params`` merged under ``params``.
|
|
265
|
+
|
|
266
|
+
Non-stack navigators fall back to ``navigate``; unknown routes bubble to the parent navigator.
|
|
267
|
+
"""
|
|
268
|
+
if self.kind != "stack":
|
|
269
|
+
self.navigate(name, params, nested)
|
|
270
|
+
return
|
|
271
|
+
if not self._validate(name):
|
|
272
|
+
if self.parent is not None:
|
|
273
|
+
self.parent._core.push(name, params, nested)
|
|
274
|
+
return
|
|
275
|
+
raise ValueError(f"Unknown route {name!r}. Known routes: {list(self.screens)}")
|
|
276
|
+
new_state = self.state.push(name, self._with_initial_params(name, params), nested)
|
|
277
|
+
self._commit(new_state)
|
|
278
|
+
|
|
279
|
+
def replace(self, name: str, params: Mapping[str, Any], nested: Optional[NavigationState] = None) -> None:
|
|
280
|
+
"""Swap the active screen for a fresh ``name`` (stacks); tabs and drawers fall back to ``navigate``."""
|
|
281
|
+
if not self._validate(name):
|
|
282
|
+
if self.parent is not None:
|
|
283
|
+
self.parent._core.replace(name, params, nested)
|
|
284
|
+
return
|
|
285
|
+
raise ValueError(f"Unknown route {name!r}. Known routes: {list(self.screens)}")
|
|
286
|
+
if self.kind != "stack":
|
|
287
|
+
self.navigate(name, params, nested)
|
|
288
|
+
return
|
|
289
|
+
new_state = self.state.replace(name, self._with_initial_params(name, params), nested)
|
|
290
|
+
self._commit(new_state)
|
|
291
|
+
|
|
292
|
+
def pop(self, count: int = 1, *, source: str = "pop") -> bool:
|
|
293
|
+
"""Pop ``count`` screens. Returns whether anything was popped here or by a parent."""
|
|
294
|
+
if self.kind != "stack" or len(self.state) <= 1:
|
|
295
|
+
if self.parent is not None:
|
|
296
|
+
return self.parent.pop(count)
|
|
297
|
+
return False
|
|
298
|
+
count = max(1, min(count, len(self.state) - 1))
|
|
299
|
+
for route in reversed(self.state.routes[len(self.state) - count :]):
|
|
300
|
+
evt = self.emit(route, "before_remove", {"action": source})
|
|
301
|
+
if evt.default_prevented:
|
|
302
|
+
return True
|
|
303
|
+
self._commit(self.state.pop(count))
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
def pop_to_top(self) -> None:
|
|
307
|
+
"""Pop every screen above the first one (no-op when only one screen is present)."""
|
|
308
|
+
if len(self.state) > 1:
|
|
309
|
+
self.pop(len(self.state) - 1, source="pop_to_top")
|
|
310
|
+
|
|
311
|
+
def reset(self, routes: Sequence[Route], index: Optional[int] = None) -> None:
|
|
312
|
+
"""Replace the whole history with ``routes``, activating ``index`` (the last route by default).
|
|
313
|
+
|
|
314
|
+
Raises ``ValueError`` if any route name is unknown to this navigator.
|
|
315
|
+
"""
|
|
316
|
+
for route in routes:
|
|
317
|
+
if not self._validate(route.name):
|
|
318
|
+
raise ValueError(f"Unknown route {route.name!r}. Known routes: {list(self.screens)}")
|
|
319
|
+
new_state = NavigationState(routes, index)
|
|
320
|
+
self._commit(new_state)
|
|
321
|
+
|
|
322
|
+
def set_params(self, route_key: str, params: Mapping[str, Any]) -> None:
|
|
323
|
+
"""Merge ``params`` into the route with ``route_key`` and commit the new state."""
|
|
324
|
+
routes = list(self.state.routes)
|
|
325
|
+
for i, route in enumerate(routes):
|
|
326
|
+
if route.key == route_key:
|
|
327
|
+
routes[i] = route.with_params(params)
|
|
328
|
+
self._commit(NavigationState(routes, self.state.index))
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
def set_options(self, route_key: str, options: Mapping[str, Any]) -> None:
|
|
332
|
+
"""Merge runtime ``options`` for the route with ``route_key`` and request a render if anything changed."""
|
|
333
|
+
current = self.runtime_options.setdefault(route_key, {})
|
|
334
|
+
if all(current.get(k) == v for k, v in options.items()) and all(k in current for k in options):
|
|
335
|
+
return
|
|
336
|
+
current.update(options)
|
|
337
|
+
if self._request_render is not None:
|
|
338
|
+
self._request_render()
|
|
339
|
+
|
|
340
|
+
def set_drawer_open(self, open_: bool) -> None:
|
|
341
|
+
"""Open or close the drawer (no-op unless a drawer navigator owns this core)."""
|
|
342
|
+
if self._set_drawer_open is not None:
|
|
343
|
+
self._set_drawer_open(bool(open_))
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class Navigation:
|
|
347
|
+
"""Imperative navigation API for one screen.
|
|
348
|
+
|
|
349
|
+
Obtained with [`use_navigation`][pythonnative.use_navigation]. Every
|
|
350
|
+
method that changes screens accepts the destination route name
|
|
351
|
+
followed by params as keyword arguments:
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
nav.navigate("Detail", id=42)
|
|
355
|
+
nav.push("Detail", id=43)
|
|
356
|
+
nav.replace("Login")
|
|
357
|
+
nav.pop()
|
|
358
|
+
nav.pop_to_top()
|
|
359
|
+
nav.set_params(id=44)
|
|
360
|
+
nav.set_options(title="Edited")
|
|
361
|
+
unsubscribe = nav.add_listener("focus", lambda e: print("focused", e.route.name))
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Unknown routes bubble to the parent navigator, so screens can
|
|
365
|
+
navigate across nested navigators without knowing the tree shape.
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
__slots__ = ("_core", "_route_key")
|
|
369
|
+
|
|
370
|
+
def __init__(self, core: NavigatorCore, route_key: str) -> None:
|
|
371
|
+
self._core = core
|
|
372
|
+
self._route_key = route_key
|
|
373
|
+
|
|
374
|
+
# ------------------------------------------------------------------
|
|
375
|
+
# Introspection
|
|
376
|
+
# ------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def route(self) -> Route:
|
|
380
|
+
"""The route this handle belongs to."""
|
|
381
|
+
return self._core.route_by_key(self._route_key)
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def kind(self) -> NavigatorKind:
|
|
385
|
+
"""``"stack"``, ``"tab"``, or ``"drawer"``."""
|
|
386
|
+
return self._core.kind
|
|
387
|
+
|
|
388
|
+
def get_params(self) -> Dict[str, Any]:
|
|
389
|
+
"""Params of this handle's route."""
|
|
390
|
+
return dict(self.route.params)
|
|
391
|
+
|
|
392
|
+
def get_state(self) -> NavigationState:
|
|
393
|
+
"""The owning navigator's current state."""
|
|
394
|
+
return self._core.state
|
|
395
|
+
|
|
396
|
+
def get_parent(self) -> Optional["Navigation"]:
|
|
397
|
+
"""The handle of the enclosing navigator, or ``None`` at the top."""
|
|
398
|
+
return self._core.parent
|
|
399
|
+
|
|
400
|
+
def get_options(self) -> Dict[str, Any]:
|
|
401
|
+
"""Effective options for this handle's route (static merged with ``set_options``)."""
|
|
402
|
+
return self._core.options_for(self.route)
|
|
403
|
+
|
|
404
|
+
def can_go_back(self) -> bool:
|
|
405
|
+
"""Whether ``pop()`` would do anything (here or in a parent)."""
|
|
406
|
+
if self._core.kind == "stack" and len(self._core.state) > 1:
|
|
407
|
+
return True
|
|
408
|
+
parent = self._core.parent
|
|
409
|
+
return parent.can_go_back() if parent is not None else False
|
|
410
|
+
|
|
411
|
+
def is_focused(self) -> bool:
|
|
412
|
+
"""Whether this handle's route is the navigator's active route."""
|
|
413
|
+
return self._core.state.current.key == self._route_key
|
|
414
|
+
|
|
415
|
+
# ------------------------------------------------------------------
|
|
416
|
+
# Actions
|
|
417
|
+
# ------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
def navigate(self, route: str, /, *, screen: Optional[str] = None, **params: Any) -> None:
|
|
420
|
+
"""Go to ``route``: switch to it if already present, otherwise push it.
|
|
421
|
+
|
|
422
|
+
When ``route`` renders a nested navigator, ``screen`` names the
|
|
423
|
+
screen to show inside it and ``params`` go to that screen:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
nav.navigate("Tabs", screen="Profile", user="ada")
|
|
427
|
+
```
|
|
428
|
+
"""
|
|
429
|
+
self._core.navigate(route, *_split_nested(screen, params))
|
|
430
|
+
|
|
431
|
+
def push(self, route: str, /, *, screen: Optional[str] = None, **params: Any) -> None:
|
|
432
|
+
"""Push a new instance of ``route`` (stacks; tabs fall back to ``navigate``)."""
|
|
433
|
+
self._core.push(route, *_split_nested(screen, params))
|
|
434
|
+
|
|
435
|
+
def replace(self, route: str, /, *, screen: Optional[str] = None, **params: Any) -> None:
|
|
436
|
+
"""Replace the current screen with ``route``."""
|
|
437
|
+
self._core.replace(route, *_split_nested(screen, params))
|
|
438
|
+
|
|
439
|
+
def pop(self, count: int = 1) -> bool:
|
|
440
|
+
"""Pop ``count`` screens off the nearest stack; returns whether anything happened."""
|
|
441
|
+
return self._core.pop(count)
|
|
442
|
+
|
|
443
|
+
def go_back(self) -> bool:
|
|
444
|
+
"""Alias for ``pop()``."""
|
|
445
|
+
return self._core.pop(1, source="go_back")
|
|
446
|
+
|
|
447
|
+
def pop_to_top(self) -> None:
|
|
448
|
+
"""Pop every screen above the first one."""
|
|
449
|
+
self._core.pop_to_top()
|
|
450
|
+
|
|
451
|
+
def reset(self, *routes: Union[str, Route], index: Optional[int] = None, **params: Any) -> None:
|
|
452
|
+
"""Replace the whole history.
|
|
453
|
+
|
|
454
|
+
``nav.reset("Home")`` installs a single route (``params`` apply
|
|
455
|
+
to it); ``nav.reset(Route("A"), Route("B", {...}))`` installs
|
|
456
|
+
several, with ``index`` selecting the active one (last by
|
|
457
|
+
default).
|
|
458
|
+
"""
|
|
459
|
+
if not routes:
|
|
460
|
+
raise TypeError("reset() needs at least one route")
|
|
461
|
+
if len(routes) == 1 and isinstance(routes[0], str):
|
|
462
|
+
self._core.reset([Route(routes[0], params)], index)
|
|
463
|
+
return
|
|
464
|
+
if params:
|
|
465
|
+
raise TypeError("params keywords are only allowed when resetting to a single route name")
|
|
466
|
+
resolved = [Route(r) if isinstance(r, str) else r for r in routes]
|
|
467
|
+
self._core.reset(resolved, index)
|
|
468
|
+
|
|
469
|
+
def set_params(self, **params: Any) -> None:
|
|
470
|
+
"""Merge ``params`` into this handle's route."""
|
|
471
|
+
self._core.set_params(self._route_key, params)
|
|
472
|
+
|
|
473
|
+
def set_options(self, **options: Any) -> None:
|
|
474
|
+
"""Override [`ScreenOptions`][pythonnative.ScreenOptions] for this route at runtime."""
|
|
475
|
+
self._core.set_options(self._route_key, options)
|
|
476
|
+
|
|
477
|
+
def add_listener(self, event: EventName, listener: Listener) -> Callable[[], None]:
|
|
478
|
+
"""Subscribe to ``"focus"``, ``"blur"``, ``"before_remove"``, or ``"state"`` for this route.
|
|
479
|
+
|
|
480
|
+
Returns an unsubscribe callable. ``before_remove`` listeners may
|
|
481
|
+
call ``event.prevent_default()`` to keep the screen (useful for
|
|
482
|
+
unsaved-changes prompts). Native back gestures on iOS can't be
|
|
483
|
+
intercepted this way; use ``gesture_enabled=False`` to disable
|
|
484
|
+
them for such screens.
|
|
485
|
+
"""
|
|
486
|
+
return self._core.add_listener(self._route_key, event, listener)
|
|
487
|
+
|
|
488
|
+
def __repr__(self) -> str:
|
|
489
|
+
return f"<Navigation {self._core.kind} route={self.route.name!r}>"
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def _split_nested(screen: Optional[str], params: Dict[str, Any]) -> Tuple[Dict[str, Any], Optional[NavigationState]]:
|
|
493
|
+
"""Turn ``screen=...`` into a nested seed state (params then belong to the nested screen)."""
|
|
494
|
+
if screen is None:
|
|
495
|
+
return params, None
|
|
496
|
+
return {}, NavigationState([Route(screen, params)])
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class TabNavigation(Navigation):
|
|
500
|
+
"""Handle for screens inside a tab navigator (adds ``jump_to``)."""
|
|
501
|
+
|
|
502
|
+
__slots__ = ()
|
|
503
|
+
|
|
504
|
+
def jump_to(self, route: str, /, **params: Any) -> None:
|
|
505
|
+
"""Switch to the tab named ``route``."""
|
|
506
|
+
self._core.navigate(route, params)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
class DrawerNavigation(Navigation):
|
|
510
|
+
"""Handle for screens inside a drawer navigator (adds drawer controls)."""
|
|
511
|
+
|
|
512
|
+
__slots__ = ()
|
|
513
|
+
|
|
514
|
+
def jump_to(self, route: str, /, **params: Any) -> None:
|
|
515
|
+
"""Switch to the drawer screen named ``route`` and close the drawer."""
|
|
516
|
+
self._core.navigate(route, params)
|
|
517
|
+
self._core.set_drawer_open(False)
|
|
518
|
+
|
|
519
|
+
def open_drawer(self) -> None:
|
|
520
|
+
"""Slide the drawer menu open."""
|
|
521
|
+
self._core.set_drawer_open(True)
|
|
522
|
+
|
|
523
|
+
def close_drawer(self) -> None:
|
|
524
|
+
"""Close the drawer menu."""
|
|
525
|
+
self._core.set_drawer_open(False)
|
|
526
|
+
|
|
527
|
+
def toggle_drawer(self) -> None:
|
|
528
|
+
"""Open the drawer menu if it's closed, otherwise close it."""
|
|
529
|
+
self._core.set_drawer_open(not self._core.drawer_open)
|
|
530
|
+
|
|
531
|
+
def is_drawer_open(self) -> bool:
|
|
532
|
+
"""Return whether the drawer menu is currently open."""
|
|
533
|
+
return self._core.drawer_open
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
NavigationContext: Context[Optional[Navigation]] = create_context(None, name="Navigation")
|
|
537
|
+
"""Provides the [`Navigation`][pythonnative.Navigation] handle for the current screen."""
|
|
538
|
+
|
|
539
|
+
FocusContext: Context[bool] = create_context(True, name="Focus")
|
|
540
|
+
"""Whether the current subtree is the focused screen (see [`use_is_focused`][pythonnative.use_is_focused])."""
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Hooks for reading navigation state from inside a screen."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable, Dict, Optional, Sequence, overload
|
|
6
|
+
|
|
7
|
+
from ..hooks import use_context, use_effect
|
|
8
|
+
from .handle import FocusContext, Navigation, NavigationContext
|
|
9
|
+
from .state import Route, RouteParams
|
|
10
|
+
|
|
11
|
+
__all__ = ["use_focus_effect", "use_is_focused", "use_navigation", "use_route"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def use_navigation() -> Navigation:
|
|
15
|
+
"""Return the [`Navigation`][pythonnative.Navigation] handle for the current screen.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
RuntimeError: When no navigator encloses the calling component.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
@pn.component
|
|
23
|
+
def HomeScreen():
|
|
24
|
+
nav = pn.use_navigation()
|
|
25
|
+
return pn.Button("Open", on_press=lambda: nav.navigate("Detail", id=42))
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
nav = use_context(NavigationContext)
|
|
29
|
+
if nav is None:
|
|
30
|
+
raise RuntimeError(
|
|
31
|
+
"use_navigation() was called outside a navigator. Render the component inside "
|
|
32
|
+
"Stack.Navigator / Tab.Navigator / Drawer.Navigator (see pn.NavigationContainer)."
|
|
33
|
+
)
|
|
34
|
+
return nav
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def use_route() -> Route[Dict[str, Any]]: ...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@overload
|
|
42
|
+
def use_route[P: RouteParams](params_type: type[P], /) -> Route[P]: ...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def use_route(params_type: Optional[type] = None, /) -> Route[Any]:
|
|
46
|
+
"""Return the current screen's [`Route`][pythonnative.navigation.Route].
|
|
47
|
+
|
|
48
|
+
Pass a ``TypedDict`` class as ``params_type`` to get a
|
|
49
|
+
``Route[MyParams]`` whose ``params`` attribute is typed for editors
|
|
50
|
+
and type checkers. At runtime the hook also verifies that every
|
|
51
|
+
*required* key of the ``TypedDict`` is present on the active route,
|
|
52
|
+
so a screen opened with the wrong params fails at its first render
|
|
53
|
+
with a message naming the missing keys, instead of a ``KeyError``
|
|
54
|
+
deep inside the render.
|
|
55
|
+
|
|
56
|
+
Outside any navigator a placeholder route named ``"__root__"`` with
|
|
57
|
+
empty params is returned (and no validation is performed), so
|
|
58
|
+
components can be rendered standalone (previews, tests) without
|
|
59
|
+
special-casing.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
params_type: Optional ``TypedDict`` describing this screen's
|
|
63
|
+
params.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
TypeError: If the active route is missing a required param
|
|
67
|
+
declared by ``params_type``.
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
```python
|
|
71
|
+
class DetailParams(TypedDict):
|
|
72
|
+
id: int
|
|
73
|
+
title: NotRequired[str]
|
|
74
|
+
|
|
75
|
+
@pn.component
|
|
76
|
+
def DetailScreen():
|
|
77
|
+
route = pn.use_route(DetailParams)
|
|
78
|
+
return pn.Text(f"Item {route.params['id']}")
|
|
79
|
+
```
|
|
80
|
+
"""
|
|
81
|
+
nav = use_context(NavigationContext)
|
|
82
|
+
if nav is None:
|
|
83
|
+
return Route("__root__", {}, key="__root__")
|
|
84
|
+
route = nav.route
|
|
85
|
+
if params_type is not None:
|
|
86
|
+
required = getattr(params_type, "__required_keys__", None)
|
|
87
|
+
if required:
|
|
88
|
+
missing = sorted(k for k in required if k not in route.params)
|
|
89
|
+
if missing:
|
|
90
|
+
raise TypeError(
|
|
91
|
+
f"Screen {route.name!r} is missing required params {missing} "
|
|
92
|
+
f"declared by {params_type.__name__}; got {sorted(route.params)}"
|
|
93
|
+
)
|
|
94
|
+
return route
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def use_is_focused() -> bool:
|
|
98
|
+
"""Whether the calling component is on the focused screen.
|
|
99
|
+
|
|
100
|
+
Combines the native host's lifecycle (a screen covered by a pushed
|
|
101
|
+
native screen is not focused) with the in-tree state of declarative
|
|
102
|
+
navigators (inactive tabs are not focused).
|
|
103
|
+
"""
|
|
104
|
+
return bool(use_context(FocusContext))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def use_focus_effect(effect: Callable[[], Any], deps: Optional[Sequence[Any]] = None) -> None:
|
|
108
|
+
"""Run ``effect`` while the screen is focused; its cleanup runs on blur.
|
|
109
|
+
|
|
110
|
+
Like [`use_effect`][pythonnative.use_effect], but the callback runs
|
|
111
|
+
only when [`use_is_focused`][pythonnative.use_is_focused] is
|
|
112
|
+
``True`` and re-runs each time the screen regains focus.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
effect: Zero-arg callable, optionally returning a cleanup.
|
|
116
|
+
deps: Extra dependencies (``None`` re-runs on every focused render).
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
```python
|
|
120
|
+
@pn.component
|
|
121
|
+
def Feed():
|
|
122
|
+
pn.use_focus_effect(lambda: refresh(), [])
|
|
123
|
+
...
|
|
124
|
+
```
|
|
125
|
+
"""
|
|
126
|
+
focused = use_is_focused()
|
|
127
|
+
|
|
128
|
+
def wrapped() -> Any:
|
|
129
|
+
if focused:
|
|
130
|
+
return effect()
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
use_effect(wrapped, None if deps is None else [focused, *deps])
|