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,785 @@
|
|
|
1
|
+
"""Keyed virtualized lists with one logical component tree."""
|
|
2
|
+
|
|
3
|
+
import bisect
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from ..component import component
|
|
7
|
+
from ..element import Element
|
|
8
|
+
from ..hooks import Ref, use_imperative_handle, use_memo, use_ref, use_state
|
|
9
|
+
from ..style import StyleProp, resolve_style
|
|
10
|
+
from .layout import Column, Row, ScrollView, View
|
|
11
|
+
from .text import Text
|
|
12
|
+
|
|
13
|
+
_DEFAULT_ROW_EXTENT = 44.0
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _RowSpec:
|
|
17
|
+
"""One virtualized row: a stable key, a lazy renderer, and an extent hint."""
|
|
18
|
+
|
|
19
|
+
__slots__ = ("key", "make", "extent", "item", "index")
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
key: str,
|
|
24
|
+
make: Callable[[], Element],
|
|
25
|
+
extent: Optional[float],
|
|
26
|
+
item: Any = None,
|
|
27
|
+
index: int = 0,
|
|
28
|
+
) -> None:
|
|
29
|
+
self.key = key
|
|
30
|
+
self.make = make
|
|
31
|
+
self.extent = extent
|
|
32
|
+
self.item = item
|
|
33
|
+
self.index = index
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _dispatch_scroll_command(scroll_ref: Any, name: str, args: Dict[str, Any]) -> Any:
|
|
37
|
+
"""Send an imperative command to the ScrollView under ``scroll_ref``."""
|
|
38
|
+
tag = getattr(scroll_ref, "_pn_tag", None)
|
|
39
|
+
if tag is None:
|
|
40
|
+
return None
|
|
41
|
+
from ..native_views import get_registry
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
return get_registry().command(tag, name, args)
|
|
45
|
+
except Exception:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ListController:
|
|
50
|
+
"""Imperative scroll handle published on a list's ``ref``.
|
|
51
|
+
|
|
52
|
+
[`FlatList`][pythonnative.FlatList] and
|
|
53
|
+
[`SectionList`][pythonnative.SectionList] install a
|
|
54
|
+
``ListController`` on ``ref.current`` (via
|
|
55
|
+
[`use_imperative_handle`][pythonnative.use_imperative_handle])
|
|
56
|
+
after mount and clear it back to ``None`` on unmount.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
```python
|
|
60
|
+
import pythonnative as pn
|
|
61
|
+
|
|
62
|
+
@pn.component
|
|
63
|
+
def Chat(messages):
|
|
64
|
+
list_ref = pn.use_ref()
|
|
65
|
+
pn.use_layout_effect(
|
|
66
|
+
lambda: list_ref.current and list_ref.current.scroll_to_end(animated=False),
|
|
67
|
+
[len(messages)],
|
|
68
|
+
)
|
|
69
|
+
return pn.FlatList(data=messages, render_item=Bubble, ref=list_ref)
|
|
70
|
+
```
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
__slots__ = ("_scroll_to_offset", "_scroll_to_index", "_scroll_to_end")
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
scroll_to_offset: Callable[[float, bool], None],
|
|
78
|
+
scroll_to_index: Callable[[int, bool], None],
|
|
79
|
+
scroll_to_end: Callable[[bool], None],
|
|
80
|
+
) -> None:
|
|
81
|
+
self._scroll_to_offset = scroll_to_offset
|
|
82
|
+
self._scroll_to_index = scroll_to_index
|
|
83
|
+
self._scroll_to_end = scroll_to_end
|
|
84
|
+
|
|
85
|
+
def scroll_to_offset(self, offset: float, animated: bool = True) -> None:
|
|
86
|
+
"""Scroll to an absolute content offset in points."""
|
|
87
|
+
self._scroll_to_offset(offset, animated)
|
|
88
|
+
|
|
89
|
+
def scroll_to_index(self, index: int, animated: bool = True) -> None:
|
|
90
|
+
"""Scroll so the row at ``index`` sits at the top of the viewport."""
|
|
91
|
+
self._scroll_to_index(index, animated)
|
|
92
|
+
|
|
93
|
+
def scroll_to_end(self, animated: bool = True) -> None:
|
|
94
|
+
"""Scroll to the end of the content."""
|
|
95
|
+
self._scroll_to_end(animated)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@component
|
|
99
|
+
def _VirtualizedList(
|
|
100
|
+
rows: Optional[List[_RowSpec]] = None,
|
|
101
|
+
horizontal: bool = False,
|
|
102
|
+
estimated_row_extent: Optional[float] = None,
|
|
103
|
+
overscan_extent: Optional[float] = None,
|
|
104
|
+
initial_window_extent: Optional[float] = None,
|
|
105
|
+
header: Optional[Element] = None,
|
|
106
|
+
footer: Optional[Element] = None,
|
|
107
|
+
empty: Optional[Element] = None,
|
|
108
|
+
refresh_control: Optional[Element] = None,
|
|
109
|
+
on_end_reached: Optional[Callable[[], Any]] = None,
|
|
110
|
+
on_end_reached_threshold: Optional[float] = None,
|
|
111
|
+
on_viewable_items_changed: Optional[Callable[[List[Dict[str, Any]]], None]] = None,
|
|
112
|
+
on_scroll: Optional[Callable[[Any], Any]] = None,
|
|
113
|
+
shows_scroll_indicator: bool = True,
|
|
114
|
+
content_container_style: Optional[Dict[str, Any]] = None,
|
|
115
|
+
list_style: Optional[Dict[str, Any]] = None,
|
|
116
|
+
controller_ref: Optional[Ref] = None,
|
|
117
|
+
) -> Element:
|
|
118
|
+
"""Shared windowing engine behind FlatList and SectionList."""
|
|
119
|
+
rows = rows or []
|
|
120
|
+
n = len(rows)
|
|
121
|
+
horizontal = bool(horizontal)
|
|
122
|
+
estimated: float = float(estimated_row_extent or _DEFAULT_ROW_EXTENT)
|
|
123
|
+
overscan: float = float(overscan_extent or 0.0)
|
|
124
|
+
initial_extent: float = float(initial_window_extent or 800.0)
|
|
125
|
+
|
|
126
|
+
window, set_window = use_state((0, -1))
|
|
127
|
+
measured: Ref[Dict[str, float]] = use_ref({}) # row key -> measured extent (points)
|
|
128
|
+
row_refs: Ref[Dict[str, Ref]] = use_ref({}) # row key -> Ref for live rows
|
|
129
|
+
end_latch = use_ref({"fired_for": -1})
|
|
130
|
+
viewable_ref: Ref[Dict[str, Tuple[str, ...]]] = use_ref({"keys": ()})
|
|
131
|
+
scroll_pos = use_ref({"offset": 0.0})
|
|
132
|
+
sv_ref: Ref = use_ref(None)
|
|
133
|
+
|
|
134
|
+
# ------------------------------------------------------------------
|
|
135
|
+
# Extent model: measured > per-row hint > estimate. ``starts`` are
|
|
136
|
+
# prefix sums; ``starts[n]`` is the total content extent.
|
|
137
|
+
# ------------------------------------------------------------------
|
|
138
|
+
measured_map: Dict[str, float] = measured.current
|
|
139
|
+
starts: List[float] = [0.0] * (n + 1)
|
|
140
|
+
acc = 0.0
|
|
141
|
+
for i, spec in enumerate(rows):
|
|
142
|
+
starts[i] = acc
|
|
143
|
+
extent = measured_map.get(spec.key)
|
|
144
|
+
if extent is None:
|
|
145
|
+
extent = spec.extent if spec.extent is not None else estimated
|
|
146
|
+
acc += max(0.0, float(extent))
|
|
147
|
+
starts[n] = acc
|
|
148
|
+
total_extent = acc
|
|
149
|
+
|
|
150
|
+
def _viewport_extent() -> float:
|
|
151
|
+
frame = sv_ref._pn_frame
|
|
152
|
+
if frame:
|
|
153
|
+
extent = frame[2] if horizontal else frame[3]
|
|
154
|
+
if extent and extent > 0:
|
|
155
|
+
return float(extent)
|
|
156
|
+
return initial_extent
|
|
157
|
+
|
|
158
|
+
def _window_for(offset: float, viewport: float) -> Tuple[int, int]:
|
|
159
|
+
if n == 0:
|
|
160
|
+
return (0, -1)
|
|
161
|
+
pad = overscan if overscan > 0 else viewport
|
|
162
|
+
lo = max(0.0, offset - pad)
|
|
163
|
+
hi = offset + viewport + pad
|
|
164
|
+
first = max(0, bisect.bisect_right(starts, lo, 0, n) - 1)
|
|
165
|
+
last = min(n - 1, bisect.bisect_left(starts, hi, 0, n))
|
|
166
|
+
return (first, last)
|
|
167
|
+
|
|
168
|
+
first, last = window
|
|
169
|
+
if last < 0 or first >= n:
|
|
170
|
+
first, last = _window_for(scroll_pos.current["offset"], _viewport_extent())
|
|
171
|
+
last = min(last, n - 1)
|
|
172
|
+
first = max(0, min(first, max(0, n - 1)))
|
|
173
|
+
|
|
174
|
+
# ------------------------------------------------------------------
|
|
175
|
+
# Scroll handling: sweep measured extents, shift the window, fire
|
|
176
|
+
# end-reached / viewability callbacks. State only changes when the
|
|
177
|
+
# window actually moves, so steady scrolling inside the overscan
|
|
178
|
+
# region costs no re-render.
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
end_threshold = float(on_end_reached_threshold or 0.5)
|
|
181
|
+
on_viewable = on_viewable_items_changed
|
|
182
|
+
user_on_scroll = on_scroll
|
|
183
|
+
|
|
184
|
+
def _sweep_measured() -> None:
|
|
185
|
+
for row_key, row_ref in row_refs.current.items():
|
|
186
|
+
frame = getattr(row_ref, "_pn_frame", None)
|
|
187
|
+
if frame:
|
|
188
|
+
extent = frame[2] if horizontal else frame[3]
|
|
189
|
+
if extent and extent > 0:
|
|
190
|
+
measured_map[row_key] = float(extent)
|
|
191
|
+
|
|
192
|
+
def _handle_scroll(payload: Any) -> None:
|
|
193
|
+
if isinstance(payload, dict):
|
|
194
|
+
offset = float(payload.get("x" if horizontal else "y", 0.0) or 0.0)
|
|
195
|
+
else:
|
|
196
|
+
offset = float(payload or 0.0)
|
|
197
|
+
scroll_pos.current["offset"] = offset
|
|
198
|
+
_sweep_measured()
|
|
199
|
+
viewport = _viewport_extent()
|
|
200
|
+
|
|
201
|
+
new_window = _window_for(offset, viewport)
|
|
202
|
+
if new_window != (first, last):
|
|
203
|
+
set_window(new_window)
|
|
204
|
+
|
|
205
|
+
if on_end_reached is not None and total_extent > 0:
|
|
206
|
+
remaining = total_extent - (offset + viewport)
|
|
207
|
+
if remaining <= end_threshold * viewport:
|
|
208
|
+
if end_latch.current["fired_for"] != n:
|
|
209
|
+
end_latch.current["fired_for"] = n
|
|
210
|
+
on_end_reached()
|
|
211
|
+
elif remaining > end_threshold * viewport + viewport:
|
|
212
|
+
end_latch.current["fired_for"] = -1
|
|
213
|
+
|
|
214
|
+
if on_viewable is not None and n > 0:
|
|
215
|
+
v_first = max(0, bisect.bisect_right(starts, offset, 0, n) - 1)
|
|
216
|
+
v_last = min(n - 1, bisect.bisect_left(starts, offset + viewport, 0, n))
|
|
217
|
+
keys = tuple(rows[i].key for i in range(v_first, v_last + 1))
|
|
218
|
+
if keys != viewable_ref.current["keys"]:
|
|
219
|
+
viewable_ref.current["keys"] = keys
|
|
220
|
+
on_viewable(
|
|
221
|
+
[
|
|
222
|
+
{"index": rows[i].index, "key": rows[i].key, "item": rows[i].item}
|
|
223
|
+
for i in range(v_first, v_last + 1)
|
|
224
|
+
]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if user_on_scroll is not None:
|
|
228
|
+
user_on_scroll(payload)
|
|
229
|
+
|
|
230
|
+
# ------------------------------------------------------------------
|
|
231
|
+
# Imperative controller (scroll_to_index / offset / end) published
|
|
232
|
+
# on the user's ref. Rebuilt every render (deps=None) so the
|
|
233
|
+
# closures see fresh extents.
|
|
234
|
+
# ------------------------------------------------------------------
|
|
235
|
+
def _scroll_to_offset(offset: float, animated: bool = True) -> None:
|
|
236
|
+
axis = "x" if horizontal else "y"
|
|
237
|
+
_dispatch_scroll_command(sv_ref, "scroll_to_offset", {axis: float(offset), "animated": animated})
|
|
238
|
+
|
|
239
|
+
def _scroll_to_index(index: int, animated: bool = True) -> None:
|
|
240
|
+
idx = max(0, min(int(index), n - 1)) if n else 0
|
|
241
|
+
_scroll_to_offset(starts[idx], animated)
|
|
242
|
+
|
|
243
|
+
def _scroll_to_end(animated: bool = True) -> None:
|
|
244
|
+
_scroll_to_offset(max(0.0, total_extent - _viewport_extent()), animated)
|
|
245
|
+
|
|
246
|
+
use_imperative_handle(
|
|
247
|
+
controller_ref,
|
|
248
|
+
lambda: ListController(_scroll_to_offset, _scroll_to_index, _scroll_to_end),
|
|
249
|
+
None,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# ------------------------------------------------------------------
|
|
253
|
+
# Children: header, leading spacer, windowed rows, trailing spacer,
|
|
254
|
+
# footer. Rows keep per-key refs so their measured extents survive
|
|
255
|
+
# recycling.
|
|
256
|
+
# ------------------------------------------------------------------
|
|
257
|
+
spacer_key = "width" if horizontal else "height"
|
|
258
|
+
children: List[Element] = []
|
|
259
|
+
if header is not None:
|
|
260
|
+
children.append(View(header, key="__pn_header__"))
|
|
261
|
+
|
|
262
|
+
if n == 0:
|
|
263
|
+
if empty is not None:
|
|
264
|
+
children.append(View(empty, key="__pn_empty__"))
|
|
265
|
+
else:
|
|
266
|
+
live_refs: Dict[str, Ref] = {}
|
|
267
|
+
lead = starts[first]
|
|
268
|
+
if lead > 0:
|
|
269
|
+
lead_style: Dict[str, Any] = {spacer_key: lead}
|
|
270
|
+
children.append(View(style=lead_style, key="__pn_lead__"))
|
|
271
|
+
for i in range(first, last + 1):
|
|
272
|
+
spec = rows[i]
|
|
273
|
+
row_ref = row_refs.current.get(spec.key) or Ref()
|
|
274
|
+
live_refs[spec.key] = row_ref
|
|
275
|
+
children.append(View(spec.make(), ref=row_ref, key=spec.key))
|
|
276
|
+
row_refs.current = live_refs
|
|
277
|
+
trail = total_extent - starts[last + 1]
|
|
278
|
+
if trail > 0:
|
|
279
|
+
trail_style: Dict[str, Any] = {spacer_key: trail}
|
|
280
|
+
children.append(View(style=trail_style, key="__pn_trail__"))
|
|
281
|
+
|
|
282
|
+
if footer is not None:
|
|
283
|
+
children.append(View(footer, key="__pn_footer__"))
|
|
284
|
+
|
|
285
|
+
wrapper = Row if horizontal else Column
|
|
286
|
+
inner = wrapper(*children, style=content_container_style)
|
|
287
|
+
return ScrollView(
|
|
288
|
+
inner,
|
|
289
|
+
scroll_axis="horizontal" if horizontal else "vertical",
|
|
290
|
+
on_scroll=_handle_scroll,
|
|
291
|
+
refresh_control=refresh_control,
|
|
292
|
+
shows_scroll_indicator=shows_scroll_indicator,
|
|
293
|
+
style=list_style,
|
|
294
|
+
ref=sv_ref,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _native_lists_supported() -> bool:
|
|
299
|
+
"""Whether the natively virtualized list path is available.
|
|
300
|
+
|
|
301
|
+
Android (RecyclerView) and iOS (UICollectionView) have native handlers;
|
|
302
|
+
the browser preview and off-device tests use the Python-windowed
|
|
303
|
+
engine. Patchable in tests to exercise the native routing.
|
|
304
|
+
"""
|
|
305
|
+
from ..native_views import get_registry
|
|
306
|
+
from ..utils import IS_ANDROID, IS_IOS
|
|
307
|
+
|
|
308
|
+
return IS_ANDROID or IS_IOS or getattr(get_registry(), "native_layout", False)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _use_native_lists() -> bool:
|
|
312
|
+
"""Resolve ``_native_lists_supported`` through the package at call time.
|
|
313
|
+
|
|
314
|
+
Looking the function up on ``pythonnative.components`` (rather than
|
|
315
|
+
this module's globals) keeps
|
|
316
|
+
``monkeypatch.setattr(pythonnative.components, "_native_lists_supported", ...)``
|
|
317
|
+
effective now that the implementation lives in a submodule.
|
|
318
|
+
"""
|
|
319
|
+
from . import _native_lists_supported as supported
|
|
320
|
+
|
|
321
|
+
return supported()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
@component
|
|
325
|
+
def _NativeList(
|
|
326
|
+
rows: Optional[List[_RowSpec]] = None,
|
|
327
|
+
on_end_reached: Optional[Callable[[], Any]] = None,
|
|
328
|
+
on_end_reached_threshold: Optional[float] = None,
|
|
329
|
+
on_viewable_items_changed: Optional[Callable[[List[Dict[str, Any]]], None]] = None,
|
|
330
|
+
on_scroll: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
331
|
+
shows_scroll_indicator: Optional[bool] = None,
|
|
332
|
+
list_style: Optional[Dict[str, Any]] = None,
|
|
333
|
+
controller_ref: Optional[Ref] = None,
|
|
334
|
+
horizontal: bool = False,
|
|
335
|
+
estimated_row_extent: float = 44,
|
|
336
|
+
header: Optional[Element] = None,
|
|
337
|
+
footer: Optional[Element] = None,
|
|
338
|
+
empty: Optional[Element] = None,
|
|
339
|
+
refresh_control: Optional[Element] = None,
|
|
340
|
+
content_container_style: Optional[Dict[str, Any]] = None,
|
|
341
|
+
) -> Element:
|
|
342
|
+
"""Prepare a bounded set of logical rows for native recycled containers.
|
|
343
|
+
|
|
344
|
+
Keys own hooks. Native container identities never enter the component
|
|
345
|
+
tree. Every data snapshot has a revision, including same-length edits.
|
|
346
|
+
"""
|
|
347
|
+
source = rows or []
|
|
348
|
+
rows = list(source)
|
|
349
|
+
if not rows and empty is not None:
|
|
350
|
+
rows.append(_RowSpec("__empty__", lambda: empty, None))
|
|
351
|
+
if header is not None:
|
|
352
|
+
rows.insert(0, _RowSpec("__header__", lambda: header, None))
|
|
353
|
+
if footer is not None:
|
|
354
|
+
rows.append(_RowSpec("__footer__", lambda: footer, None))
|
|
355
|
+
keys = [row.key for row in rows]
|
|
356
|
+
if len(set(keys)) != len(keys):
|
|
357
|
+
raise ValueError("List keys must be unique")
|
|
358
|
+
generation = use_ref(0)
|
|
359
|
+
|
|
360
|
+
def next_revision() -> int:
|
|
361
|
+
generation.current += 1
|
|
362
|
+
return generation.current
|
|
363
|
+
|
|
364
|
+
revision = use_memo(next_revision, [source, header, footer, empty])
|
|
365
|
+
center, set_center = use_state(0)
|
|
366
|
+
first, last = max(0, center - 16), min(len(rows), center + 40)
|
|
367
|
+
internal_ref: Ref = use_ref()
|
|
368
|
+
end_revision = use_ref(-1)
|
|
369
|
+
|
|
370
|
+
def bind(info: Dict[str, Any]) -> None:
|
|
371
|
+
if info.get("revision") != revision:
|
|
372
|
+
return
|
|
373
|
+
index = int(info.get("index", -1))
|
|
374
|
+
if not 0 <= index < len(rows) or info.get("key") != keys[index]:
|
|
375
|
+
return
|
|
376
|
+
if index < first + 8 or index >= last - 8:
|
|
377
|
+
set_center(index)
|
|
378
|
+
|
|
379
|
+
def scroll(info: Dict[str, Any]) -> Any:
|
|
380
|
+
if on_end_reached is not None:
|
|
381
|
+
remaining = info.get("range", 0) - info.get("x" if horizontal else "y", 0) - info.get("extent", 0)
|
|
382
|
+
if (
|
|
383
|
+
remaining <= (on_end_reached_threshold or 0.5) * info.get("extent", 0)
|
|
384
|
+
and end_revision.current != revision
|
|
385
|
+
):
|
|
386
|
+
end_revision.current = revision
|
|
387
|
+
from ..runtime import invoke
|
|
388
|
+
|
|
389
|
+
invoke(on_end_reached)
|
|
390
|
+
if on_viewable_items_changed is not None:
|
|
391
|
+
start, end = int(info.get("first", 0)), int(info.get("last", -1))
|
|
392
|
+
from ..runtime import invoke
|
|
393
|
+
|
|
394
|
+
invoke(
|
|
395
|
+
on_viewable_items_changed,
|
|
396
|
+
[
|
|
397
|
+
{"key": rows[i].key, "index": rows[i].index, "item": rows[i].item}
|
|
398
|
+
for i in range(max(0, start), min(len(rows), end + 1))
|
|
399
|
+
],
|
|
400
|
+
)
|
|
401
|
+
return on_scroll(info) if on_scroll is not None else None
|
|
402
|
+
|
|
403
|
+
use_imperative_handle(
|
|
404
|
+
controller_ref,
|
|
405
|
+
lambda: ListController(
|
|
406
|
+
lambda offset, animated: _dispatch_scroll_command(
|
|
407
|
+
internal_ref, "scroll_to_offset", {"x" if horizontal else "y": offset, "animated": animated}
|
|
408
|
+
),
|
|
409
|
+
lambda index, animated: _dispatch_scroll_command(
|
|
410
|
+
internal_ref, "scroll_to_index", {"index": index, "animated": animated}
|
|
411
|
+
),
|
|
412
|
+
lambda animated: _dispatch_scroll_command(internal_ref, "scroll_to_end", {"animated": animated}),
|
|
413
|
+
),
|
|
414
|
+
[horizontal],
|
|
415
|
+
)
|
|
416
|
+
props = {
|
|
417
|
+
"flex_grow": 1,
|
|
418
|
+
**(list_style or {}),
|
|
419
|
+
"keys": keys,
|
|
420
|
+
"revision": revision,
|
|
421
|
+
"count": len(rows),
|
|
422
|
+
"row_heights": [r.extent or estimated_row_extent for r in rows],
|
|
423
|
+
"horizontal": horizontal,
|
|
424
|
+
"on_bind_row": bind,
|
|
425
|
+
"on_scroll": scroll,
|
|
426
|
+
"shows_scroll_indicator": shows_scroll_indicator is not False,
|
|
427
|
+
"ref": internal_ref,
|
|
428
|
+
}
|
|
429
|
+
if refresh_control is not None:
|
|
430
|
+
props["refresh_control"] = dict(refresh_control.props)
|
|
431
|
+
children = []
|
|
432
|
+
for row in rows[first:last]:
|
|
433
|
+
style = dict(content_container_style or {})
|
|
434
|
+
if row.extent is not None:
|
|
435
|
+
style["width" if horizontal else "height"] = row.extent
|
|
436
|
+
if not horizontal:
|
|
437
|
+
style["width"] = "100%"
|
|
438
|
+
child = View(row.make(), style=style, key=row.key)
|
|
439
|
+
child.props["_pn_list_key"] = row.key
|
|
440
|
+
children.append(child)
|
|
441
|
+
return Element("VirtualList", props, children)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def FlatList(
|
|
445
|
+
*,
|
|
446
|
+
data: Optional[List[Any]] = None,
|
|
447
|
+
render_item: Optional[Callable[[Any, int], Element]] = None,
|
|
448
|
+
key_extractor: Optional[Callable[[Any, int], str]] = None,
|
|
449
|
+
item_height: Optional[float] = None,
|
|
450
|
+
get_item_height: Optional[Callable[[Any, int], float]] = None,
|
|
451
|
+
estimated_item_height: Optional[float] = None,
|
|
452
|
+
separator_height: float = 0,
|
|
453
|
+
refresh_control: Optional[Element] = None,
|
|
454
|
+
horizontal: bool = False,
|
|
455
|
+
num_columns: int = 1,
|
|
456
|
+
list_header: Optional[Element] = None,
|
|
457
|
+
list_footer: Optional[Element] = None,
|
|
458
|
+
list_empty: Optional[Element] = None,
|
|
459
|
+
on_end_reached: Optional[Callable[[], Any]] = None,
|
|
460
|
+
on_end_reached_threshold: float = 0.5,
|
|
461
|
+
on_viewable_items_changed: Optional[Callable[[List[Dict[str, Any]]], None]] = None,
|
|
462
|
+
on_scroll: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
463
|
+
shows_scroll_indicator: bool = True,
|
|
464
|
+
content_container_style: StyleProp = None,
|
|
465
|
+
style: StyleProp = None,
|
|
466
|
+
ref: Optional[Ref] = None,
|
|
467
|
+
key: Optional[str] = None,
|
|
468
|
+
) -> Element:
|
|
469
|
+
"""Virtualized scrollable list that renders items from ``data`` lazily.
|
|
470
|
+
|
|
471
|
+
Only the rows inside (and just beyond) the viewport are mounted;
|
|
472
|
+
leading and trailing spacers stand in for everything else, and the
|
|
473
|
+
window shifts as the user scrolls. Rows may have **variable
|
|
474
|
+
heights**: pass ``item_height`` when rows are uniform,
|
|
475
|
+
``get_item_height`` for exact per-item extents, or nothing at all;
|
|
476
|
+
unknown rows start at ``estimated_item_height`` and are corrected
|
|
477
|
+
with their measured extent once they've been on screen.
|
|
478
|
+
|
|
479
|
+
Pass a [`Ref`][pythonnative.Ref] (from
|
|
480
|
+
[`use_ref`][pythonnative.use_ref]) to receive a
|
|
481
|
+
[`ListController`][pythonnative.ListController] on ``ref.current``:
|
|
482
|
+
``ref.current.scroll_to_index(i)``,
|
|
483
|
+
``ref.current.scroll_to_offset(pts)``, and
|
|
484
|
+
``ref.current.scroll_to_end()``.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
data: List of arbitrary item values.
|
|
488
|
+
render_item: ``render_item(item, index) -> Element``. Defaults
|
|
489
|
+
to wrapping each item in a [`Text`][pythonnative.Text].
|
|
490
|
+
key_extractor: Function returning a stable key per item
|
|
491
|
+
(recommended whenever ``data`` can reorder).
|
|
492
|
+
item_height: Uniform row extent in points, when known.
|
|
493
|
+
get_item_height: ``get_item_height(item, index) -> float`` for
|
|
494
|
+
exact variable extents without measurement.
|
|
495
|
+
estimated_item_height: Starting extent estimate for rows whose
|
|
496
|
+
true size isn't known yet (default 44).
|
|
497
|
+
separator_height: Gap below each row, in points.
|
|
498
|
+
refresh_control: Optional [`RefreshControl`][pythonnative.RefreshControl]
|
|
499
|
+
element for pull-to-refresh.
|
|
500
|
+
horizontal: Scroll horizontally (extents become widths).
|
|
501
|
+
num_columns: Render items in a grid of this many columns.
|
|
502
|
+
list_header: Element rendered once before all rows.
|
|
503
|
+
list_footer: Element rendered once after all rows.
|
|
504
|
+
list_empty: Element rendered when ``data`` is empty.
|
|
505
|
+
on_end_reached: Called when the user scrolls within
|
|
506
|
+
``on_end_reached_threshold`` viewports of the end (fires
|
|
507
|
+
once per data length).
|
|
508
|
+
on_end_reached_threshold: Distance from the end, in viewport
|
|
509
|
+
multiples, at which ``on_end_reached`` fires.
|
|
510
|
+
on_viewable_items_changed: Called with a list of
|
|
511
|
+
``{"index", "key", "item"}`` dicts whenever the set of
|
|
512
|
+
visible rows changes.
|
|
513
|
+
on_scroll: Called with the raw scroll payload
|
|
514
|
+
(``{"x": …, "y": …}``).
|
|
515
|
+
shows_scroll_indicator: When ``False``, hides the scroll bar.
|
|
516
|
+
content_container_style: Style applied to the inner content
|
|
517
|
+
wrapper.
|
|
518
|
+
style: Style for the outer scroll container.
|
|
519
|
+
ref: Optional [`Ref`][pythonnative.Ref]; receives a
|
|
520
|
+
[`ListController`][pythonnative.ListController] on
|
|
521
|
+
``ref.current`` after mount.
|
|
522
|
+
key: Stable identity for keyed reconciliation of the list.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
A virtualized list element (a function component instance).
|
|
526
|
+
|
|
527
|
+
Example:
|
|
528
|
+
```python
|
|
529
|
+
import pythonnative as pn
|
|
530
|
+
|
|
531
|
+
items = [{"id": i, "name": f"Item {i}"} for i in range(10000)]
|
|
532
|
+
|
|
533
|
+
pn.FlatList(
|
|
534
|
+
data=items,
|
|
535
|
+
item_height=44,
|
|
536
|
+
render_item=lambda item, _: pn.Text(item["name"]),
|
|
537
|
+
key_extractor=lambda item, _: str(item["id"]),
|
|
538
|
+
)
|
|
539
|
+
```
|
|
540
|
+
"""
|
|
541
|
+
items_list = list(data or [])
|
|
542
|
+
sep = float(separator_height or 0.0)
|
|
543
|
+
|
|
544
|
+
def _row_key(item: Any, index: int) -> str:
|
|
545
|
+
if key_extractor is not None:
|
|
546
|
+
try:
|
|
547
|
+
return str(key_extractor(item, index))
|
|
548
|
+
except Exception:
|
|
549
|
+
pass
|
|
550
|
+
return f"__pn_row_{index}__"
|
|
551
|
+
|
|
552
|
+
def _row_extent(item: Any, index: int) -> Optional[float]:
|
|
553
|
+
if get_item_height is not None:
|
|
554
|
+
try:
|
|
555
|
+
return float(get_item_height(item, index)) + sep
|
|
556
|
+
except Exception:
|
|
557
|
+
return None
|
|
558
|
+
if item_height is not None:
|
|
559
|
+
return float(item_height) + sep
|
|
560
|
+
return None
|
|
561
|
+
|
|
562
|
+
def _make_row(item: Any, index: int) -> Callable[[], Element]:
|
|
563
|
+
def _make() -> Element:
|
|
564
|
+
el = render_item(item, index) if render_item else Text(str(item))
|
|
565
|
+
if sep > 0:
|
|
566
|
+
pad_style: Dict[str, Any] = {"padding_end" if horizontal else "padding_bottom": sep}
|
|
567
|
+
return View(el, style=pad_style)
|
|
568
|
+
return el
|
|
569
|
+
|
|
570
|
+
return _make
|
|
571
|
+
|
|
572
|
+
rows: List[_RowSpec] = []
|
|
573
|
+
if num_columns > 1 and not horizontal:
|
|
574
|
+
for start in range(0, len(items_list), num_columns):
|
|
575
|
+
chunk = items_list[start : start + num_columns]
|
|
576
|
+
|
|
577
|
+
def _make_group(group: List[Any] = chunk, base: int = start) -> Element:
|
|
578
|
+
cells = [
|
|
579
|
+
View(
|
|
580
|
+
render_item(it, base + j) if render_item else Text(str(it)),
|
|
581
|
+
style={"flex": 1},
|
|
582
|
+
key=_row_key(it, base + j),
|
|
583
|
+
)
|
|
584
|
+
for j, it in enumerate(group)
|
|
585
|
+
]
|
|
586
|
+
row = Row(*cells)
|
|
587
|
+
if sep > 0:
|
|
588
|
+
return View(row, style={"padding_bottom": sep})
|
|
589
|
+
return row
|
|
590
|
+
|
|
591
|
+
group_key = "__pn_grp_" + "|".join(_row_key(it, start + j) for j, it in enumerate(chunk))
|
|
592
|
+
extent = (float(item_height) + sep) if item_height is not None else None
|
|
593
|
+
rows.append(_RowSpec(group_key, _make_group, extent, item=chunk, index=start))
|
|
594
|
+
else:
|
|
595
|
+
for i, item in enumerate(items_list):
|
|
596
|
+
rows.append(_RowSpec(_row_key(item, i), _make_row(item, i), _row_extent(item, i), item=item, index=i))
|
|
597
|
+
|
|
598
|
+
estimated = estimated_item_height if estimated_item_height is not None else (item_height or _DEFAULT_ROW_EXTENT)
|
|
599
|
+
|
|
600
|
+
if _use_native_lists():
|
|
601
|
+
return _NativeList(
|
|
602
|
+
rows=rows,
|
|
603
|
+
on_end_reached=on_end_reached,
|
|
604
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
605
|
+
on_viewable_items_changed=on_viewable_items_changed,
|
|
606
|
+
on_scroll=on_scroll,
|
|
607
|
+
shows_scroll_indicator=shows_scroll_indicator,
|
|
608
|
+
list_style=resolve_style(style) or None,
|
|
609
|
+
controller_ref=ref,
|
|
610
|
+
horizontal=horizontal,
|
|
611
|
+
estimated_row_extent=float(estimated) + sep,
|
|
612
|
+
header=list_header,
|
|
613
|
+
footer=list_footer,
|
|
614
|
+
empty=list_empty,
|
|
615
|
+
refresh_control=refresh_control,
|
|
616
|
+
content_container_style=resolve_style(content_container_style) or None,
|
|
617
|
+
).with_key(key)
|
|
618
|
+
|
|
619
|
+
return _VirtualizedList(
|
|
620
|
+
rows=rows,
|
|
621
|
+
horizontal=horizontal,
|
|
622
|
+
estimated_row_extent=float(estimated) + sep,
|
|
623
|
+
header=list_header,
|
|
624
|
+
footer=list_footer,
|
|
625
|
+
empty=list_empty,
|
|
626
|
+
refresh_control=refresh_control,
|
|
627
|
+
on_end_reached=on_end_reached,
|
|
628
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
629
|
+
on_viewable_items_changed=on_viewable_items_changed,
|
|
630
|
+
on_scroll=on_scroll,
|
|
631
|
+
shows_scroll_indicator=shows_scroll_indicator,
|
|
632
|
+
content_container_style=resolve_style(content_container_style) or None,
|
|
633
|
+
list_style=resolve_style(style) or None,
|
|
634
|
+
controller_ref=ref,
|
|
635
|
+
).with_key(key)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def SectionList(
|
|
639
|
+
*,
|
|
640
|
+
sections: Optional[List[Dict[str, Any]]] = None,
|
|
641
|
+
render_item: Optional[Callable[[Any, int, int], Element]] = None,
|
|
642
|
+
render_section_header: Optional[Callable[[Dict[str, Any], int], Element]] = None,
|
|
643
|
+
key_extractor: Optional[Callable[[Any, int], str]] = None,
|
|
644
|
+
item_height: Optional[float] = None,
|
|
645
|
+
get_item_height: Optional[Callable[[Any, int, int], float]] = None,
|
|
646
|
+
estimated_item_height: Optional[float] = None,
|
|
647
|
+
section_header_height: Optional[float] = None,
|
|
648
|
+
separator_height: float = 0,
|
|
649
|
+
refresh_control: Optional[Element] = None,
|
|
650
|
+
list_header: Optional[Element] = None,
|
|
651
|
+
list_footer: Optional[Element] = None,
|
|
652
|
+
list_empty: Optional[Element] = None,
|
|
653
|
+
on_end_reached: Optional[Callable[[], Any]] = None,
|
|
654
|
+
on_end_reached_threshold: float = 0.5,
|
|
655
|
+
on_scroll: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
656
|
+
style: StyleProp = None,
|
|
657
|
+
ref: Optional[Ref] = None,
|
|
658
|
+
key: Optional[str] = None,
|
|
659
|
+
) -> Element:
|
|
660
|
+
"""Virtualized list with section headers interleaved between row groups.
|
|
661
|
+
|
|
662
|
+
Flattens ``sections`` into a single virtualized sequence where each
|
|
663
|
+
entry is either a header or an item, then reuses the same windowing
|
|
664
|
+
engine as [`FlatList`][pythonnative.FlatList]; headers and items
|
|
665
|
+
may have different (and variable) heights.
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
sections: Each section is ``{"title": ..., "data": [...]}``.
|
|
669
|
+
render_item: ``render_item(item, item_index, section_index) ->
|
|
670
|
+
Element``.
|
|
671
|
+
render_section_header: ``render_section_header(section,
|
|
672
|
+
section_index) -> Element``. Defaults to a bold
|
|
673
|
+
[`Text`][pythonnative.Text] of the section title.
|
|
674
|
+
key_extractor: Stable key per item: ``key_extractor(item,
|
|
675
|
+
item_index) -> str``.
|
|
676
|
+
item_height: Uniform item extent in points, when known.
|
|
677
|
+
get_item_height: ``get_item_height(item, item_index,
|
|
678
|
+
section_index) -> float`` for exact variable extents.
|
|
679
|
+
estimated_item_height: Starting estimate for unmeasured rows.
|
|
680
|
+
section_header_height: Header extent in points, when known.
|
|
681
|
+
separator_height: Gap below each item, in points.
|
|
682
|
+
refresh_control: Optional [`RefreshControl`][pythonnative.RefreshControl] element.
|
|
683
|
+
list_header: Element rendered once before everything.
|
|
684
|
+
list_footer: Element rendered once after everything.
|
|
685
|
+
list_empty: Element rendered when there are no sections.
|
|
686
|
+
on_end_reached: Called near the end of the content.
|
|
687
|
+
on_end_reached_threshold: Distance from the end, in viewport
|
|
688
|
+
multiples, at which ``on_end_reached`` fires.
|
|
689
|
+
on_scroll: Called with the raw scroll payload.
|
|
690
|
+
style: Style for the outer scroll container.
|
|
691
|
+
ref: Optional [`Ref`][pythonnative.Ref]; receives a
|
|
692
|
+
[`ListController`][pythonnative.ListController] on
|
|
693
|
+
``ref.current`` after mount.
|
|
694
|
+
key: Stable identity for keyed reconciliation of the list.
|
|
695
|
+
|
|
696
|
+
Returns:
|
|
697
|
+
A virtualized list element (a function component instance).
|
|
698
|
+
"""
|
|
699
|
+
sections_list = list(sections or [])
|
|
700
|
+
sep = float(separator_height or 0.0)
|
|
701
|
+
|
|
702
|
+
def _header_el(section: Dict[str, Any], s_idx: int) -> Element:
|
|
703
|
+
if render_section_header is not None:
|
|
704
|
+
return render_section_header(section, s_idx)
|
|
705
|
+
return Text(str(section.get("title", "")), style={"bold": True, "padding": 8})
|
|
706
|
+
|
|
707
|
+
def _item_el(item: Any, i_idx: int, s_idx: int) -> Element:
|
|
708
|
+
if render_item is not None:
|
|
709
|
+
return render_item(item, i_idx, s_idx)
|
|
710
|
+
return Text(str(item))
|
|
711
|
+
|
|
712
|
+
rows: List[_RowSpec] = []
|
|
713
|
+
flat_index = 0
|
|
714
|
+
for s_idx, section in enumerate(sections_list):
|
|
715
|
+
|
|
716
|
+
def _make_header(sec: Dict[str, Any] = section, si: int = s_idx) -> Element:
|
|
717
|
+
return _header_el(sec, si)
|
|
718
|
+
|
|
719
|
+
rows.append(
|
|
720
|
+
_RowSpec(
|
|
721
|
+
f"__pn_sec_{s_idx}__",
|
|
722
|
+
_make_header,
|
|
723
|
+
float(section_header_height) if section_header_height is not None else None,
|
|
724
|
+
item=section,
|
|
725
|
+
index=flat_index,
|
|
726
|
+
)
|
|
727
|
+
)
|
|
728
|
+
flat_index += 1
|
|
729
|
+
for i_idx, item in enumerate(section.get("data", []) or []):
|
|
730
|
+
if key_extractor is not None:
|
|
731
|
+
try:
|
|
732
|
+
row_key = f"s{s_idx}:" + str(key_extractor(item, i_idx))
|
|
733
|
+
except Exception:
|
|
734
|
+
row_key = f"__pn_row_{s_idx}_{i_idx}__"
|
|
735
|
+
else:
|
|
736
|
+
row_key = f"__pn_row_{s_idx}_{i_idx}__"
|
|
737
|
+
|
|
738
|
+
def _make_item(it: Any = item, ii: int = i_idx, si: int = s_idx) -> Element:
|
|
739
|
+
el = _item_el(it, ii, si)
|
|
740
|
+
if sep > 0:
|
|
741
|
+
return View(el, style={"padding_bottom": sep})
|
|
742
|
+
return el
|
|
743
|
+
|
|
744
|
+
extent: Optional[float] = None
|
|
745
|
+
if get_item_height is not None:
|
|
746
|
+
try:
|
|
747
|
+
extent = float(get_item_height(item, i_idx, s_idx)) + sep
|
|
748
|
+
except Exception:
|
|
749
|
+
extent = None
|
|
750
|
+
elif item_height is not None:
|
|
751
|
+
extent = float(item_height) + sep
|
|
752
|
+
rows.append(_RowSpec(row_key, _make_item, extent, item=item, index=flat_index))
|
|
753
|
+
flat_index += 1
|
|
754
|
+
|
|
755
|
+
estimated = estimated_item_height if estimated_item_height is not None else (item_height or _DEFAULT_ROW_EXTENT)
|
|
756
|
+
|
|
757
|
+
if _use_native_lists():
|
|
758
|
+
return _NativeList(
|
|
759
|
+
rows=rows,
|
|
760
|
+
on_end_reached=on_end_reached,
|
|
761
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
762
|
+
on_scroll=on_scroll,
|
|
763
|
+
list_style=resolve_style(style) or None,
|
|
764
|
+
controller_ref=ref,
|
|
765
|
+
estimated_row_extent=float(estimated) + sep,
|
|
766
|
+
header=list_header,
|
|
767
|
+
footer=list_footer,
|
|
768
|
+
empty=list_empty,
|
|
769
|
+
refresh_control=refresh_control,
|
|
770
|
+
).with_key(key)
|
|
771
|
+
|
|
772
|
+
return _VirtualizedList(
|
|
773
|
+
rows=rows,
|
|
774
|
+
horizontal=False,
|
|
775
|
+
estimated_row_extent=float(estimated) + sep,
|
|
776
|
+
header=list_header,
|
|
777
|
+
footer=list_footer,
|
|
778
|
+
empty=list_empty,
|
|
779
|
+
refresh_control=refresh_control,
|
|
780
|
+
on_end_reached=on_end_reached,
|
|
781
|
+
on_end_reached_threshold=on_end_reached_threshold,
|
|
782
|
+
on_scroll=on_scroll,
|
|
783
|
+
list_style=resolve_style(style) or None,
|
|
784
|
+
controller_ref=ref,
|
|
785
|
+
).with_key(key)
|