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,393 @@
|
|
|
1
|
+
"""The layout phase of a reconciler pass.
|
|
2
|
+
|
|
3
|
+
After native mutations commit, the reconciler builds a
|
|
4
|
+
[`LayoutNode`][pythonnative.layout.LayoutNode] tree mirroring the
|
|
5
|
+
native nodes of the mounted tree, runs the flexbox engine against the
|
|
6
|
+
viewport, and emits a ``SetFrameOp`` for every frame that changed.
|
|
7
|
+
|
|
8
|
+
Two details keep this cheap on every state update:
|
|
9
|
+
|
|
10
|
+
- **Incremental rebuild.** Each native ``VNode`` caches its
|
|
11
|
+
``LayoutNode``; subtrees whose props and child list are unchanged
|
|
12
|
+
reuse the cached node with ``dirty=False`` so the engine can serve
|
|
13
|
+
their sizes from its measurement memo.
|
|
14
|
+
- **Frame diffing.** Frames equal to the last applied frame produce no
|
|
15
|
+
op at all.
|
|
16
|
+
|
|
17
|
+
Detached subtrees (``Modal``, ``Portal``) are excluded from the main
|
|
18
|
+
flow and laid out against the viewport as independent trees, since
|
|
19
|
+
their content lives in a separately presented native container.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
|
|
25
|
+
|
|
26
|
+
from .. import diagnostics
|
|
27
|
+
from ..element import Element
|
|
28
|
+
from ..layout import LAYOUT_STYLE_KEYS, LayoutNode, calculate_layout, extract_layout_style
|
|
29
|
+
from ..mutations import Mutation, SetFrameOp
|
|
30
|
+
from ..profiling import profiled
|
|
31
|
+
from .vnode import DETACHED_TYPES, VNode
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
Frame = Tuple[float, float, float, float]
|
|
37
|
+
|
|
38
|
+
# Native leaves whose size derives from their content; any prop change
|
|
39
|
+
# invalidates their measurement.
|
|
40
|
+
INTRINSIC_TYPES = frozenset(
|
|
41
|
+
{
|
|
42
|
+
"Text",
|
|
43
|
+
"Button",
|
|
44
|
+
"Image",
|
|
45
|
+
"TextInput",
|
|
46
|
+
"Switch",
|
|
47
|
+
"Slider",
|
|
48
|
+
"ProgressBar",
|
|
49
|
+
"ActivityIndicator",
|
|
50
|
+
"TabBar",
|
|
51
|
+
"Picker",
|
|
52
|
+
"Checkbox",
|
|
53
|
+
"SegmentedControl",
|
|
54
|
+
"DatePicker",
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Childless native leaves that get a measure callback. Extends the
|
|
59
|
+
# intrinsic set with ``VirtualList``, whose handlers report "fill the
|
|
60
|
+
# available space" (like a ScrollView clamped to its parent): without
|
|
61
|
+
# the callback an unstyled list would collapse to 0 points and the
|
|
62
|
+
# platform virtualizer would never bind a row.
|
|
63
|
+
MEASURED_LEAF_TYPES = INTRINSIC_TYPES | {"VirtualList"}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def affects_layout(type_name: str, changed: Dict[str, Any]) -> bool:
|
|
67
|
+
"""Whether ``changed`` props can alter a native node's layout."""
|
|
68
|
+
from ..sdk.schema import COMPONENTS
|
|
69
|
+
|
|
70
|
+
schema = COMPONENTS.get(type_name)
|
|
71
|
+
if schema is None or schema.measurement == "intrinsic":
|
|
72
|
+
return True
|
|
73
|
+
if any(schema.props.get(key, {}).get("native", {}).get("invalidates_layout") for key in changed):
|
|
74
|
+
return True
|
|
75
|
+
return any(key in LAYOUT_STYLE_KEYS for key in changed)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class LayoutMixin:
|
|
79
|
+
"""Layout-pass behavior mixed into the reconciler."""
|
|
80
|
+
|
|
81
|
+
# Provided by the concrete reconciler.
|
|
82
|
+
backend: Any
|
|
83
|
+
root: Optional[VNode]
|
|
84
|
+
_ops: List[Mutation]
|
|
85
|
+
|
|
86
|
+
def _init_layout_state(self) -> None:
|
|
87
|
+
self._viewport_size: Tuple[float, float] = (0.0, 0.0)
|
|
88
|
+
self._layout_pass_count = 0
|
|
89
|
+
# ``(tag, frame)`` pairs whose ``on_layout`` should fire after
|
|
90
|
+
# the layout pass commits (frames are only queued on change).
|
|
91
|
+
self._pending_layout_events: List[Tuple[int, Frame]] = []
|
|
92
|
+
|
|
93
|
+
# ------------------------------------------------------------------
|
|
94
|
+
# Public surface
|
|
95
|
+
# ------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def viewport_size(self) -> Tuple[float, float]:
|
|
99
|
+
"""The last viewport size supplied by the host, ``(0, 0)`` before layout is possible."""
|
|
100
|
+
return self._viewport_size
|
|
101
|
+
|
|
102
|
+
def set_viewport_size(self, width: float, height: float) -> None:
|
|
103
|
+
"""Update the viewport size and re-run layout if it changed.
|
|
104
|
+
|
|
105
|
+
Called by the screen host whenever the platform reports a new
|
|
106
|
+
container size. The first call after mount triggers the
|
|
107
|
+
initial layout pass; identical sizes are no-ops.
|
|
108
|
+
"""
|
|
109
|
+
if width <= 0 or height <= 0:
|
|
110
|
+
return
|
|
111
|
+
if self._viewport_size == (width, height):
|
|
112
|
+
return
|
|
113
|
+
self._viewport_size = (width, height)
|
|
114
|
+
if self.root is not None:
|
|
115
|
+
self._run_layout()
|
|
116
|
+
self._flush_ops()
|
|
117
|
+
self._dispatch_layout_events()
|
|
118
|
+
|
|
119
|
+
def compute_layout_for_test(self, viewport_width: float, viewport_height: float) -> Optional[LayoutNode]:
|
|
120
|
+
"""Build and compute a layout tree without touching the backend.
|
|
121
|
+
|
|
122
|
+
Returns the synthetic viewport ``LayoutNode`` with all
|
|
123
|
+
descendants positioned, or ``None`` if nothing is mounted.
|
|
124
|
+
"""
|
|
125
|
+
if self.root is None:
|
|
126
|
+
return None
|
|
127
|
+
roots = self._build_layout_list(self.root)
|
|
128
|
+
if not roots:
|
|
129
|
+
return None
|
|
130
|
+
viewport = LayoutNode(style={"width": viewport_width, "height": viewport_height}, children=list(roots))
|
|
131
|
+
calculate_layout(viewport, viewport_width, viewport_height)
|
|
132
|
+
return viewport
|
|
133
|
+
|
|
134
|
+
# ------------------------------------------------------------------
|
|
135
|
+
# Hooks used by the core
|
|
136
|
+
# ------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
def _flush_ops(self) -> None: ... # pragma: no cover
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def _mark_layout_dirty(node: VNode) -> None:
|
|
142
|
+
node.layout_dirty = True
|
|
143
|
+
node.layout_node = None
|
|
144
|
+
|
|
145
|
+
# ------------------------------------------------------------------
|
|
146
|
+
# The pass
|
|
147
|
+
# ------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
@profiled("layout")
|
|
150
|
+
def _run_layout(self) -> None:
|
|
151
|
+
"""Build/refresh the layout tree, compute frames, and emit changed ones.
|
|
152
|
+
|
|
153
|
+
Wraps the user's native roots in a synthetic outer ``LayoutNode``
|
|
154
|
+
sized to the viewport so the root fills the screen by default.
|
|
155
|
+
Skipped until the host has supplied a viewport size.
|
|
156
|
+
|
|
157
|
+
The first native root's own frame is intentionally left alone:
|
|
158
|
+
its position and size are owned by the screen host (iOS places
|
|
159
|
+
it below the safe-area inset; Android attaches it with
|
|
160
|
+
``MATCH_PARENT``).
|
|
161
|
+
"""
|
|
162
|
+
root = self.root
|
|
163
|
+
if root is None:
|
|
164
|
+
return
|
|
165
|
+
viewport_w, viewport_h = self._viewport_size
|
|
166
|
+
if viewport_w <= 0 or viewport_h <= 0:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
self._layout_pass_count += 1
|
|
170
|
+
if getattr(self.backend, "native_layout", False):
|
|
171
|
+
self.backend.compute_layout([node.tag for node in self._native_roots(root)], viewport_w, viewport_h)
|
|
172
|
+
return
|
|
173
|
+
layout_roots = self._build_layout_list_cached(root)
|
|
174
|
+
if layout_roots:
|
|
175
|
+
viewport = LayoutNode(style={"width": viewport_w, "height": viewport_h}, children=list(layout_roots))
|
|
176
|
+
viewport.dirty = True
|
|
177
|
+
calculate_layout(viewport, viewport_w, viewport_h)
|
|
178
|
+
for i, layout_root in enumerate(layout_roots):
|
|
179
|
+
if i == 0:
|
|
180
|
+
for child in layout_root.children:
|
|
181
|
+
self._collect_frames(child, 0.0, 0.0)
|
|
182
|
+
else:
|
|
183
|
+
self._collect_frames(layout_root, 0.0, 0.0)
|
|
184
|
+
self._layout_detached_subtrees(root, viewport_w, viewport_h)
|
|
185
|
+
self._clear_layout_dirty(root)
|
|
186
|
+
|
|
187
|
+
def _accept_native_layout(self, frames: list[list[float]]) -> None:
|
|
188
|
+
"""Update refs and queue observations only for acknowledged native frames."""
|
|
189
|
+
for tag, x, y, width, height in frames:
|
|
190
|
+
node = self._tag_nodes.get(int(tag))
|
|
191
|
+
if node is None or not node.mounted:
|
|
192
|
+
continue
|
|
193
|
+
frame = (x, y, width, height)
|
|
194
|
+
node.last_frame = frame
|
|
195
|
+
ref = node.element.props.get("ref")
|
|
196
|
+
if ref is not None:
|
|
197
|
+
ref._pn_frame = frame
|
|
198
|
+
if "on_layout" in node.element.props:
|
|
199
|
+
self._pending_layout_events.append((int(tag), frame))
|
|
200
|
+
if not self._rendering:
|
|
201
|
+
self._dispatch_layout_events()
|
|
202
|
+
|
|
203
|
+
def _layout_detached_subtrees(self, node: VNode, viewport_w: float, viewport_h: float) -> None:
|
|
204
|
+
element = node.element
|
|
205
|
+
if isinstance(element.type, str) and element.type in DETACHED_TYPES:
|
|
206
|
+
active = bool(element.props.get("visible")) if element.type == "Modal" else True
|
|
207
|
+
if not active:
|
|
208
|
+
return
|
|
209
|
+
child_layouts: List[LayoutNode] = []
|
|
210
|
+
for child in node.children:
|
|
211
|
+
child_layouts.extend(self._build_layout_list(child))
|
|
212
|
+
if child_layouts:
|
|
213
|
+
viewport = LayoutNode(style={"width": viewport_w, "height": viewport_h}, children=child_layouts)
|
|
214
|
+
calculate_layout(viewport, viewport_w, viewport_h)
|
|
215
|
+
for c in viewport.children:
|
|
216
|
+
self._collect_frames(c, 0.0, 0.0)
|
|
217
|
+
for child in node.children:
|
|
218
|
+
self._layout_detached_subtrees(child, viewport_w, viewport_h)
|
|
219
|
+
return
|
|
220
|
+
for child in node.children:
|
|
221
|
+
self._layout_detached_subtrees(child, viewport_w, viewport_h)
|
|
222
|
+
|
|
223
|
+
def _build_layout_list_cached(self, node: VNode) -> List[LayoutNode]:
|
|
224
|
+
"""Like ``_build_layout_list`` but reuses cached subtrees when clean."""
|
|
225
|
+
element = node.element
|
|
226
|
+
if not node.is_native:
|
|
227
|
+
out: List[LayoutNode] = []
|
|
228
|
+
for child in node.children:
|
|
229
|
+
out.extend(self._build_layout_list_cached(child))
|
|
230
|
+
return out
|
|
231
|
+
if element.type in DETACHED_TYPES:
|
|
232
|
+
return []
|
|
233
|
+
|
|
234
|
+
child_layouts: List[LayoutNode] = []
|
|
235
|
+
for child in node.children:
|
|
236
|
+
child_layouts.extend(self._build_layout_list_cached(child))
|
|
237
|
+
|
|
238
|
+
cached = node.layout_node
|
|
239
|
+
if cached is not None and not node.layout_dirty:
|
|
240
|
+
cached_children = self._direct_child_layouts(cached, element)
|
|
241
|
+
if len(cached_children) == len(child_layouts) and all(
|
|
242
|
+
a is b for a, b in zip(cached_children, child_layouts)
|
|
243
|
+
):
|
|
244
|
+
return [cached]
|
|
245
|
+
|
|
246
|
+
layout = self._new_layout_node(node)
|
|
247
|
+
for child_layout in child_layouts:
|
|
248
|
+
if element.type == "ScrollView":
|
|
249
|
+
child_layout = self._wrap_scroll_axis(child_layout, self._scroll_axis(element))
|
|
250
|
+
child_layout.dirty = True
|
|
251
|
+
layout.children.append(child_layout)
|
|
252
|
+
node.layout_node = layout
|
|
253
|
+
return [layout]
|
|
254
|
+
|
|
255
|
+
def _build_layout_list(self, node: VNode) -> List[LayoutNode]:
|
|
256
|
+
"""Build fresh (uncached) ``LayoutNode`` for ``node`` and its subtree."""
|
|
257
|
+
element = node.element
|
|
258
|
+
if not node.is_native:
|
|
259
|
+
out: List[LayoutNode] = []
|
|
260
|
+
for child in node.children:
|
|
261
|
+
out.extend(self._build_layout_list(child))
|
|
262
|
+
return out
|
|
263
|
+
if element.type in DETACHED_TYPES:
|
|
264
|
+
return []
|
|
265
|
+
layout = self._new_layout_node(node)
|
|
266
|
+
for child in node.children:
|
|
267
|
+
for child_layout in self._build_layout_list(child):
|
|
268
|
+
if element.type == "ScrollView":
|
|
269
|
+
child_layout = self._wrap_scroll_axis(child_layout, self._scroll_axis(element))
|
|
270
|
+
child_layout.dirty = True
|
|
271
|
+
layout.children.append(child_layout)
|
|
272
|
+
return [layout]
|
|
273
|
+
|
|
274
|
+
def _new_layout_node(self, node: VNode) -> LayoutNode:
|
|
275
|
+
element = node.element
|
|
276
|
+
layout = LayoutNode(style=extract_layout_style(element.props), user_data=node)
|
|
277
|
+
layout.dirty = True
|
|
278
|
+
if element.type == "ScrollView":
|
|
279
|
+
# Mark the scroll axis so the engine clamps the container's
|
|
280
|
+
# main-axis size to its parent's available space; otherwise
|
|
281
|
+
# the container grows to fit its content and nothing scrolls.
|
|
282
|
+
layout._pn_scroll_axis = self._scroll_axis(element)
|
|
283
|
+
if not node.children:
|
|
284
|
+
measure = self._make_measure_callback(node)
|
|
285
|
+
if measure is not None:
|
|
286
|
+
layout.measure = measure
|
|
287
|
+
return layout
|
|
288
|
+
|
|
289
|
+
@staticmethod
|
|
290
|
+
def _scroll_axis(element: Element) -> str:
|
|
291
|
+
return "x" if element.props.get("scroll_axis", "vertical") == "horizontal" else "y"
|
|
292
|
+
|
|
293
|
+
@staticmethod
|
|
294
|
+
def _direct_child_layouts(layout: LayoutNode, element: Element) -> List[LayoutNode]:
|
|
295
|
+
"""Return the cached child layout nodes, unwrapping ScrollView wrappers."""
|
|
296
|
+
if element.type == "ScrollView":
|
|
297
|
+
out: List[LayoutNode] = []
|
|
298
|
+
for wrapper in layout.children:
|
|
299
|
+
out.extend(wrapper.children)
|
|
300
|
+
return out
|
|
301
|
+
return list(layout.children)
|
|
302
|
+
|
|
303
|
+
def _clear_layout_dirty(self, node: VNode) -> None:
|
|
304
|
+
node.layout_dirty = False
|
|
305
|
+
layout = node.layout_node
|
|
306
|
+
if layout is not None:
|
|
307
|
+
layout.dirty = False
|
|
308
|
+
for layout_child in layout.children:
|
|
309
|
+
layout_child.dirty = False
|
|
310
|
+
for child in node.children:
|
|
311
|
+
self._clear_layout_dirty(child)
|
|
312
|
+
|
|
313
|
+
@staticmethod
|
|
314
|
+
def _wrap_scroll_axis(child: LayoutNode, axis: str) -> LayoutNode:
|
|
315
|
+
"""Wrap ``child`` so the layout engine treats one axis as unbounded."""
|
|
316
|
+
wrapper = LayoutNode(style={"flex_direction": "column" if axis == "y" else "row"}, user_data=None)
|
|
317
|
+
wrapper.children.append(child)
|
|
318
|
+
return wrapper
|
|
319
|
+
|
|
320
|
+
def _make_measure_callback(self, node: VNode) -> Optional[Callable[[float, float], Tuple[float, float]]]:
|
|
321
|
+
"""Return a measure callback for ``node`` if it has an intrinsic size."""
|
|
322
|
+
if node.tag is None or node.element.type in {
|
|
323
|
+
"View",
|
|
324
|
+
"Row",
|
|
325
|
+
"Column",
|
|
326
|
+
"ScrollView",
|
|
327
|
+
"Modal",
|
|
328
|
+
"Portal",
|
|
329
|
+
"ScreenStack",
|
|
330
|
+
}:
|
|
331
|
+
return None
|
|
332
|
+
backend = self.backend
|
|
333
|
+
tag = node.tag
|
|
334
|
+
|
|
335
|
+
def measure(max_w: float, max_h: float) -> Tuple[float, float]:
|
|
336
|
+
cache = node.measure_cache
|
|
337
|
+
if cache is not None and cache[0] == max_w and cache[1] == max_h:
|
|
338
|
+
return (cache[2], cache[3])
|
|
339
|
+
try:
|
|
340
|
+
w, h = backend.measure_intrinsic(tag, max_w, max_h)
|
|
341
|
+
result = (float(w), float(h))
|
|
342
|
+
node.measure_cache = (max_w, max_h, result[0], result[1])
|
|
343
|
+
return result
|
|
344
|
+
except Exception:
|
|
345
|
+
return (0.0, 0.0)
|
|
346
|
+
|
|
347
|
+
return measure
|
|
348
|
+
|
|
349
|
+
def _collect_frames(self, layout_node: LayoutNode, parent_x: float, parent_y: float) -> None:
|
|
350
|
+
"""Walk a positioned layout tree and emit ``SetFrameOp`` for changed frames.
|
|
351
|
+
|
|
352
|
+
Coordinates accumulate through transparent wrapper nodes (the
|
|
353
|
+
ScrollView axis wrapper) so the native view receives its
|
|
354
|
+
position relative to its true native parent.
|
|
355
|
+
"""
|
|
356
|
+
node: Optional[VNode] = layout_node.user_data
|
|
357
|
+
if node is not None and node.tag is not None:
|
|
358
|
+
frame: Frame = (
|
|
359
|
+
layout_node.x + parent_x,
|
|
360
|
+
layout_node.y + parent_y,
|
|
361
|
+
layout_node.width,
|
|
362
|
+
layout_node.height,
|
|
363
|
+
)
|
|
364
|
+
if node.last_frame != frame:
|
|
365
|
+
node.last_frame = frame
|
|
366
|
+
self._ops.append(SetFrameOp(node.tag, *frame))
|
|
367
|
+
ref = node.element.props.get("ref")
|
|
368
|
+
if ref is not None and hasattr(ref, "current"):
|
|
369
|
+
try:
|
|
370
|
+
ref._pn_frame = frame
|
|
371
|
+
except Exception:
|
|
372
|
+
pass
|
|
373
|
+
if "on_layout" in node.element.props:
|
|
374
|
+
self._pending_layout_events.append((node.tag, frame))
|
|
375
|
+
child_x = child_y = 0.0
|
|
376
|
+
else:
|
|
377
|
+
child_x = layout_node.x + parent_x
|
|
378
|
+
child_y = layout_node.y + parent_y
|
|
379
|
+
for child in layout_node.children:
|
|
380
|
+
self._collect_frames(child, child_x, child_y)
|
|
381
|
+
|
|
382
|
+
def _dispatch_layout_events(self) -> None:
|
|
383
|
+
"""Fire queued ``on_layout`` callbacks after frames were applied."""
|
|
384
|
+
if not self._pending_layout_events:
|
|
385
|
+
return
|
|
386
|
+
from ..events import dispatch_event
|
|
387
|
+
|
|
388
|
+
pending, self._pending_layout_events = self._pending_layout_events, []
|
|
389
|
+
for tag, frame in pending:
|
|
390
|
+
try:
|
|
391
|
+
dispatch_event(tag, "on_layout", {"x": frame[0], "y": frame[1], "width": frame[2], "height": frame[3]})
|
|
392
|
+
except Exception as exc:
|
|
393
|
+
diagnostics.report_error(exc, phase="event")
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Mounted-tree nodes and the pure helpers the reconciler shares.
|
|
2
|
+
|
|
3
|
+
[`VNode`][pythonnative.reconciler.VNode] pairs an
|
|
4
|
+
[`Element`][pythonnative.Element] with the identity of the native view
|
|
5
|
+
it produced (its **tag**) plus the bookkeeping the reconciler needs
|
|
6
|
+
across passes (layout cache, boundary state, hook state).
|
|
7
|
+
|
|
8
|
+
Everything in this module is side-effect free with respect to the
|
|
9
|
+
native layer.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import itertools
|
|
15
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
16
|
+
|
|
17
|
+
from .. import diagnostics
|
|
18
|
+
from ..component import Component
|
|
19
|
+
from ..element import ERROR_BOUNDARY, FRAGMENT, SUSPENSE, Element, type_label
|
|
20
|
+
from ..hooks import Context, HookState
|
|
21
|
+
from ..layout import LayoutNode
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"DETACHED_TYPES",
|
|
25
|
+
"VNode",
|
|
26
|
+
"next_tag",
|
|
27
|
+
"normalize_children",
|
|
28
|
+
"shallow_equal_props",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# Native element types whose subtree is laid out against the viewport
|
|
32
|
+
# in a detached pass instead of participating in the main layout flow.
|
|
33
|
+
# Their handlers host the children in a separately presented native
|
|
34
|
+
# container (a modal, an overlay, a pushed screen).
|
|
35
|
+
DETACHED_TYPES = frozenset({"Modal", "Portal"})
|
|
36
|
+
|
|
37
|
+
# Tags are globally unique so multiple reconcilers (screens, list rows)
|
|
38
|
+
# can share one registry without collisions.
|
|
39
|
+
_tag_counter = itertools.count(1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def next_tag() -> int:
|
|
43
|
+
"""Allocate a fresh, process-unique view tag."""
|
|
44
|
+
return next(_tag_counter)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def shallow_equal_props(old: dict, new: dict) -> bool:
|
|
48
|
+
"""Return whether two prop dicts are equal under shallow comparison.
|
|
49
|
+
|
|
50
|
+
Used by [`memo`][pythonnative.memo] to skip re-rendering when none
|
|
51
|
+
of a component's props changed identity. Callables only count as
|
|
52
|
+
equal if they're the *same object*; fresh closures always invalidate
|
|
53
|
+
the memo (matching React's behavior; pair with
|
|
54
|
+
[`use_callback`][pythonnative.use_callback] when stability matters).
|
|
55
|
+
"""
|
|
56
|
+
if old is new:
|
|
57
|
+
return True
|
|
58
|
+
if old.keys() != new.keys():
|
|
59
|
+
return False
|
|
60
|
+
for key, ov in old.items():
|
|
61
|
+
nv = new[key]
|
|
62
|
+
if ov is nv:
|
|
63
|
+
continue
|
|
64
|
+
if callable(ov) or callable(nv):
|
|
65
|
+
return False
|
|
66
|
+
try:
|
|
67
|
+
if ov != nv:
|
|
68
|
+
return False
|
|
69
|
+
except Exception:
|
|
70
|
+
return False
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def normalize_children(children: Any, owner: str = "") -> List[Element]:
|
|
75
|
+
"""Normalize arbitrary render output into a flat list of Elements.
|
|
76
|
+
|
|
77
|
+
Accepts a single element, ``None``, ``True``/``False`` (both
|
|
78
|
+
skipped, enabling inline conditionals like ``cond and Text(...)``),
|
|
79
|
+
lists/tuples/generators (flattened recursively), and unkeyed
|
|
80
|
+
Fragments (expanded inline so they never touch the native tree).
|
|
81
|
+
Keyed Fragments are preserved so they can participate in keyed
|
|
82
|
+
reconciliation as a unit.
|
|
83
|
+
|
|
84
|
+
Non-Element values other than the above are dropped with a
|
|
85
|
+
dev-mode warning.
|
|
86
|
+
"""
|
|
87
|
+
out: List[Element] = []
|
|
88
|
+
|
|
89
|
+
def add(item: Any) -> None:
|
|
90
|
+
if item is None or item is True or item is False:
|
|
91
|
+
return
|
|
92
|
+
if isinstance(item, Element):
|
|
93
|
+
if item.type is FRAGMENT and item.key is None:
|
|
94
|
+
for sub in item.children:
|
|
95
|
+
add(sub)
|
|
96
|
+
return
|
|
97
|
+
out.append(item)
|
|
98
|
+
return
|
|
99
|
+
if isinstance(item, (str, bytes)):
|
|
100
|
+
diagnostics.warn_once(
|
|
101
|
+
f"Ignoring bare string child {item!r}"
|
|
102
|
+
+ (f" under {owner}" if owner else "")
|
|
103
|
+
+ ". Wrap text in pn.Text(...).",
|
|
104
|
+
key=f"strchild:{owner}",
|
|
105
|
+
)
|
|
106
|
+
return
|
|
107
|
+
try:
|
|
108
|
+
iterator = iter(item)
|
|
109
|
+
except TypeError:
|
|
110
|
+
diagnostics.warn_once(
|
|
111
|
+
f"Ignoring non-Element child {item!r} ({type(item).__name__})"
|
|
112
|
+
+ (f" under {owner}" if owner else "")
|
|
113
|
+
+ ". Children must be Elements, iterables of Elements, or None/False for conditionals.",
|
|
114
|
+
key=f"badchild:{owner}:{type(item).__name__}",
|
|
115
|
+
)
|
|
116
|
+
return
|
|
117
|
+
for sub in iterator:
|
|
118
|
+
add(sub)
|
|
119
|
+
|
|
120
|
+
add(children)
|
|
121
|
+
|
|
122
|
+
if diagnostics.is_dev() and len(out) > 1:
|
|
123
|
+
seen: Set[Any] = set()
|
|
124
|
+
for el in out:
|
|
125
|
+
if el.key is None:
|
|
126
|
+
continue
|
|
127
|
+
if el.key in seen:
|
|
128
|
+
diagnostics.warn_once(
|
|
129
|
+
f"Duplicate key {el.key!r} among children"
|
|
130
|
+
+ (f" of {owner}" if owner else "")
|
|
131
|
+
+ ". Keys must be unique among siblings; duplicates break "
|
|
132
|
+
"keyed reconciliation and can cross-wire component state.",
|
|
133
|
+
key=f"dupkey:{owner}:{el.key!r}",
|
|
134
|
+
)
|
|
135
|
+
seen.add(el.key)
|
|
136
|
+
return out
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class VNode:
|
|
140
|
+
"""A mounted [`Element`][pythonnative.Element] plus its native identity.
|
|
141
|
+
|
|
142
|
+
The reconciler walks parallel trees of ``VNode`` and incoming
|
|
143
|
+
``Element`` to compute the minimal set of native mutations.
|
|
144
|
+
|
|
145
|
+
Attributes:
|
|
146
|
+
element: The ``Element`` last rendered into this slot.
|
|
147
|
+
tag: Integer identity of the underlying native view. Native
|
|
148
|
+
elements own a fresh tag; transparent wrappers (components,
|
|
149
|
+
providers, boundaries, keyed fragments) delegate the tag of
|
|
150
|
+
their first native root. ``None`` when the subtree renders
|
|
151
|
+
no native view.
|
|
152
|
+
native_view: The platform-native view object, resolved from the
|
|
153
|
+
backend after commit. ``None`` for wrappers that rendered
|
|
154
|
+
nothing.
|
|
155
|
+
children: Ordered list of child ``VNode`` instances.
|
|
156
|
+
parent: The owning ``VNode``, or ``None`` for the tree root.
|
|
157
|
+
hook_state: The component's
|
|
158
|
+
[`HookState`][pythonnative.hooks.HookState] when the node
|
|
159
|
+
wraps a component, otherwise ``None``.
|
|
160
|
+
mounted: ``False`` once the node has been destroyed, so stale
|
|
161
|
+
entries in the reconciler's dirty set are skipped.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
__slots__ = (
|
|
165
|
+
"element",
|
|
166
|
+
"tag",
|
|
167
|
+
"native_view",
|
|
168
|
+
"children",
|
|
169
|
+
"parent",
|
|
170
|
+
"hook_state",
|
|
171
|
+
"mounted",
|
|
172
|
+
"rendered",
|
|
173
|
+
"clean_props",
|
|
174
|
+
"measure_cache",
|
|
175
|
+
"last_frame",
|
|
176
|
+
"layout_node",
|
|
177
|
+
"layout_dirty",
|
|
178
|
+
"error",
|
|
179
|
+
"suspense_showing_fallback",
|
|
180
|
+
"suspense_hydration",
|
|
181
|
+
"suspense_waits",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def __init__(self, element: Element, children: Optional[List["VNode"]] = None, tag: Optional[int] = None) -> None:
|
|
185
|
+
self.element = element
|
|
186
|
+
self.tag = tag
|
|
187
|
+
self.native_view: Any = None
|
|
188
|
+
self.children: List[VNode] = children if children is not None else []
|
|
189
|
+
self.parent: Optional[VNode] = None
|
|
190
|
+
self.hook_state: Optional[HookState] = None
|
|
191
|
+
self.mounted: bool = True
|
|
192
|
+
# The normalized element list a component body last produced.
|
|
193
|
+
self.rendered: Optional[List[Element]] = None
|
|
194
|
+
# Native-safe props (events and reconciler-owned keys stripped)
|
|
195
|
+
# from the last commit; the baseline for prop diffing.
|
|
196
|
+
self.clean_props: Dict[str, Any] = {}
|
|
197
|
+
# Cache for the leaf intrinsic-size measure callback:
|
|
198
|
+
# ``(max_w, max_h, width, height)``. Invalidated whenever the
|
|
199
|
+
# node's props change, so unchanged leaves skip native
|
|
200
|
+
# ``measure_intrinsic`` calls entirely.
|
|
201
|
+
self.measure_cache: Optional[Tuple[float, float, float, float]] = None
|
|
202
|
+
# Last frame sent to the native side; unchanged frames are
|
|
203
|
+
# skipped (frame diffing).
|
|
204
|
+
self.last_frame: Optional[Tuple[float, float, float, float]] = None
|
|
205
|
+
# Cached LayoutNode reused across passes while the subtree is
|
|
206
|
+
# clean (see the layout mixin).
|
|
207
|
+
self.layout_node: Optional[LayoutNode] = None
|
|
208
|
+
# True when this node's layout-relevant props or child list
|
|
209
|
+
# changed since the last layout pass.
|
|
210
|
+
self.layout_dirty: bool = True
|
|
211
|
+
# For error-boundary nodes: the caught exception while the
|
|
212
|
+
# fallback is showing, else ``None``.
|
|
213
|
+
self.error: Optional[BaseException] = None
|
|
214
|
+
# For Suspense nodes: whether the fallback is showing, the hook
|
|
215
|
+
# states of suspended descendants preserved for the next retry
|
|
216
|
+
# (keyed by ``(component identity, element key)``), and the ids
|
|
217
|
+
# of waitables already wired to trigger a retry.
|
|
218
|
+
self.suspense_showing_fallback: bool = False
|
|
219
|
+
self.suspense_hydration: Optional[Dict[Tuple[int, Any], List[HookState]]] = None
|
|
220
|
+
self.suspense_waits: Optional[Set[int]] = None
|
|
221
|
+
|
|
222
|
+
# ------------------------------------------------------------------
|
|
223
|
+
# Classification helpers
|
|
224
|
+
# ------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def is_native(self) -> bool:
|
|
228
|
+
"""Whether this node owns a native view."""
|
|
229
|
+
return isinstance(self.element.type, str)
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def is_component(self) -> bool:
|
|
233
|
+
"""Whether this node renders a user component (a ``@component`` function)."""
|
|
234
|
+
return isinstance(self.element.type, Component)
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def is_provider(self) -> bool:
|
|
238
|
+
"""Whether this node is a context provider."""
|
|
239
|
+
return isinstance(self.element.type, Context)
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def is_error_boundary(self) -> bool:
|
|
243
|
+
"""Whether this node is an ``ErrorBoundary``."""
|
|
244
|
+
return self.element.type is ERROR_BOUNDARY
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def is_suspense(self) -> bool:
|
|
248
|
+
"""Whether this node is a ``Suspense`` boundary."""
|
|
249
|
+
return self.element.type is SUSPENSE
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def label(self) -> str:
|
|
253
|
+
"""Human-readable name of the element type (for diagnostics and tree dumps)."""
|
|
254
|
+
return type_label(self.element.type)
|
|
255
|
+
|
|
256
|
+
def depth(self) -> int:
|
|
257
|
+
"""Return the number of ancestors above this node (the root has depth 0)."""
|
|
258
|
+
depth = 0
|
|
259
|
+
node = self.parent
|
|
260
|
+
while node is not None:
|
|
261
|
+
depth += 1
|
|
262
|
+
node = node.parent
|
|
263
|
+
return depth
|
|
264
|
+
|
|
265
|
+
def __repr__(self) -> str:
|
|
266
|
+
return f"VNode({self.label!r}, tag={self.tag}, children={len(self.children)})"
|