pythonnative 0.36.0__py3-none-any.whl → 0.37.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 +6 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +34 -88
- 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/components/layout.py +65 -3
- pythonnative/gestures.py +13 -10
- 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 +30 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +20 -84
- pythonnative/native_modules/biometrics.py +11 -124
- pythonnative/native_modules/camera.py +15 -291
- pythonnative/native_modules/clipboard.py +14 -109
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +15 -21
- pythonnative/native_modules/haptics.py +19 -129
- pythonnative/native_modules/linking.py +24 -137
- pythonnative/native_modules/location.py +28 -180
- pythonnative/native_modules/net_info.py +25 -135
- pythonnative/native_modules/notifications.py +36 -257
- pythonnative/native_modules/permissions.py +20 -184
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +18 -155
- pythonnative/native_modules/share.py +8 -113
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +0 -3
- pythonnative/project/builder.py +38 -16
- pythonnative/project/config.py +12 -2
- pythonnative/project/plugins.py +366 -0
- 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/templates/android_template/app/build.gradle +6 -9
- 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/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 +88 -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 +251 -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 +38 -46
- 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 +47 -1
- pythonnative/utils.py +4 -82
- pythonnative/virtual_rows.py +3 -3
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
- pythonnative-0.37.0.dist-info/RECORD +263 -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.37.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
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
|
-----------
|
|
@@ -518,14 +521,14 @@ def serialize_gestures(
|
|
|
518
521
|
|
|
519
522
|
|
|
520
523
|
# ======================================================================
|
|
521
|
-
# Pure-Python recognition engine (
|
|
524
|
+
# Pure-Python recognition engine (desktop backend + reference semantics)
|
|
522
525
|
# ======================================================================
|
|
523
526
|
#
|
|
524
|
-
# iOS
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
#
|
|
527
|
+
# iOS and Android recognize natively. The desktop preview receives raw
|
|
528
|
+
# Tk pointer streams instead, which this arbiter turns into the same
|
|
529
|
+
# GestureEvent payloads. Keeping it in pure Python makes the state
|
|
530
|
+
# machines unit-testable with scripted event sequences; the Kotlin
|
|
531
|
+
# arbiter mirrors this file so the two stay in lockstep.
|
|
529
532
|
|
|
530
533
|
EmitFn = Callable[[int, Dict[str, Any]], None]
|
|
531
534
|
"""``emit(gesture_index, payload)``: the arbiter's output channel."""
|
pythonnative/hooks.py
CHANGED
|
@@ -67,6 +67,7 @@ from typing import (
|
|
|
67
67
|
|
|
68
68
|
from . import diagnostics
|
|
69
69
|
from .element import Element, Node
|
|
70
|
+
from .platform_metrics import SafeAreaInsets, WindowDimensions
|
|
70
71
|
from .scheduler import TransitionQueue, in_transition, run_in_transition, schedule_trigger
|
|
71
72
|
from .suspense import CoroDriver, Resource
|
|
72
73
|
|
|
@@ -1235,27 +1236,39 @@ def use_mutation(
|
|
|
1235
1236
|
from .runtime import run_async
|
|
1236
1237
|
|
|
1237
1238
|
state, set_state = use_state(lambda: MutationState[T]())
|
|
1239
|
+
# The trigger is identity-stable across renders (like ``set_state``)
|
|
1240
|
+
# so it can sit in effect deps or be passed to memoized children;
|
|
1241
|
+
# it always calls the latest ``mutator``.
|
|
1242
|
+
latest = use_ref(mutator)
|
|
1243
|
+
latest.current = mutator
|
|
1244
|
+
|
|
1245
|
+
def _make_mutate() -> Callable[..., MutationCall[T]]:
|
|
1246
|
+
def mutate(*args: Any, **kwargs: Any) -> MutationCall[T]:
|
|
1247
|
+
set_state(lambda s: replace(s, loading=True, error=None))
|
|
1248
|
+
fn = latest.current
|
|
1238
1249
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1250
|
+
async def _runner() -> T:
|
|
1251
|
+
try:
|
|
1252
|
+
data = await fn(*args, **kwargs)
|
|
1253
|
+
set_state(lambda s: replace(s, data=data, loading=False, error=None))
|
|
1254
|
+
return data
|
|
1255
|
+
except asyncio.CancelledError:
|
|
1256
|
+
set_state(lambda s: replace(s, loading=False))
|
|
1257
|
+
raise
|
|
1258
|
+
except BaseException as exc:
|
|
1259
|
+
failure = exc
|
|
1260
|
+
set_state(lambda s: replace(s, loading=False, error=failure))
|
|
1261
|
+
raise
|
|
1241
1262
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
data = await mutator(*args, **kwargs)
|
|
1245
|
-
set_state(lambda s: replace(s, data=data, loading=False, error=None))
|
|
1246
|
-
return data
|
|
1247
|
-
except asyncio.CancelledError:
|
|
1248
|
-
set_state(lambda s: replace(s, loading=False))
|
|
1249
|
-
raise
|
|
1250
|
-
except BaseException as exc:
|
|
1251
|
-
failure = exc
|
|
1252
|
-
set_state(lambda s: replace(s, loading=False, error=failure))
|
|
1253
|
-
raise
|
|
1263
|
+
future = run_async(_runner())
|
|
1264
|
+
return MutationCall[T](future)
|
|
1254
1265
|
|
|
1255
|
-
|
|
1256
|
-
return MutationCall[T](future)
|
|
1266
|
+
return mutate
|
|
1257
1267
|
|
|
1258
|
-
|
|
1268
|
+
mutate_ref: Ref[Optional[Callable[..., MutationCall[T]]]] = use_ref(None)
|
|
1269
|
+
if mutate_ref.current is None:
|
|
1270
|
+
mutate_ref.current = _make_mutate()
|
|
1271
|
+
return state, mutate_ref.current
|
|
1259
1272
|
|
|
1260
1273
|
|
|
1261
1274
|
# ======================================================================
|
|
@@ -1294,7 +1307,7 @@ def use_subscription(subscribe: Callable[[Callable[[], None]], Callable[[], None
|
|
|
1294
1307
|
return get_snapshot()
|
|
1295
1308
|
|
|
1296
1309
|
|
|
1297
|
-
def use_window_dimensions() ->
|
|
1310
|
+
def use_window_dimensions() -> WindowDimensions:
|
|
1298
1311
|
"""Return the current viewport size and re-render when it changes.
|
|
1299
1312
|
|
|
1300
1313
|
Equivalent to React Native's ``useWindowDimensions``. The values
|
|
@@ -1302,26 +1315,29 @@ def use_window_dimensions() -> Dict[str, float]:
|
|
|
1302
1315
|
size (initial layout, rotation, multitasking split-view).
|
|
1303
1316
|
|
|
1304
1317
|
Returns:
|
|
1305
|
-
A
|
|
1318
|
+
A [`WindowDimensions`][pythonnative.platform_metrics.WindowDimensions]
|
|
1319
|
+
named tuple with ``width`` and ``height`` floats in layout
|
|
1306
1320
|
units (pt on iOS, dp on Android). Both are ``0.0`` until the
|
|
1307
|
-
screen host has run its first layout pass.
|
|
1321
|
+
screen host has run its first layout pass. Being a tuple, it
|
|
1322
|
+
unpacks (``width, height = pn.use_window_dimensions()``) and
|
|
1323
|
+
compares by value.
|
|
1308
1324
|
|
|
1309
1325
|
Raises:
|
|
1310
1326
|
RuntimeError: If called outside a ``@component`` function.
|
|
1311
1327
|
"""
|
|
1312
1328
|
from . import platform_metrics
|
|
1313
1329
|
|
|
1314
|
-
|
|
1315
|
-
return {"width": dims.width, "height": dims.height}
|
|
1330
|
+
return use_subscription(platform_metrics.subscribe, platform_metrics.get_window_dimensions)
|
|
1316
1331
|
|
|
1317
1332
|
|
|
1318
|
-
def use_safe_area_insets() ->
|
|
1333
|
+
def use_safe_area_insets() -> SafeAreaInsets:
|
|
1319
1334
|
"""Return the current safe-area insets and re-render on change.
|
|
1320
1335
|
|
|
1321
1336
|
Mirrors ``react-native-safe-area-context``'s ``useSafeAreaInsets``.
|
|
1322
1337
|
|
|
1323
1338
|
Returns:
|
|
1324
|
-
A
|
|
1339
|
+
A [`SafeAreaInsets`][pythonnative.platform_metrics.SafeAreaInsets]
|
|
1340
|
+
named tuple with ``top``, ``left``, ``bottom``, and ``right``
|
|
1325
1341
|
floats in layout units (pt on iOS, dp on Android).
|
|
1326
1342
|
|
|
1327
1343
|
Raises:
|
|
@@ -1329,8 +1345,7 @@ def use_safe_area_insets() -> Dict[str, float]:
|
|
|
1329
1345
|
"""
|
|
1330
1346
|
from . import platform_metrics
|
|
1331
1347
|
|
|
1332
|
-
|
|
1333
|
-
return {"top": insets.top, "bottom": insets.bottom, "left": insets.left, "right": insets.right}
|
|
1348
|
+
return use_subscription(platform_metrics.subscribe, platform_metrics.get_safe_area_insets)
|
|
1334
1349
|
|
|
1335
1350
|
|
|
1336
1351
|
def use_keyboard_height() -> float:
|
pythonnative/hosts/__init__.py
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
"""Screen hosts: the bridge between a native screen and the reconciler.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
runtime ([`AndroidScreenHost`][pythonnative.hosts.android.AndroidScreenHost],
|
|
12
|
-
[`IOSScreenHost`][pythonnative.hosts.ios.IOSScreenHost],
|
|
13
|
-
[`DesktopScreenHost`][pythonnative.hosts.desktop.DesktopScreenHost]);
|
|
14
|
-
the headless base [`ScreenHost`][pythonnative.hosts.ScreenHost] is used
|
|
15
|
-
in unit tests with a fake backend.
|
|
3
|
+
On device the native runtime creates screens through the bridge
|
|
4
|
+
(``callback("host", screen_id, "create", {...})``), which lands in
|
|
5
|
+
[`NativeScreenHost`][pythonnative.hosts.native.NativeScreenHost]. Off device,
|
|
6
|
+
``pn preview`` creates a
|
|
7
|
+
[`DesktopScreenHost`][pythonnative.hosts.desktop.DesktopScreenHost]
|
|
8
|
+
through [`create_screen`][pythonnative.hosts.create_screen], and unit
|
|
9
|
+
tests use the headless base [`ScreenHost`][pythonnative.hosts.ScreenHost]
|
|
10
|
+
with a fake backend.
|
|
16
11
|
"""
|
|
17
12
|
|
|
18
13
|
from __future__ import annotations
|
|
@@ -27,18 +22,14 @@ __all__ = ["ScreenHost", "create_screen", "host_class", "import_component"]
|
|
|
27
22
|
|
|
28
23
|
def host_class() -> Type[ScreenHost]:
|
|
29
24
|
"""The host class for the current runtime."""
|
|
30
|
-
if IS_ANDROID:
|
|
31
|
-
from .
|
|
25
|
+
if IS_ANDROID or IS_IOS:
|
|
26
|
+
from .native import NativeScreenHost
|
|
32
27
|
|
|
33
|
-
return
|
|
28
|
+
return NativeScreenHost
|
|
34
29
|
if IS_DESKTOP:
|
|
35
30
|
from .desktop import DesktopScreenHost
|
|
36
31
|
|
|
37
32
|
return DesktopScreenHost
|
|
38
|
-
if IS_IOS:
|
|
39
|
-
from .ios import IOSScreenHost
|
|
40
|
-
|
|
41
|
-
return IOSScreenHost
|
|
42
33
|
return ScreenHost
|
|
43
34
|
|
|
44
35
|
|
|
@@ -49,8 +40,8 @@ def create_screen(component_path: str, native_instance: Any = None, args_json: O
|
|
|
49
40
|
component_path: ``"app.main"`` (the module's ``App`` is used) or a
|
|
50
41
|
dotted path like ``"app.main.RootScreen"``. Imported lazily
|
|
51
42
|
so the dev server can reload it.
|
|
52
|
-
native_instance: The platform object owning the screen
|
|
53
|
-
|
|
43
|
+
native_instance: The platform object owning the screen (the
|
|
44
|
+
integer screen id on device, ``DesktopApp`` in the preview).
|
|
54
45
|
args_json: Optional JSON launch arguments (pushed screens
|
|
55
46
|
receive their navigation history here).
|
|
56
47
|
|
|
@@ -64,20 +55,6 @@ def create_screen(component_path: str, native_instance: Any = None, args_json: O
|
|
|
64
55
|
return host
|
|
65
56
|
|
|
66
57
|
|
|
67
|
-
def drain_ios_scheduled_renders() -> None:
|
|
68
|
-
"""Drain deferred iOS renders (called by the Swift template on the main thread)."""
|
|
69
|
-
from .ios import drain_ios_scheduled_renders as drain
|
|
70
|
-
|
|
71
|
-
drain()
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def forward_lifecycle(native_addr: int, event: str) -> None:
|
|
75
|
-
"""Forward a Swift view-controller lifecycle event to its host."""
|
|
76
|
-
from .ios import forward_lifecycle as forward
|
|
77
|
-
|
|
78
|
-
forward(native_addr, event)
|
|
79
|
-
|
|
80
|
-
|
|
81
58
|
def drain_desktop_scheduled_renders() -> None:
|
|
82
59
|
"""Drain deferred desktop renders (called by the preview's Tk loop)."""
|
|
83
60
|
from .desktop import drain_desktop_scheduled_renders as drain
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"""On-device screen host, shared by iOS and Android.
|
|
2
|
+
|
|
3
|
+
One [`NativeScreenHost`][pythonnative.hosts.native.NativeScreenHost]
|
|
4
|
+
exists per native screen (``UIViewController`` / ``Fragment``). The
|
|
5
|
+
native side never holds Python objects: screens are addressed by an
|
|
6
|
+
integer id it assigns, lifecycle arrives through
|
|
7
|
+
``callback("host", screen_id, event, payload)`` (routed here by
|
|
8
|
+
[`dispatch_host_event`][pythonnative.hosts.native.dispatch_host_event]),
|
|
9
|
+
and everything the host needs from the platform goes out through the
|
|
10
|
+
``Host`` native module (``attach_root``, ``push``, ``pop``, ...). See
|
|
11
|
+
``docs/concepts/bridge.md`` for the payloads.
|
|
12
|
+
|
|
13
|
+
Because native reports viewport size, safe-area insets, keyboard
|
|
14
|
+
height, and color scheme *with* each lifecycle event, this module
|
|
15
|
+
never queries the platform; it only publishes what it is told.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import threading
|
|
22
|
+
from typing import Any, Dict, Optional, Sequence, Tuple
|
|
23
|
+
|
|
24
|
+
from .. import appearance, diagnostics, platform_metrics
|
|
25
|
+
from ..bridge import codec
|
|
26
|
+
from ..native_modules.registry import native_module
|
|
27
|
+
from .base import ScreenHost, flush_hosts, log_pn
|
|
28
|
+
|
|
29
|
+
__all__ = ["NativeScreenHost", "dispatch_host_event", "host_for_screen", "live_hosts"]
|
|
30
|
+
|
|
31
|
+
_HOSTS: Dict[int, "NativeScreenHost"] = {}
|
|
32
|
+
_SCHEDULED: Dict[int, ScreenHost] = {}
|
|
33
|
+
_flush_pending = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def host_for_screen(screen_id: int) -> Optional["NativeScreenHost"]:
|
|
37
|
+
"""Return the host registered for ``screen_id`` (``None`` if unknown)."""
|
|
38
|
+
return _HOSTS.get(int(screen_id))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def live_hosts() -> Sequence["NativeScreenHost"]:
|
|
42
|
+
"""Every host currently registered, in creation order."""
|
|
43
|
+
return list(_HOSTS.values())
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _host_module() -> Any:
|
|
47
|
+
return native_module("Host")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ======================================================================
|
|
51
|
+
# Deferred renders
|
|
52
|
+
# ======================================================================
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _flush_scheduled() -> None:
|
|
56
|
+
global _flush_pending
|
|
57
|
+
_flush_pending = False
|
|
58
|
+
hosts = list(_SCHEDULED.values())
|
|
59
|
+
_SCHEDULED.clear()
|
|
60
|
+
if hosts:
|
|
61
|
+
log_pn(f"render_scheduler: flushing {len(hosts)} host(s)")
|
|
62
|
+
flush_hosts(hosts)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _request_flush() -> None:
|
|
66
|
+
"""Queue one main-thread flush of every scheduled host."""
|
|
67
|
+
global _flush_pending
|
|
68
|
+
if _flush_pending:
|
|
69
|
+
return
|
|
70
|
+
_flush_pending = True
|
|
71
|
+
from ..bridge import post_to_main
|
|
72
|
+
|
|
73
|
+
post_to_main(_flush_scheduled)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ======================================================================
|
|
77
|
+
# Payload helpers
|
|
78
|
+
# ======================================================================
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _publish_metrics(payload: Any) -> Tuple[float, float]:
|
|
82
|
+
"""Publish viewport, insets, keyboard, and color scheme from a host payload.
|
|
83
|
+
|
|
84
|
+
Returns the ``(width, height)`` it found (``0, 0`` when absent).
|
|
85
|
+
"""
|
|
86
|
+
if not isinstance(payload, dict):
|
|
87
|
+
return (0.0, 0.0)
|
|
88
|
+
width = float(payload.get("width") or 0.0)
|
|
89
|
+
height = float(payload.get("height") or 0.0)
|
|
90
|
+
insets = payload.get("insets")
|
|
91
|
+
if isinstance(insets, dict):
|
|
92
|
+
platform_metrics.set_safe_area_insets(
|
|
93
|
+
float(insets.get("top") or 0.0),
|
|
94
|
+
float(insets.get("left") or 0.0),
|
|
95
|
+
float(insets.get("bottom") or 0.0),
|
|
96
|
+
float(insets.get("right") or 0.0),
|
|
97
|
+
)
|
|
98
|
+
if "keyboard_height" in payload:
|
|
99
|
+
platform_metrics.set_keyboard_height(float(payload.get("keyboard_height") or 0.0))
|
|
100
|
+
scheme = payload.get("color_scheme")
|
|
101
|
+
if scheme in ("light", "dark"):
|
|
102
|
+
appearance.set_system_color_scheme(str(scheme))
|
|
103
|
+
if width > 0 and height > 0:
|
|
104
|
+
platform_metrics.set_window_dimensions(width, height)
|
|
105
|
+
return (width, height)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ======================================================================
|
|
109
|
+
# Host
|
|
110
|
+
# ======================================================================
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class NativeScreenHost(ScreenHost):
|
|
114
|
+
"""Screen host addressed by an integer ``screen_id`` on the bridge."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, screen_id: int, component_path: str, component: Any) -> None:
|
|
117
|
+
super().__init__(int(screen_id), component_path, component)
|
|
118
|
+
self.screen_id = int(screen_id)
|
|
119
|
+
self._pending_viewport: Optional[Tuple[float, float]] = None
|
|
120
|
+
_HOSTS[self.screen_id] = self
|
|
121
|
+
|
|
122
|
+
# -- lifecycle ------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def on_destroy(self) -> None:
|
|
125
|
+
"""Forget the screen id, then tear down the tree."""
|
|
126
|
+
_HOSTS.pop(self.screen_id, None)
|
|
127
|
+
_SCHEDULED.pop(id(self), None)
|
|
128
|
+
super().on_destroy()
|
|
129
|
+
|
|
130
|
+
def apply_metrics(self, payload: Any) -> None:
|
|
131
|
+
"""Publish the metrics carried by a host event and resize the viewport."""
|
|
132
|
+
width, height = _publish_metrics(payload)
|
|
133
|
+
if width > 0 and height > 0:
|
|
134
|
+
self._pending_viewport = (width, height)
|
|
135
|
+
self.set_viewport_size(width, height)
|
|
136
|
+
|
|
137
|
+
# -- platform primitives -------------------------------------------
|
|
138
|
+
|
|
139
|
+
def _initial_viewport_size(self) -> Optional[Tuple[float, float]]:
|
|
140
|
+
if self._pending_viewport is not None:
|
|
141
|
+
return self._pending_viewport
|
|
142
|
+
dims = platform_metrics.get_window_dimensions()
|
|
143
|
+
if dims.width > 0 and dims.height > 0:
|
|
144
|
+
return (dims.width, dims.height)
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
def _schedule_render_async(self) -> bool:
|
|
148
|
+
if self._render_scheduled:
|
|
149
|
+
return True
|
|
150
|
+
self._render_scheduled = True
|
|
151
|
+
_SCHEDULED[id(self)] = self
|
|
152
|
+
try:
|
|
153
|
+
_request_flush()
|
|
154
|
+
except Exception as exc:
|
|
155
|
+
self._render_scheduled = False
|
|
156
|
+
_SCHEDULED.pop(id(self), None)
|
|
157
|
+
log_pn(f"request_render: bridge defer failed ({exc!r}); rendering synchronously")
|
|
158
|
+
return False
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
def _attach_root(self, native_view: Any) -> None:
|
|
162
|
+
tag = getattr(native_view, "tag", None)
|
|
163
|
+
if tag is None:
|
|
164
|
+
return
|
|
165
|
+
# Native answers with the viewport the root now occupies, which
|
|
166
|
+
# is the earliest exact size available on iOS.
|
|
167
|
+
result = _host_module().call("attach_root", screen=self.screen_id, tag=int(tag))
|
|
168
|
+
if isinstance(result, dict):
|
|
169
|
+
self.apply_metrics(result)
|
|
170
|
+
|
|
171
|
+
def _detach_root(self, native_view: Any) -> None:
|
|
172
|
+
tag = getattr(native_view, "tag", None)
|
|
173
|
+
if tag is None:
|
|
174
|
+
return
|
|
175
|
+
try:
|
|
176
|
+
_host_module().call("detach_root", screen=self.screen_id, tag=int(tag))
|
|
177
|
+
except Exception:
|
|
178
|
+
diagnostics.swallowed("hosts.native.detach_root")
|
|
179
|
+
|
|
180
|
+
def _native_push(self, component_path: str, args: Dict[str, Any], options: Dict[str, Any]) -> None:
|
|
181
|
+
_host_module().call(
|
|
182
|
+
"push",
|
|
183
|
+
screen=self.screen_id,
|
|
184
|
+
path=component_path,
|
|
185
|
+
args=json.dumps(args) if args else None,
|
|
186
|
+
options=codec.to_jsonable(options or {}),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def _native_pop(self, count: int) -> None:
|
|
190
|
+
_host_module().call("pop", screen=self.screen_id, count=int(count))
|
|
191
|
+
|
|
192
|
+
def _native_pop_to_root(self) -> None:
|
|
193
|
+
_host_module().call("pop_to_root", screen=self.screen_id)
|
|
194
|
+
|
|
195
|
+
def _native_replace(self, component_path: str, args: Dict[str, Any], options: Dict[str, Any]) -> None:
|
|
196
|
+
_host_module().call(
|
|
197
|
+
"replace",
|
|
198
|
+
screen=self.screen_id,
|
|
199
|
+
path=component_path,
|
|
200
|
+
args=json.dumps(args) if args else None,
|
|
201
|
+
options=codec.to_jsonable(options or {}),
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def _native_reset(self, component_path: str, screens: Sequence[Tuple[Dict[str, Any], Dict[str, Any]]]) -> None:
|
|
205
|
+
_host_module().call(
|
|
206
|
+
"reset",
|
|
207
|
+
screen=self.screen_id,
|
|
208
|
+
path=component_path,
|
|
209
|
+
screens=[
|
|
210
|
+
{
|
|
211
|
+
"path": component_path,
|
|
212
|
+
"args": json.dumps(args) if args else None,
|
|
213
|
+
"options": codec.to_jsonable(options or {}),
|
|
214
|
+
}
|
|
215
|
+
for args, options in screens
|
|
216
|
+
],
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def _native_set_options(self, options: Dict[str, Any]) -> None:
|
|
220
|
+
_host_module().call("set_options", screen=self.screen_id, options=codec.to_jsonable(options or {}))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# ======================================================================
|
|
224
|
+
# Inbound host events
|
|
225
|
+
# ======================================================================
|
|
226
|
+
|
|
227
|
+
_create_lock = threading.Lock()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def dispatch_host_event(screen_id: int, event: str, payload: Any) -> Optional[str]:
|
|
231
|
+
"""Route ``callback("host", screen_id, event, payload)`` to a host.
|
|
232
|
+
|
|
233
|
+
Events (payloads are what ``PNViewController`` and
|
|
234
|
+
``PNScreenFragment`` send):
|
|
235
|
+
|
|
236
|
+
- ``create`` ``{"path", "args", "dev_root", "restored_state",
|
|
237
|
+
...metrics}``: import the component, create the host, and mount.
|
|
238
|
+
``path`` is the dotted component path (``None`` means the app
|
|
239
|
+
entry module), ``args`` a JSON string, ``dev_root`` the hot-reload
|
|
240
|
+
overlay directory for debug builds. Returns ``{"root": tag}``.
|
|
241
|
+
- ``start`` / ``resume`` / ``pause`` / ``stop`` / ``destroy``:
|
|
242
|
+
lifecycle. ``resume`` and ``layout`` carry metrics.
|
|
243
|
+
- ``layout`` ``{width, height, insets, keyboard_height, color_scheme}``.
|
|
244
|
+
- ``appearance`` ``{"color_scheme"}``.
|
|
245
|
+
- ``back_pressed``: returns ``"true"`` when a handler consumed it.
|
|
246
|
+
- ``hot_reload_tick``: poll the reload manifest; returns ``"true"``
|
|
247
|
+
when a reload ran.
|
|
248
|
+
- ``save_state`` / ``restore_state``: instance-state hooks.
|
|
249
|
+
- ``flush``: run deferred renders now.
|
|
250
|
+
|
|
251
|
+
Returns a JSON string for request-style events, else ``None``.
|
|
252
|
+
"""
|
|
253
|
+
screen_id = int(screen_id)
|
|
254
|
+
if event == "create":
|
|
255
|
+
return _create(screen_id, payload if isinstance(payload, dict) else {})
|
|
256
|
+
if event == "flush":
|
|
257
|
+
_flush_scheduled()
|
|
258
|
+
return None
|
|
259
|
+
if event == "appearance":
|
|
260
|
+
_publish_metrics(payload)
|
|
261
|
+
return None
|
|
262
|
+
host = _HOSTS.get(screen_id)
|
|
263
|
+
if host is None:
|
|
264
|
+
log_pn(f"dispatch_host_event: no host for screen={screen_id} event={event!r}")
|
|
265
|
+
return None
|
|
266
|
+
if event == "layout":
|
|
267
|
+
host.apply_metrics(payload)
|
|
268
|
+
host.on_layout()
|
|
269
|
+
return None
|
|
270
|
+
if event == "resume":
|
|
271
|
+
host.apply_metrics(payload)
|
|
272
|
+
host.on_resume()
|
|
273
|
+
return None
|
|
274
|
+
if event == "back_pressed":
|
|
275
|
+
return "true" if host.on_back_pressed() else "false"
|
|
276
|
+
if event in ("hot_reload_tick", "tick"):
|
|
277
|
+
return "true" if host.hot_reload_tick() else "false"
|
|
278
|
+
if event == "save_state":
|
|
279
|
+
host.on_save_instance_state()
|
|
280
|
+
return None
|
|
281
|
+
if event == "restore_state":
|
|
282
|
+
host.on_restore_instance_state()
|
|
283
|
+
return None
|
|
284
|
+
if event == "destroy":
|
|
285
|
+
host.on_destroy()
|
|
286
|
+
return None
|
|
287
|
+
handler = getattr(host, f"on_{event}", None)
|
|
288
|
+
if handler is None:
|
|
289
|
+
log_pn(f"dispatch_host_event: unknown event {event!r}")
|
|
290
|
+
return None
|
|
291
|
+
handler()
|
|
292
|
+
return None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _create(screen_id: int, payload: Dict[str, Any]) -> Optional[str]:
|
|
296
|
+
from . import import_component
|
|
297
|
+
|
|
298
|
+
component_path = str(payload.get("path") or payload.get("component") or _entry_module())
|
|
299
|
+
with _create_lock:
|
|
300
|
+
existing = _HOSTS.get(screen_id)
|
|
301
|
+
if existing is not None:
|
|
302
|
+
existing.apply_metrics(payload)
|
|
303
|
+
existing.on_create()
|
|
304
|
+
return _root_json(existing)
|
|
305
|
+
component = import_component(component_path)
|
|
306
|
+
host = NativeScreenHost(screen_id, component_path, component)
|
|
307
|
+
args = payload.get("args")
|
|
308
|
+
if args:
|
|
309
|
+
host.set_args(args)
|
|
310
|
+
dev_root = payload.get("dev_root")
|
|
311
|
+
if dev_root:
|
|
312
|
+
from .. import hot_reload
|
|
313
|
+
|
|
314
|
+
host.enable_hot_reload(hot_reload.manifest_path_for(str(dev_root)), str(dev_root))
|
|
315
|
+
if not (payload.get("width") and payload.get("height")):
|
|
316
|
+
# ``create`` is sent before the first layout pass; ask native for
|
|
317
|
+
# its best guess so the first commit lays out at the right size.
|
|
318
|
+
try:
|
|
319
|
+
viewport = _host_module().call("viewport", screen=screen_id)
|
|
320
|
+
except Exception:
|
|
321
|
+
viewport = None
|
|
322
|
+
if isinstance(viewport, dict):
|
|
323
|
+
payload = {**viewport, **payload}
|
|
324
|
+
_publish_metrics(payload)
|
|
325
|
+
width = float(payload.get("width") or 0.0)
|
|
326
|
+
height = float(payload.get("height") or 0.0)
|
|
327
|
+
if width > 0 and height > 0:
|
|
328
|
+
host._pending_viewport = (width, height)
|
|
329
|
+
host.on_create()
|
|
330
|
+
return _root_json(host)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _entry_module() -> str:
|
|
334
|
+
import os
|
|
335
|
+
|
|
336
|
+
return os.environ.get("PN_ENTRY_MODULE") or "app.main"
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _root_json(host: ScreenHost) -> str:
|
|
340
|
+
tag = getattr(host.root_native_view, "tag", None)
|
|
341
|
+
return codec.dumps({"root": None if tag is None else int(tag)})
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _reset_for_tests() -> None:
|
|
345
|
+
global _flush_pending
|
|
346
|
+
_HOSTS.clear()
|
|
347
|
+
_SCHEDULED.clear()
|
|
348
|
+
_flush_pending = False
|
pythonnative/images.py
CHANGED
|
@@ -38,13 +38,14 @@ def _default_cache_dir() -> str:
|
|
|
38
38
|
backups). Desktop: a per-user directory under the system temp dir.
|
|
39
39
|
"""
|
|
40
40
|
try:
|
|
41
|
-
from .utils import IS_ANDROID
|
|
41
|
+
from .utils import IS_ANDROID, IS_IOS
|
|
42
42
|
|
|
43
|
-
if IS_ANDROID:
|
|
44
|
-
from .
|
|
43
|
+
if IS_ANDROID or IS_IOS:
|
|
44
|
+
from .native_modules.registry import native_module
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
info = native_module("Device").call("info")
|
|
47
|
+
if isinstance(info, dict) and info.get("cache_dir"):
|
|
48
|
+
return os.path.join(str(info["cache_dir"]), "pn_images")
|
|
48
49
|
except Exception:
|
|
49
50
|
pass
|
|
50
51
|
home = os.path.expanduser("~")
|