pythonnative 0.36.0__py3-none-any.whl → 0.38.0__py3-none-any.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 +14 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +24 -86
- pythonnative/bootstrap.py +90 -0
- pythonnative/bridge/__init__.py +306 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +164 -0
- pythonnative/bridge/fake.py +236 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/cli/pn.py +96 -10
- pythonnative/components/_base.py +31 -0
- pythonnative/components/controls.py +19 -16
- pythonnative/components/layout.py +70 -11
- pythonnative/components/lists.py +6 -6
- pythonnative/gestures.py +40 -16
- pythonnative/hooks.py +41 -26
- pythonnative/hosts/__init__.py +13 -36
- pythonnative/hosts/native.py +348 -0
- pythonnative/images.py +6 -5
- pythonnative/native_modules/__init__.py +49 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +22 -87
- pythonnative/native_modules/biometrics.py +15 -130
- pythonnative/native_modules/camera.py +24 -296
- pythonnative/native_modules/clipboard.py +14 -111
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +90 -187
- pythonnative/native_modules/haptics.py +29 -135
- pythonnative/native_modules/linking.py +29 -141
- pythonnative/native_modules/location.py +32 -186
- pythonnative/native_modules/net_info.py +22 -135
- pythonnative/native_modules/notifications.py +37 -265
- pythonnative/native_modules/permissions.py +78 -194
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +23 -164
- pythonnative/native_modules/share.py +9 -114
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/navigation/__init__.py +2 -1
- pythonnative/navigation/hooks.py +46 -7
- pythonnative/navigation/state.py +30 -9
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +23 -3
- pythonnative/project/builder.py +99 -48
- pythonnative/project/config.py +96 -26
- pythonnative/project/deps.py +703 -0
- pythonnative/project/doctor.py +53 -4
- pythonnative/project/plugins.py +366 -0
- pythonnative/project/runtime_assets.py +13 -14
- pythonnative/py.typed +0 -0
- pythonnative/runtime.py +35 -203
- pythonnative/sdk/__init__.py +48 -33
- pythonnative/sdk/_components.py +57 -76
- pythonnative/storage.py +18 -243
- pythonnative/style.py +94 -44
- pythonnative/templates/android_template/app/build.gradle +11 -11
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
- pythonnative/templates/android_template/build.gradle +7 -4
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +1 -1
- pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
- pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
- pythonnative/templates/android_template/settings.gradle +1 -0
- pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
- pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +41 -48
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
- pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +82 -10
- pythonnative/utils.py +26 -109
- pythonnative/virtual_rows.py +13 -23
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/METADATA +10 -12
- pythonnative-0.38.0.dist-info/RECORD +264 -0
- pythonnative/hosts/android.py +0 -269
- pythonnative/hosts/ios.py +0 -404
- pythonnative/native_views/android.py +0 -3702
- pythonnative/native_views/ios.py +0 -5253
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
- pythonnative-0.36.0.dist-info/RECORD +0 -155
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/top_level.txt +0 -0
pythonnative/cli/pn.py
CHANGED
|
@@ -6,6 +6,9 @@ The console script `pn` (declared in `pyproject.toml`) dispatches to:
|
|
|
6
6
|
``app/``) into ``./name/``, or into the current directory when no name
|
|
7
7
|
is given.
|
|
8
8
|
- `pn doctor [platform]`: diagnose the local toolchain and config.
|
|
9
|
+
- `pn deps [platform]`: resolve ``[requirements].packages`` for every
|
|
10
|
+
device target and report which wheels would be used (or why a package
|
|
11
|
+
can't be installed), without building anything.
|
|
9
12
|
- `pn preview [component]`: render the app in a desktop (Tkinter) window
|
|
10
13
|
with Fast Refresh, the fast inner dev loop, no device required.
|
|
11
14
|
- `pn devices [platform]`: list connected devices, emulators, and
|
|
@@ -41,6 +44,7 @@ from pathlib import Path
|
|
|
41
44
|
from typing import Any, Dict, List, Optional, TextIO
|
|
42
45
|
|
|
43
46
|
from ..project import builder as builder_mod
|
|
47
|
+
from ..project import deps as deps_mod
|
|
44
48
|
from ..project import devices as devices_mod
|
|
45
49
|
from ..project import doctor as doctor_mod
|
|
46
50
|
from ..project.android import collect_logcat_filters
|
|
@@ -54,22 +58,29 @@ HOT_RELOAD_DEV_ROOT = "pythonnative_dev"
|
|
|
54
58
|
# init
|
|
55
59
|
# ======================================================================
|
|
56
60
|
|
|
57
|
-
_MAIN_TEMPLATE = """
|
|
61
|
+
_MAIN_TEMPLATE = """from typing import TypedDict
|
|
62
|
+
|
|
63
|
+
import pythonnative as pn
|
|
58
64
|
|
|
59
65
|
Stack = pn.create_stack_navigator()
|
|
60
66
|
|
|
61
67
|
|
|
68
|
+
class DetailParams(TypedDict):
|
|
69
|
+
count: int
|
|
70
|
+
|
|
71
|
+
|
|
62
72
|
@pn.component
|
|
63
73
|
def HomeScreen():
|
|
64
74
|
count, set_count = pn.use_state(0)
|
|
65
75
|
nav = pn.use_navigation()
|
|
76
|
+
theme = pn.use_theme()
|
|
66
77
|
return pn.ScrollView(
|
|
67
78
|
pn.Column(
|
|
68
|
-
pn.Text("Hello from PythonNative!", style={"font_size":
|
|
79
|
+
pn.Text("Hello from PythonNative!", style={"font_size": theme.font_size_title, "bold": True}),
|
|
69
80
|
pn.Text(f"Tapped {count} times"),
|
|
70
81
|
pn.Button("Tap me", on_press=lambda: set_count(count + 1)),
|
|
71
|
-
pn.Button("Open detail", on_press=lambda: nav.navigate("Detail",
|
|
72
|
-
style={"spacing":
|
|
82
|
+
pn.Button("Open detail", on_press=lambda: nav.navigate("Detail", count=count)),
|
|
83
|
+
style={"spacing": theme.spacing_large, "padding": 16, "align_items": "stretch"},
|
|
73
84
|
)
|
|
74
85
|
)
|
|
75
86
|
|
|
@@ -77,9 +88,9 @@ def HomeScreen():
|
|
|
77
88
|
@pn.component
|
|
78
89
|
def DetailScreen():
|
|
79
90
|
nav = pn.use_navigation()
|
|
80
|
-
|
|
91
|
+
route = pn.use_route(DetailParams)
|
|
81
92
|
return pn.Column(
|
|
82
|
-
pn.Text(f"Detail: count was {params
|
|
93
|
+
pn.Text(f"Detail: count was {route.params['count']}", style={"font_size": 20}),
|
|
83
94
|
pn.Button("Back", on_press=nav.go_back),
|
|
84
95
|
style={"spacing": 12, "padding": 16},
|
|
85
96
|
)
|
|
@@ -89,8 +100,8 @@ def DetailScreen():
|
|
|
89
100
|
def App():
|
|
90
101
|
return pn.NavigationContainer(
|
|
91
102
|
Stack.Navigator(
|
|
92
|
-
Stack.Screen("Home",
|
|
93
|
-
Stack.Screen("Detail",
|
|
103
|
+
Stack.Screen("Home", HomeScreen, title="Home"),
|
|
104
|
+
Stack.Screen("Detail", DetailScreen, title="Detail"),
|
|
94
105
|
)
|
|
95
106
|
)
|
|
96
107
|
"""
|
|
@@ -278,6 +289,56 @@ def app_id_command(args: argparse.Namespace) -> None:
|
|
|
278
289
|
print(config.application_id if args.platform == "android" else config.bundle_id)
|
|
279
290
|
|
|
280
291
|
|
|
292
|
+
# ======================================================================
|
|
293
|
+
# deps
|
|
294
|
+
# ======================================================================
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def deps_command(args: argparse.Namespace) -> None:
|
|
298
|
+
"""Report how ``[requirements].packages`` resolve for each device target.
|
|
299
|
+
|
|
300
|
+
Runs pip in its cross-platform dry-run mode once per target (iOS
|
|
301
|
+
device, iOS Simulator, and one per Android ABI) and prints the
|
|
302
|
+
wheel each package would use, flagging binary wheels and their
|
|
303
|
+
source index. Exits non-zero when any target can't be satisfied,
|
|
304
|
+
so it doubles as a CI gate. ``--json`` emits the same data as a
|
|
305
|
+
machine-readable document.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
args: Parsed namespace with optional ``platform``, ``json``, and
|
|
309
|
+
``python`` (the interpreter to run pip with).
|
|
310
|
+
"""
|
|
311
|
+
platform: Optional[str] = getattr(args, "platform", None)
|
|
312
|
+
as_json: bool = getattr(args, "json", False)
|
|
313
|
+
python: Optional[str] = getattr(args, "python", None)
|
|
314
|
+
|
|
315
|
+
config = _load_config_or_exit()
|
|
316
|
+
targets = deps_mod.targets_for(config, platform)
|
|
317
|
+
runner = builder_mod.SubprocessRunner()
|
|
318
|
+
if not as_json and config.requirements:
|
|
319
|
+
print(
|
|
320
|
+
f"Resolving {len(config.requirements)} requirement(s) for Python {config.python_version} "
|
|
321
|
+
f"across {len(targets)} target(s)...\n"
|
|
322
|
+
)
|
|
323
|
+
resolutions = deps_mod.resolve_all(config, targets, runner=runner, python=python)
|
|
324
|
+
|
|
325
|
+
if as_json:
|
|
326
|
+
print(
|
|
327
|
+
json.dumps(
|
|
328
|
+
{
|
|
329
|
+
"python_version": config.python_version,
|
|
330
|
+
"requirements": list(config.requirements),
|
|
331
|
+
"targets": [res.to_dict() for res in resolutions],
|
|
332
|
+
},
|
|
333
|
+
indent=2,
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
else:
|
|
337
|
+
print(deps_mod.format_report(resolutions, requirements=config.requirements))
|
|
338
|
+
if any(not res.ok for res in resolutions):
|
|
339
|
+
sys.exit(1)
|
|
340
|
+
|
|
341
|
+
|
|
281
342
|
# ======================================================================
|
|
282
343
|
# preview
|
|
283
344
|
# ======================================================================
|
|
@@ -426,8 +487,18 @@ def run_project(args: argparse.Namespace) -> None:
|
|
|
426
487
|
config = _load_config_or_exit()
|
|
427
488
|
builder = builder_mod.Builder(config, log=print)
|
|
428
489
|
|
|
490
|
+
# Resolve third-party packages only for the destination being built
|
|
491
|
+
# (device wheels and Simulator wheels differ); prepare-only keeps both
|
|
492
|
+
# slices so the staged project builds for either in Xcode.
|
|
493
|
+
if prepare_only:
|
|
494
|
+
ios_sdks: tuple = deps_mod.IOS_SDKS
|
|
495
|
+
elif device is not None and device.kind == "device":
|
|
496
|
+
ios_sdks = ("iphoneos",)
|
|
497
|
+
else:
|
|
498
|
+
ios_sdks = ("iphonesimulator",)
|
|
499
|
+
|
|
429
500
|
try:
|
|
430
|
-
prepared = builder.prepare(platform)
|
|
501
|
+
prepared = builder.prepare(platform, ios_sdks=ios_sdks)
|
|
431
502
|
except builder_mod.BuildError as exc:
|
|
432
503
|
print(f"Error: {exc}")
|
|
433
504
|
sys.exit(1)
|
|
@@ -593,7 +664,11 @@ def build_project(args: argparse.Namespace) -> None:
|
|
|
593
664
|
sys.exit(1)
|
|
594
665
|
|
|
595
666
|
try:
|
|
596
|
-
prepared = builder.prepare(
|
|
667
|
+
prepared = builder.prepare(
|
|
668
|
+
platform,
|
|
669
|
+
release=not debug,
|
|
670
|
+
ios_sdks=("iphonesimulator",) if debug else ("iphoneos",),
|
|
671
|
+
)
|
|
597
672
|
if platform == "android":
|
|
598
673
|
artifacts = builder.build_android(prepared, debug=debug)
|
|
599
674
|
else:
|
|
@@ -1020,6 +1095,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1020
1095
|
parser_doctor.add_argument("platform", nargs="?", choices=["android", "ios"], help="Restrict checks to a platform")
|
|
1021
1096
|
parser_doctor.set_defaults(func=doctor_command)
|
|
1022
1097
|
|
|
1098
|
+
parser_deps = subparsers.add_parser(
|
|
1099
|
+
"deps", help="Check which wheels [requirements].packages resolve to on each device target"
|
|
1100
|
+
)
|
|
1101
|
+
parser_deps.add_argument("platform", nargs="?", choices=["android", "ios"], help="Restrict to a platform")
|
|
1102
|
+
parser_deps.add_argument("--json", action="store_true", help="Print a JSON report for scripting")
|
|
1103
|
+
parser_deps.add_argument(
|
|
1104
|
+
"--python",
|
|
1105
|
+
help="Interpreter to run pip with (default: the one running pn; any version works, pip cross-resolves)",
|
|
1106
|
+
)
|
|
1107
|
+
parser_deps.set_defaults(func=deps_command)
|
|
1108
|
+
|
|
1023
1109
|
parser_preview = subparsers.add_parser("preview", help="Render the app in a desktop window")
|
|
1024
1110
|
parser_preview.add_argument(
|
|
1025
1111
|
"component",
|
pythonnative/components/_base.py
CHANGED
|
@@ -76,6 +76,37 @@ def _make_element(
|
|
|
76
76
|
return Element(name, out, list(children), key=key)
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
# ======================================================================
|
|
80
|
+
# RefreshControl attachment
|
|
81
|
+
# ======================================================================
|
|
82
|
+
|
|
83
|
+
REFRESH_CONTROL_TYPE = "RefreshControl"
|
|
84
|
+
"""Element type produced by [`RefreshControl`][pythonnative.RefreshControl]."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _refresh_control_props(control: Optional[Element], *, owner: str) -> Optional[Dict[str, Any]]:
|
|
88
|
+
"""Turn a ``RefreshControl`` element into the ``refresh_control`` wire prop.
|
|
89
|
+
|
|
90
|
+
Scroll containers hold their refresh control as a prop rather than
|
|
91
|
+
a child (the native side attaches a ``UIRefreshControl`` /
|
|
92
|
+
``SwipeRefreshLayout`` to the scroll view itself), so the element
|
|
93
|
+
is unwrapped here into the flat dict the bridge and the event
|
|
94
|
+
extractor expect. Keeping it an element on the Python side means
|
|
95
|
+
users build it like every other piece of UI and get a clear error
|
|
96
|
+
if they pass the wrong thing.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
TypeError: If ``control`` is not a ``RefreshControl`` element.
|
|
100
|
+
"""
|
|
101
|
+
if control is None:
|
|
102
|
+
return None
|
|
103
|
+
if not isinstance(control, Element) or control.type != REFRESH_CONTROL_TYPE:
|
|
104
|
+
raise TypeError(
|
|
105
|
+
f"{owner}(refresh_control=...) expects pn.RefreshControl(...), got {type(control).__name__}: {control!r}"
|
|
106
|
+
)
|
|
107
|
+
return dict(control.props)
|
|
108
|
+
|
|
109
|
+
|
|
79
110
|
# ======================================================================
|
|
80
111
|
# Rich text spans
|
|
81
112
|
# ======================================================================
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
``Switch``, ``Slider``, ``ProgressBar``, ``ActivityIndicator``,
|
|
4
4
|
``Checkbox``, ``SegmentedControl``, ``DatePicker``, ``Picker``, the
|
|
5
|
-
``RefreshControl
|
|
5
|
+
``RefreshControl``, and ``StatusBar``.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from typing import Any, Callable, Dict, List, Literal, Optional
|
|
@@ -10,7 +10,7 @@ from typing import Any, Callable, Dict, List, Literal, Optional
|
|
|
10
10
|
from ..element import Element
|
|
11
11
|
from ..hooks import Ref
|
|
12
12
|
from ..style import AccessibilityState, Color, StyleProp
|
|
13
|
-
from ._base import _make_element
|
|
13
|
+
from ._base import REFRESH_CONTROL_TYPE, _make_element
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def Switch(
|
|
@@ -426,13 +426,17 @@ def RefreshControl(
|
|
|
426
426
|
refreshing: bool = False,
|
|
427
427
|
on_refresh: Optional[Callable[[], Any]] = None,
|
|
428
428
|
tint_color: Optional[Color] = None,
|
|
429
|
-
) ->
|
|
430
|
-
"""Pull-to-refresh
|
|
429
|
+
) -> Element:
|
|
430
|
+
"""Pull-to-refresh control for [`ScrollView`][pythonnative.ScrollView] and the list components.
|
|
431
431
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
[`
|
|
435
|
-
|
|
432
|
+
Pass the result as the ``refresh_control=`` prop of a
|
|
433
|
+
[`ScrollView`][pythonnative.ScrollView],
|
|
434
|
+
[`FlatList`][pythonnative.FlatList], or
|
|
435
|
+
[`SectionList`][pythonnative.SectionList]. It is a regular
|
|
436
|
+
[`Element`][pythonnative.Element] (type ``"RefreshControl"``) built
|
|
437
|
+
like every other piece of UI; the scroll container attaches it to
|
|
438
|
+
its native scroll view rather than rendering it as a child, and
|
|
439
|
+
rejects anything else with a ``TypeError``.
|
|
436
440
|
|
|
437
441
|
Args:
|
|
438
442
|
refreshing: Drive the spinner's visibility from a use_state
|
|
@@ -443,8 +447,7 @@ def RefreshControl(
|
|
|
443
447
|
tint_color: Color of the spinner.
|
|
444
448
|
|
|
445
449
|
Returns:
|
|
446
|
-
|
|
447
|
-
container.
|
|
450
|
+
An [`Element`][pythonnative.Element] of type ``"RefreshControl"``.
|
|
448
451
|
|
|
449
452
|
Example:
|
|
450
453
|
```python
|
|
@@ -467,12 +470,12 @@ def RefreshControl(
|
|
|
467
470
|
)
|
|
468
471
|
```
|
|
469
472
|
"""
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
473
|
+
return _make_element(
|
|
474
|
+
REFRESH_CONTROL_TYPE,
|
|
475
|
+
refreshing=bool(refreshing),
|
|
476
|
+
on_refresh=on_refresh,
|
|
477
|
+
tint_color=tint_color,
|
|
478
|
+
)
|
|
476
479
|
|
|
477
480
|
|
|
478
481
|
def StatusBar(
|
|
@@ -12,7 +12,7 @@ from ..component import component
|
|
|
12
12
|
from ..element import Element
|
|
13
13
|
from ..hooks import Ref, use_keyboard_height, use_safe_area_insets, use_state
|
|
14
14
|
from ..style import AccessibilityState, StyleProp, resolve_style
|
|
15
|
-
from ._base import _make_element
|
|
15
|
+
from ._base import _make_element, _refresh_control_props
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def View(
|
|
@@ -113,7 +113,16 @@ def View(
|
|
|
113
113
|
def Column(
|
|
114
114
|
*children: Element,
|
|
115
115
|
style: StyleProp = None,
|
|
116
|
+
gestures: Optional[List[Any]] = None,
|
|
117
|
+
hit_slop: Optional[Union[float, Dict[str, float]]] = None,
|
|
116
118
|
on_layout: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
119
|
+
accessibility_label: Optional[str] = None,
|
|
120
|
+
accessibility_hint: Optional[str] = None,
|
|
121
|
+
accessibility_role: Optional[str] = None,
|
|
122
|
+
accessible: Optional[bool] = None,
|
|
123
|
+
accessibility_state: Optional[AccessibilityState] = None,
|
|
124
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
125
|
+
test_id: Optional[str] = None,
|
|
117
126
|
ref: Optional[Ref] = None,
|
|
118
127
|
key: Optional[str] = None,
|
|
119
128
|
) -> Element:
|
|
@@ -123,12 +132,25 @@ def Column(
|
|
|
123
132
|
``flex_direction`` locked to ``"column"``. Use ``View`` directly if
|
|
124
133
|
you need to switch between row and column at runtime.
|
|
125
134
|
|
|
135
|
+
Accepts every [`View`][pythonnative.View] prop (gestures, hit slop,
|
|
136
|
+
accessibility, ``test_id``); only ``flex_direction`` is fixed.
|
|
137
|
+
|
|
126
138
|
Args:
|
|
127
139
|
*children: Child elements stacked top to bottom.
|
|
128
140
|
style: Style dict (or list of dicts).
|
|
141
|
+
gestures: Gesture descriptors recognized natively on this view.
|
|
142
|
+
hit_slop: Extra touch target beyond the bounds (see ``View``).
|
|
129
143
|
on_layout: Callback invoked with
|
|
130
144
|
``{"x", "y", "width", "height"}`` after layout and on
|
|
131
145
|
frame changes.
|
|
146
|
+
accessibility_label: Spoken description for screen readers.
|
|
147
|
+
accessibility_hint: Spoken extra detail (iOS only).
|
|
148
|
+
accessibility_role: Semantic role for assistive tech.
|
|
149
|
+
accessible: Override whether the element is exposed to AT.
|
|
150
|
+
accessibility_state: Current widget state for assistive tech.
|
|
151
|
+
accessibility_live_region: How AT announces dynamic changes
|
|
152
|
+
(Android only).
|
|
153
|
+
test_id: Stable identifier for UI tests.
|
|
132
154
|
ref: Optional [`Ref`][pythonnative.Ref] for native-view access.
|
|
133
155
|
key: Stable identity for keyed reconciliation.
|
|
134
156
|
|
|
@@ -139,9 +161,18 @@ def Column(
|
|
|
139
161
|
"Column",
|
|
140
162
|
*children,
|
|
141
163
|
style=style,
|
|
142
|
-
on_layout=on_layout,
|
|
143
164
|
ref=ref,
|
|
144
165
|
key=key,
|
|
166
|
+
gestures=gestures,
|
|
167
|
+
hit_slop=hit_slop,
|
|
168
|
+
on_layout=on_layout,
|
|
169
|
+
accessibility_label=accessibility_label,
|
|
170
|
+
accessibility_hint=accessibility_hint,
|
|
171
|
+
accessibility_role=accessibility_role,
|
|
172
|
+
accessible=accessible,
|
|
173
|
+
accessibility_state=accessibility_state,
|
|
174
|
+
accessibility_live_region=accessibility_live_region,
|
|
175
|
+
test_id=test_id,
|
|
145
176
|
_forced={"flex_direction": "column"},
|
|
146
177
|
)
|
|
147
178
|
|
|
@@ -149,7 +180,16 @@ def Column(
|
|
|
149
180
|
def Row(
|
|
150
181
|
*children: Element,
|
|
151
182
|
style: StyleProp = None,
|
|
183
|
+
gestures: Optional[List[Any]] = None,
|
|
184
|
+
hit_slop: Optional[Union[float, Dict[str, float]]] = None,
|
|
152
185
|
on_layout: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
186
|
+
accessibility_label: Optional[str] = None,
|
|
187
|
+
accessibility_hint: Optional[str] = None,
|
|
188
|
+
accessibility_role: Optional[str] = None,
|
|
189
|
+
accessible: Optional[bool] = None,
|
|
190
|
+
accessibility_state: Optional[AccessibilityState] = None,
|
|
191
|
+
accessibility_live_region: Optional[Literal["none", "polite", "assertive"]] = None,
|
|
192
|
+
test_id: Optional[str] = None,
|
|
153
193
|
ref: Optional[Ref] = None,
|
|
154
194
|
key: Optional[str] = None,
|
|
155
195
|
) -> Element:
|
|
@@ -159,12 +199,25 @@ def Row(
|
|
|
159
199
|
``flex_direction`` locked to ``"row"``. Use ``View`` directly if you
|
|
160
200
|
need to switch between row and column at runtime.
|
|
161
201
|
|
|
202
|
+
Accepts every [`View`][pythonnative.View] prop (gestures, hit slop,
|
|
203
|
+
accessibility, ``test_id``); only ``flex_direction`` is fixed.
|
|
204
|
+
|
|
162
205
|
Args:
|
|
163
206
|
*children: Child elements arranged left to right.
|
|
164
207
|
style: Style dict (or list of dicts).
|
|
208
|
+
gestures: Gesture descriptors recognized natively on this view.
|
|
209
|
+
hit_slop: Extra touch target beyond the bounds (see ``View``).
|
|
165
210
|
on_layout: Callback invoked with
|
|
166
211
|
``{"x", "y", "width", "height"}`` after layout and on
|
|
167
212
|
frame changes.
|
|
213
|
+
accessibility_label: Spoken description for screen readers.
|
|
214
|
+
accessibility_hint: Spoken extra detail (iOS only).
|
|
215
|
+
accessibility_role: Semantic role for assistive tech.
|
|
216
|
+
accessible: Override whether the element is exposed to AT.
|
|
217
|
+
accessibility_state: Current widget state for assistive tech.
|
|
218
|
+
accessibility_live_region: How AT announces dynamic changes
|
|
219
|
+
(Android only).
|
|
220
|
+
test_id: Stable identifier for UI tests.
|
|
168
221
|
ref: Optional [`Ref`][pythonnative.Ref] for native-view access.
|
|
169
222
|
key: Stable identity for keyed reconciliation.
|
|
170
223
|
|
|
@@ -175,9 +228,18 @@ def Row(
|
|
|
175
228
|
"Row",
|
|
176
229
|
*children,
|
|
177
230
|
style=style,
|
|
178
|
-
on_layout=on_layout,
|
|
179
231
|
ref=ref,
|
|
180
232
|
key=key,
|
|
233
|
+
gestures=gestures,
|
|
234
|
+
hit_slop=hit_slop,
|
|
235
|
+
on_layout=on_layout,
|
|
236
|
+
accessibility_label=accessibility_label,
|
|
237
|
+
accessibility_hint=accessibility_hint,
|
|
238
|
+
accessibility_role=accessibility_role,
|
|
239
|
+
accessible=accessible,
|
|
240
|
+
accessibility_state=accessibility_state,
|
|
241
|
+
accessibility_live_region=accessibility_live_region,
|
|
242
|
+
test_id=test_id,
|
|
181
243
|
_forced={"flex_direction": "row"},
|
|
182
244
|
)
|
|
183
245
|
|
|
@@ -219,7 +281,7 @@ def Spacer(
|
|
|
219
281
|
|
|
220
282
|
def ScrollView(
|
|
221
283
|
*children: Element,
|
|
222
|
-
refresh_control: Optional[
|
|
284
|
+
refresh_control: Optional[Element] = None,
|
|
223
285
|
scroll_axis: Optional[Literal["vertical", "horizontal"]] = None,
|
|
224
286
|
on_scroll: Optional[Callable[[Dict[str, float]], None]] = None,
|
|
225
287
|
shows_scroll_indicator: bool = True,
|
|
@@ -240,11 +302,8 @@ def ScrollView(
|
|
|
240
302
|
|
|
241
303
|
Args:
|
|
242
304
|
*children: Child elements to scroll.
|
|
243
|
-
refresh_control: Optional
|
|
244
|
-
|
|
245
|
-
[`RefreshControl`][pythonnative.RefreshControl]. The dict
|
|
246
|
-
must have ``refreshing`` (bool) and ``on_refresh``
|
|
247
|
-
(callable).
|
|
305
|
+
refresh_control: Optional [`RefreshControl`][pythonnative.RefreshControl]
|
|
306
|
+
element attached to the scroll view for pull-to-refresh.
|
|
248
307
|
scroll_axis: ``"vertical"`` (default) or ``"horizontal"``.
|
|
249
308
|
on_scroll: Callback invoked with ``{"x": …, "y": …}`` content
|
|
250
309
|
offsets as the user scrolls.
|
|
@@ -271,7 +330,7 @@ def ScrollView(
|
|
|
271
330
|
style=style,
|
|
272
331
|
ref=ref,
|
|
273
332
|
key=key,
|
|
274
|
-
refresh_control=refresh_control,
|
|
333
|
+
refresh_control=_refresh_control_props(refresh_control, owner="ScrollView"),
|
|
275
334
|
scroll_axis=scroll_axis,
|
|
276
335
|
on_scroll=on_scroll,
|
|
277
336
|
shows_scroll_indicator=False if shows_scroll_indicator is False else None,
|
|
@@ -326,7 +385,7 @@ def _SafeAreaContainer(
|
|
|
326
385
|
for edge in _SAFE_AREA_EDGES:
|
|
327
386
|
if edge not in selected:
|
|
328
387
|
continue
|
|
329
|
-
inset = float(insets
|
|
388
|
+
inset = float(getattr(insets, edge, 0.0) or 0.0)
|
|
330
389
|
if inset > 0:
|
|
331
390
|
resolved[f"padding_{edge}"] = _numeric_edge_padding(resolved, edge) + inset
|
|
332
391
|
return Element("SafeAreaView", resolved, list(children))
|
pythonnative/components/lists.py
CHANGED
|
@@ -124,7 +124,7 @@ def _VirtualizedList(
|
|
|
124
124
|
header: Optional[Element] = None,
|
|
125
125
|
footer: Optional[Element] = None,
|
|
126
126
|
empty: Optional[Element] = None,
|
|
127
|
-
refresh_control: Optional[
|
|
127
|
+
refresh_control: Optional[Element] = None,
|
|
128
128
|
on_end_reached: Optional[Callable[[], Any]] = None,
|
|
129
129
|
on_end_reached_threshold: Optional[float] = None,
|
|
130
130
|
on_viewable_items_changed: Optional[Callable[[List[Dict[str, Any]]], None]] = None,
|
|
@@ -464,7 +464,7 @@ def FlatList(
|
|
|
464
464
|
get_item_height: Optional[Callable[[Any, int], float]] = None,
|
|
465
465
|
estimated_item_height: Optional[float] = None,
|
|
466
466
|
separator_height: float = 0,
|
|
467
|
-
refresh_control: Optional[
|
|
467
|
+
refresh_control: Optional[Element] = None,
|
|
468
468
|
horizontal: bool = False,
|
|
469
469
|
num_columns: int = 1,
|
|
470
470
|
list_header: Optional[Element] = None,
|
|
@@ -509,8 +509,8 @@ def FlatList(
|
|
|
509
509
|
estimated_item_height: Starting extent estimate for rows whose
|
|
510
510
|
true size isn't known yet (default 44).
|
|
511
511
|
separator_height: Gap below each row, in points.
|
|
512
|
-
refresh_control: Optional
|
|
513
|
-
|
|
512
|
+
refresh_control: Optional [`RefreshControl`][pythonnative.RefreshControl]
|
|
513
|
+
element for pull-to-refresh.
|
|
514
514
|
horizontal: Scroll horizontally (extents become widths).
|
|
515
515
|
num_columns: Render items in a grid of this many columns.
|
|
516
516
|
list_header: Element rendered once before all rows.
|
|
@@ -666,7 +666,7 @@ def SectionList(
|
|
|
666
666
|
estimated_item_height: Optional[float] = None,
|
|
667
667
|
section_header_height: Optional[float] = None,
|
|
668
668
|
separator_height: float = 0,
|
|
669
|
-
refresh_control: Optional[
|
|
669
|
+
refresh_control: Optional[Element] = None,
|
|
670
670
|
list_header: Optional[Element] = None,
|
|
671
671
|
list_footer: Optional[Element] = None,
|
|
672
672
|
list_empty: Optional[Element] = None,
|
|
@@ -699,7 +699,7 @@ def SectionList(
|
|
|
699
699
|
estimated_item_height: Starting estimate for unmeasured rows.
|
|
700
700
|
section_header_height: Header extent in points, when known.
|
|
701
701
|
separator_height: Gap below each item, in points.
|
|
702
|
-
refresh_control: Optional
|
|
702
|
+
refresh_control: Optional [`RefreshControl`][pythonnative.RefreshControl] element.
|
|
703
703
|
list_header: Element rendered once before everything.
|
|
704
704
|
list_footer: Element rendered once after everything.
|
|
705
705
|
list_empty: Element rendered when there are no sections.
|
pythonnative/gestures.py
CHANGED
|
@@ -34,10 +34,13 @@ configuration into plain dicts for the native handler (so prop diffing
|
|
|
34
34
|
never compares closures) and routes the callbacks through the
|
|
35
35
|
tag-based event channel. Recognition itself is native:
|
|
36
36
|
|
|
37
|
-
- **iOS** attaches real ``UIGestureRecognizer`` instances
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
- **iOS** attaches real ``UIGestureRecognizer`` instances
|
|
38
|
+
(``PythonNativeKit``).
|
|
39
|
+
- **Android** runs an equivalent recognizer set in Kotlin (the
|
|
40
|
+
``pythonnative`` Gradle module) on top of ``MotionEvent`` streams.
|
|
41
|
+
- **Desktop** feeds Tk pointer events into the pure-Python
|
|
42
|
+
[`GestureArbiter`][pythonnative.gestures.GestureArbiter] below, which
|
|
43
|
+
doubles as the executable specification for the native ports.
|
|
41
44
|
|
|
42
45
|
Composition
|
|
43
46
|
-----------
|
|
@@ -75,6 +78,7 @@ from __future__ import annotations
|
|
|
75
78
|
|
|
76
79
|
import math
|
|
77
80
|
from dataclasses import dataclass, field
|
|
81
|
+
from enum import Enum
|
|
78
82
|
from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Set, Tuple
|
|
79
83
|
|
|
80
84
|
__all__ = [
|
|
@@ -96,16 +100,30 @@ __all__ = [
|
|
|
96
100
|
]
|
|
97
101
|
|
|
98
102
|
|
|
99
|
-
class GestureState:
|
|
100
|
-
"""
|
|
103
|
+
class GestureState(str, Enum):
|
|
104
|
+
"""Lifecycle states reported on [`GestureEvent.state`][pythonnative.gestures.GestureEvent].
|
|
105
|
+
|
|
106
|
+
A ``str`` enum, so members compare equal to their wire value
|
|
107
|
+
(``GestureState.ENDED == "ended"``) and serialize as plain strings
|
|
108
|
+
across the native bridge, while callers get exhaustive
|
|
109
|
+
``match`` support and autocomplete.
|
|
110
|
+
|
|
111
|
+
Attributes:
|
|
112
|
+
BEGAN: The gesture activated (first callback).
|
|
113
|
+
CHANGED: A continuous gesture updated (pan, pinch, rotation).
|
|
114
|
+
ENDED: The gesture completed successfully.
|
|
115
|
+
CANCELLED: The gesture was interrupted (lost arbitration, view
|
|
116
|
+
unmounted, pointer left the window).
|
|
117
|
+
"""
|
|
101
118
|
|
|
102
119
|
BEGAN = "began"
|
|
103
120
|
CHANGED = "changed"
|
|
104
121
|
ENDED = "ended"
|
|
105
122
|
CANCELLED = "cancelled"
|
|
106
123
|
|
|
124
|
+
def __str__(self) -> str:
|
|
125
|
+
return self.value
|
|
107
126
|
|
|
108
|
-
GestureStateName = Literal["began", "changed", "ended", "cancelled"]
|
|
109
127
|
|
|
110
128
|
GestureCallback = Callable[["GestureEvent"], Any]
|
|
111
129
|
|
|
@@ -138,7 +156,7 @@ class GestureEvent:
|
|
|
138
156
|
"""
|
|
139
157
|
|
|
140
158
|
kind: str
|
|
141
|
-
state:
|
|
159
|
+
state: GestureState
|
|
142
160
|
x: float = 0.0
|
|
143
161
|
y: float = 0.0
|
|
144
162
|
translation_x: float = 0.0
|
|
@@ -150,6 +168,12 @@ class GestureEvent:
|
|
|
150
168
|
pointer_count: int = 1
|
|
151
169
|
direction: Optional[str] = None
|
|
152
170
|
|
|
171
|
+
def __post_init__(self) -> None:
|
|
172
|
+
# Payloads from the native bridge carry plain strings; coerce
|
|
173
|
+
# so ``event.state`` is always a ``GestureState`` member.
|
|
174
|
+
if not isinstance(self.state, GestureState):
|
|
175
|
+
object.__setattr__(self, "state", GestureState(self.state))
|
|
176
|
+
|
|
153
177
|
|
|
154
178
|
_EVENT_FIELDS = frozenset(
|
|
155
179
|
{
|
|
@@ -518,14 +542,14 @@ def serialize_gestures(
|
|
|
518
542
|
|
|
519
543
|
|
|
520
544
|
# ======================================================================
|
|
521
|
-
# Pure-Python recognition engine (
|
|
545
|
+
# Pure-Python recognition engine (desktop backend + reference semantics)
|
|
522
546
|
# ======================================================================
|
|
523
547
|
#
|
|
524
|
-
# iOS
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
#
|
|
548
|
+
# iOS and Android recognize natively. The desktop preview receives raw
|
|
549
|
+
# Tk pointer streams instead, which this arbiter turns into the same
|
|
550
|
+
# GestureEvent payloads. Keeping it in pure Python makes the state
|
|
551
|
+
# machines unit-testable with scripted event sequences; the Kotlin
|
|
552
|
+
# arbiter mirrors this file so the two stay in lockstep.
|
|
529
553
|
|
|
530
554
|
EmitFn = Callable[[int, Dict[str, Any]], None]
|
|
531
555
|
"""``emit(gesture_index, payload)``: the arbiter's output channel."""
|
|
@@ -573,8 +597,8 @@ class _Recognizer:
|
|
|
573
597
|
self.config = config
|
|
574
598
|
self._emit_fn = emit
|
|
575
599
|
|
|
576
|
-
def emit(self, state:
|
|
577
|
-
payload: Dict[str, Any] = {"kind": self.kind(), "state": state}
|
|
600
|
+
def emit(self, state: GestureState, **fields: Any) -> None:
|
|
601
|
+
payload: Dict[str, Any] = {"kind": self.kind(), "state": state.value}
|
|
578
602
|
payload.update(fields)
|
|
579
603
|
self._emit_fn(self.index, payload)
|
|
580
604
|
|