pythonnative 0.35.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/cli/pn.py +24 -6
- 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/mutations.py +21 -0
- 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.35.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.35.0.dist-info/RECORD +0 -155
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
pythonnative/hosts/ios.py
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
"""iOS screen host: one ``UIViewController`` per screen, driven by ``ViewController.swift``."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import sys
|
|
7
|
-
import threading
|
|
8
|
-
from typing import Any, Callable, Dict, Optional, Sequence, Tuple
|
|
9
|
-
|
|
10
|
-
from ..utils import IS_IOS
|
|
11
|
-
from .base import ScreenHost, flush_hosts, log_pn
|
|
12
|
-
|
|
13
|
-
__all__ = ["IOSScreenHost", "drain_ios_scheduled_renders", "forward_lifecycle"]
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
from rubicon.objc import SEL, ObjCClass, ObjCInstance, objc_method
|
|
17
|
-
|
|
18
|
-
_rubicon_available = True
|
|
19
|
-
import gc as _gc
|
|
20
|
-
|
|
21
|
-
_gc.disable()
|
|
22
|
-
except ImportError: # pragma: no cover - only on hosts without the [ios] extra
|
|
23
|
-
_rubicon_available = False
|
|
24
|
-
|
|
25
|
-
# Redirect Python's stdout/stderr through fd 2 so ``print()`` output is
|
|
26
|
-
# visible via ``xcrun simctl launch --console-pty``. Runs before any user
|
|
27
|
-
# module is imported so their top-level prints are captured too.
|
|
28
|
-
if IS_IOS:
|
|
29
|
-
try:
|
|
30
|
-
from .. import _ios_log
|
|
31
|
-
|
|
32
|
-
_ios_log.install()
|
|
33
|
-
except Exception:
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
_REGISTRY: Dict[int, ScreenHost] = {}
|
|
37
|
-
_SCHEDULED: Dict[int, ScreenHost] = {}
|
|
38
|
-
_timer_target: Any = None
|
|
39
|
-
_native_scheduler: Any = None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _objc_addr(obj: Any) -> Optional[int]:
|
|
43
|
-
"""Pointer of an ``ObjCInstance`` as an int (``ptr`` varies by rubicon release)."""
|
|
44
|
-
ptr = getattr(obj, "ptr", None)
|
|
45
|
-
if ptr is None:
|
|
46
|
-
return None
|
|
47
|
-
if isinstance(ptr, (bytes, bytearray)):
|
|
48
|
-
try:
|
|
49
|
-
return int.from_bytes(ptr, byteorder=sys.byteorder, signed=False)
|
|
50
|
-
except Exception:
|
|
51
|
-
return None
|
|
52
|
-
if isinstance(ptr, int):
|
|
53
|
-
return ptr
|
|
54
|
-
value = getattr(ptr, "value", None)
|
|
55
|
-
if isinstance(value, int):
|
|
56
|
-
return value
|
|
57
|
-
try:
|
|
58
|
-
return int(ptr)
|
|
59
|
-
except Exception:
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _publish_color_scheme() -> None:
|
|
64
|
-
try:
|
|
65
|
-
from .. import appearance
|
|
66
|
-
|
|
67
|
-
UITraitCollection = ObjCClass("UITraitCollection")
|
|
68
|
-
style_value = int(UITraitCollection.currentTraitCollection.userInterfaceStyle)
|
|
69
|
-
appearance.set_system_color_scheme("dark" if style_value == 2 else "light")
|
|
70
|
-
except Exception:
|
|
71
|
-
pass
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _flush_scheduled() -> None:
|
|
75
|
-
hosts = list(_SCHEDULED.values())
|
|
76
|
-
_SCHEDULED.clear()
|
|
77
|
-
if hosts:
|
|
78
|
-
log_pn(f"render_scheduler: flushing {len(hosts)} host(s)")
|
|
79
|
-
flush_hosts(hosts)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def drain_ios_scheduled_renders() -> None:
|
|
83
|
-
"""Entry point used by the iOS template to drain pending renders on the main thread."""
|
|
84
|
-
_flush_scheduled()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _wake_native_drain() -> bool:
|
|
88
|
-
"""Ask the template's ``pn_schedule_render_drain`` to drain on the main thread."""
|
|
89
|
-
global _native_scheduler
|
|
90
|
-
try:
|
|
91
|
-
if _native_scheduler is None:
|
|
92
|
-
import ctypes
|
|
93
|
-
|
|
94
|
-
scheduler = ctypes.CDLL(None).pn_schedule_render_drain
|
|
95
|
-
scheduler.restype = None
|
|
96
|
-
scheduler.argtypes = []
|
|
97
|
-
_native_scheduler = scheduler
|
|
98
|
-
_native_scheduler()
|
|
99
|
-
return True
|
|
100
|
-
except Exception as exc:
|
|
101
|
-
log_pn(f"render_scheduler: native iOS wake failed: {exc!r}")
|
|
102
|
-
return False
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def forward_lifecycle(native_addr: int, event: str) -> None:
|
|
106
|
-
"""Forward a Swift ``UIViewController`` lifecycle event to its host.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
native_addr: Pointer of the view controller, as registered by
|
|
110
|
-
``create_screen``.
|
|
111
|
-
event: Host method name (``"on_resume"``, ``"on_layout"``, ...).
|
|
112
|
-
"""
|
|
113
|
-
try:
|
|
114
|
-
key = int(native_addr)
|
|
115
|
-
except Exception as exc:
|
|
116
|
-
log_pn(f"forward_lifecycle: bad native_addr={native_addr!r}: {exc!r}")
|
|
117
|
-
return
|
|
118
|
-
host = _REGISTRY.get(key)
|
|
119
|
-
if host is None:
|
|
120
|
-
log_pn(f"forward_lifecycle: no host for event={event!r} addr={key}")
|
|
121
|
-
return
|
|
122
|
-
handler = getattr(host, event, None)
|
|
123
|
-
if handler is None:
|
|
124
|
-
log_pn(f"forward_lifecycle: host has no {event!r}")
|
|
125
|
-
return
|
|
126
|
-
try:
|
|
127
|
-
handler()
|
|
128
|
-
except Exception as exc:
|
|
129
|
-
log_pn(f"forward_lifecycle: {event!r} raised: {exc!r}")
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if _rubicon_available and IS_IOS:
|
|
133
|
-
NSObject = ObjCClass("NSObject")
|
|
134
|
-
|
|
135
|
-
class _RenderTimerTarget(NSObject): # type: ignore[misc, valid-type]
|
|
136
|
-
@objc_method
|
|
137
|
-
def onRenderTimer_(self, timer: object) -> None:
|
|
138
|
-
_flush_scheduled()
|
|
139
|
-
|
|
140
|
-
def _timer_target_instance() -> Any:
|
|
141
|
-
global _timer_target
|
|
142
|
-
if _timer_target is None:
|
|
143
|
-
target = _RenderTimerTarget.new()
|
|
144
|
-
try:
|
|
145
|
-
target.retain()
|
|
146
|
-
except Exception:
|
|
147
|
-
pass
|
|
148
|
-
_timer_target = target
|
|
149
|
-
return _timer_target
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
class IOSScreenHost(ScreenHost):
|
|
153
|
-
"""Host owned by a ``ViewController``; pushes onto its ``UINavigationController``."""
|
|
154
|
-
|
|
155
|
-
def __init__(self, native_instance: Any, component_path: str, component: Any) -> None:
|
|
156
|
-
if isinstance(native_instance, int):
|
|
157
|
-
try:
|
|
158
|
-
native_instance = ObjCInstance(native_instance)
|
|
159
|
-
except Exception:
|
|
160
|
-
native_instance = None
|
|
161
|
-
super().__init__(native_instance, component_path, component)
|
|
162
|
-
if self.native_instance is not None:
|
|
163
|
-
addr = _objc_addr(self.native_instance)
|
|
164
|
-
if addr is not None:
|
|
165
|
-
_REGISTRY[addr] = self
|
|
166
|
-
|
|
167
|
-
# -- lifecycle ------------------------------------------------------
|
|
168
|
-
|
|
169
|
-
def on_create(self) -> None:
|
|
170
|
-
"""Publish the system color scheme, then mount the root component."""
|
|
171
|
-
_publish_color_scheme()
|
|
172
|
-
super().on_create()
|
|
173
|
-
|
|
174
|
-
def on_layout(self) -> None:
|
|
175
|
-
"""Sync the root view's frame and viewport size after ``viewDidLayoutSubviews``."""
|
|
176
|
-
# ``viewDidLayoutSubviews``: safe-area insets are valid now.
|
|
177
|
-
_publish_color_scheme()
|
|
178
|
-
if self.root_native_view is not None:
|
|
179
|
-
self._sync_root_frame(self.root_native_view)
|
|
180
|
-
self._push_viewport_from_root(self.root_native_view)
|
|
181
|
-
|
|
182
|
-
def on_resume(self) -> None:
|
|
183
|
-
"""Mark the screen focused and refresh the color scheme, root frame, and viewport size."""
|
|
184
|
-
_publish_color_scheme()
|
|
185
|
-
super().on_resume()
|
|
186
|
-
if self.root_native_view is not None:
|
|
187
|
-
self._sync_root_frame(self.root_native_view)
|
|
188
|
-
self._push_viewport_from_root(self.root_native_view)
|
|
189
|
-
|
|
190
|
-
def on_destroy(self) -> None:
|
|
191
|
-
"""Drop this host from the view-controller registry, then tear down the tree."""
|
|
192
|
-
if self.native_instance is not None:
|
|
193
|
-
addr = _objc_addr(self.native_instance)
|
|
194
|
-
if addr is not None:
|
|
195
|
-
_REGISTRY.pop(addr, None)
|
|
196
|
-
super().on_destroy()
|
|
197
|
-
|
|
198
|
-
# -- platform primitives -------------------------------------------
|
|
199
|
-
|
|
200
|
-
def _initial_viewport_size(self) -> Optional[Tuple[float, float]]:
|
|
201
|
-
try:
|
|
202
|
-
if self.native_instance is not None:
|
|
203
|
-
bounds = self.native_instance.view.bounds
|
|
204
|
-
if bounds.size.width > 0 and bounds.size.height > 0:
|
|
205
|
-
return (float(bounds.size.width), float(bounds.size.height))
|
|
206
|
-
except Exception:
|
|
207
|
-
pass
|
|
208
|
-
try:
|
|
209
|
-
bounds = ObjCClass("UIScreen").mainScreen.bounds
|
|
210
|
-
return (float(bounds.size.width), float(bounds.size.height))
|
|
211
|
-
except Exception:
|
|
212
|
-
return None
|
|
213
|
-
|
|
214
|
-
def _schedule_render_async(self) -> bool:
|
|
215
|
-
if self._render_scheduled:
|
|
216
|
-
return True
|
|
217
|
-
self._render_scheduled = True
|
|
218
|
-
_SCHEDULED[id(self)] = self
|
|
219
|
-
if threading.current_thread() is not threading.main_thread():
|
|
220
|
-
if not _wake_native_drain():
|
|
221
|
-
log_pn("request_render: native iOS scheduler unavailable; render remains queued")
|
|
222
|
-
return True
|
|
223
|
-
# Main-queue GCD blocks run in the common run-loop modes, so
|
|
224
|
-
# renders land during scrolls and synthesized test touches; a
|
|
225
|
-
# default-mode NSTimer is starved for the whole gesture.
|
|
226
|
-
from ..runtime import _ensure_libdispatch_loaded, _ios_dispatch_async
|
|
227
|
-
|
|
228
|
-
if _ensure_libdispatch_loaded():
|
|
229
|
-
_ios_dispatch_async(_flush_scheduled)
|
|
230
|
-
return True
|
|
231
|
-
try:
|
|
232
|
-
NSTimer = ObjCClass("NSTimer")
|
|
233
|
-
NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
|
234
|
-
0.0, _timer_target_instance(), SEL("onRenderTimer:"), None, False
|
|
235
|
-
)
|
|
236
|
-
return True
|
|
237
|
-
except Exception as exc:
|
|
238
|
-
self._render_scheduled = False
|
|
239
|
-
_SCHEDULED.pop(id(self), None)
|
|
240
|
-
log_pn(f"request_render: iOS defer failed ({exc!r}); rendering synchronously")
|
|
241
|
-
return False
|
|
242
|
-
|
|
243
|
-
def _view_controller_class(self) -> Any:
|
|
244
|
-
try:
|
|
245
|
-
return ObjCClass("ViewController")
|
|
246
|
-
except Exception:
|
|
247
|
-
pass
|
|
248
|
-
try:
|
|
249
|
-
bundle = ObjCClass("NSBundle").mainBundle
|
|
250
|
-
module_name = bundle.objectForInfoDictionaryKey_("CFBundleName") or bundle.objectForInfoDictionaryKey_(
|
|
251
|
-
"CFBundleExecutable"
|
|
252
|
-
)
|
|
253
|
-
if module_name:
|
|
254
|
-
return ObjCClass(f"{module_name}.ViewController")
|
|
255
|
-
except Exception:
|
|
256
|
-
pass
|
|
257
|
-
raise NameError("ViewController class not found; ensure the Swift class is ObjC-visible")
|
|
258
|
-
|
|
259
|
-
def _make_view_controller(self, component_path: str, args: Dict[str, Any], options: Dict[str, Any]) -> Any:
|
|
260
|
-
vc = self._view_controller_class().alloc().init()
|
|
261
|
-
try:
|
|
262
|
-
vc.setValue_forKey_(component_path, "requestedScreenPath")
|
|
263
|
-
if args:
|
|
264
|
-
vc.setValue_forKey_(json.dumps(args), "requestedScreenArgsJSON")
|
|
265
|
-
except Exception:
|
|
266
|
-
pass
|
|
267
|
-
title = options.get("title")
|
|
268
|
-
if title is not None:
|
|
269
|
-
try:
|
|
270
|
-
vc.setTitle_(str(title))
|
|
271
|
-
except Exception:
|
|
272
|
-
pass
|
|
273
|
-
return vc
|
|
274
|
-
|
|
275
|
-
def _run_nav_op(self, op: Callable[[Any], None]) -> None:
|
|
276
|
-
"""Run ``op(nav)`` unless a transition is mid-flight.
|
|
277
|
-
|
|
278
|
-
UIKit doesn't queue navigation calls: one issued during an
|
|
279
|
-
animation is ignored or misapplied. Dropping it keeps the stack
|
|
280
|
-
consistent; a retry lands on a settled stack.
|
|
281
|
-
"""
|
|
282
|
-
nav = getattr(self.native_instance, "navigationController", None)
|
|
283
|
-
if nav is None:
|
|
284
|
-
raise RuntimeError("No UINavigationController; the template must embed the root in one")
|
|
285
|
-
try:
|
|
286
|
-
coord = nav.transitionCoordinator
|
|
287
|
-
if callable(coord):
|
|
288
|
-
coord = coord()
|
|
289
|
-
if coord is not None:
|
|
290
|
-
log_pn("_run_nav_op: transition in flight; dropping nav op")
|
|
291
|
-
return
|
|
292
|
-
except Exception:
|
|
293
|
-
pass
|
|
294
|
-
op(nav)
|
|
295
|
-
|
|
296
|
-
def _native_push(self, component_path: str, args: Dict[str, Any], options: Dict[str, Any]) -> None:
|
|
297
|
-
vc = self._make_view_controller(component_path, args, options)
|
|
298
|
-
self._run_nav_op(lambda nav: nav.pushViewController_animated_(vc, True))
|
|
299
|
-
|
|
300
|
-
def _native_pop(self, count: int) -> None:
|
|
301
|
-
def op(nav: Any) -> None:
|
|
302
|
-
if count <= 1:
|
|
303
|
-
nav.popViewControllerAnimated_(True)
|
|
304
|
-
return
|
|
305
|
-
controllers = nav.viewControllers
|
|
306
|
-
n = int(controllers.count)
|
|
307
|
-
target_index = max(0, n - 1 - count)
|
|
308
|
-
nav.popToViewController_animated_(controllers.objectAtIndex_(target_index), True)
|
|
309
|
-
|
|
310
|
-
self._run_nav_op(op)
|
|
311
|
-
|
|
312
|
-
def _native_pop_to_root(self) -> None:
|
|
313
|
-
self._run_nav_op(lambda nav: nav.popToRootViewControllerAnimated_(True))
|
|
314
|
-
|
|
315
|
-
def _native_replace(self, component_path: str, args: Dict[str, Any], options: Dict[str, Any]) -> None:
|
|
316
|
-
vc = self._make_view_controller(component_path, args, options)
|
|
317
|
-
|
|
318
|
-
def op(nav: Any) -> None:
|
|
319
|
-
controllers = list(nav.viewControllers)
|
|
320
|
-
controllers[-1] = vc
|
|
321
|
-
nav.setViewControllers_animated_(controllers, True)
|
|
322
|
-
|
|
323
|
-
self._run_nav_op(op)
|
|
324
|
-
|
|
325
|
-
def _native_reset(self, component_path: str, screens: Sequence[Tuple[Dict[str, Any], Dict[str, Any]]]) -> None:
|
|
326
|
-
vcs = [self._make_view_controller(component_path, args, options) for args, options in screens]
|
|
327
|
-
|
|
328
|
-
def op(nav: Any) -> None:
|
|
329
|
-
root = nav.viewControllers.objectAtIndex_(0)
|
|
330
|
-
nav.setViewControllers_animated_([root, *vcs], True)
|
|
331
|
-
|
|
332
|
-
self._run_nav_op(op)
|
|
333
|
-
|
|
334
|
-
def _native_set_options(self, options: Dict[str, Any]) -> None:
|
|
335
|
-
title = options.get("title")
|
|
336
|
-
if title is None or self.native_instance is None:
|
|
337
|
-
return
|
|
338
|
-
try:
|
|
339
|
-
self.native_instance.setTitle_(str(title))
|
|
340
|
-
except Exception as exc:
|
|
341
|
-
log_pn(f"set_screen_options: setTitle failed: {exc!r}")
|
|
342
|
-
|
|
343
|
-
def _attach_root(self, native_view: Any) -> None:
|
|
344
|
-
self.native_instance.view.addSubview_(native_view)
|
|
345
|
-
# Frame-based layout for the root so the layout engine's frames
|
|
346
|
-
# aren't fought by Auto Layout.
|
|
347
|
-
try:
|
|
348
|
-
native_view.setTranslatesAutoresizingMaskIntoConstraints_(True)
|
|
349
|
-
native_view.setAutoresizingMask_(2 | 16) # FlexibleWidth | FlexibleHeight
|
|
350
|
-
except Exception:
|
|
351
|
-
pass
|
|
352
|
-
self._sync_root_frame(native_view)
|
|
353
|
-
self._push_viewport_from_root(native_view)
|
|
354
|
-
|
|
355
|
-
def _detach_root(self, native_view: Any) -> None:
|
|
356
|
-
try:
|
|
357
|
-
native_view.removeFromSuperview()
|
|
358
|
-
except Exception:
|
|
359
|
-
pass
|
|
360
|
-
|
|
361
|
-
# -- viewport plumbing ---------------------------------------------
|
|
362
|
-
|
|
363
|
-
def _sync_root_frame(self, native_view: Any) -> None:
|
|
364
|
-
"""Position the root below the top safe area, full-bleed at the bottom.
|
|
365
|
-
|
|
366
|
-
The bottom inset is published to ``platform_metrics`` instead of
|
|
367
|
-
being subtracted, so a tab bar can reach the home indicator;
|
|
368
|
-
content that needs it opts in via ``SafeAreaView``.
|
|
369
|
-
"""
|
|
370
|
-
root_view = self.native_instance.view
|
|
371
|
-
if root_view is None:
|
|
372
|
-
return
|
|
373
|
-
try:
|
|
374
|
-
bounds, insets = root_view.bounds, root_view.safeAreaInsets
|
|
375
|
-
top, left, right, bottom = float(insets.top), float(insets.left), float(insets.right), float(insets.bottom)
|
|
376
|
-
w = max(0.0, float(bounds.size.width) - left - right)
|
|
377
|
-
h = max(0.0, float(bounds.size.height) - top)
|
|
378
|
-
try:
|
|
379
|
-
from .. import platform_metrics
|
|
380
|
-
|
|
381
|
-
platform_metrics.set_safe_area_insets(0.0, left, bottom, right)
|
|
382
|
-
except Exception:
|
|
383
|
-
pass
|
|
384
|
-
if w > 0 and h > 0:
|
|
385
|
-
native_view.setFrame_(((left, top), (w, h)))
|
|
386
|
-
return
|
|
387
|
-
except Exception as exc:
|
|
388
|
-
log_pn(f"sync_root_frame: insets path failed: {exc!r}")
|
|
389
|
-
try:
|
|
390
|
-
bounds = root_view.bounds
|
|
391
|
-
native_view.setFrame_(((0, 0), (float(bounds.size.width), float(bounds.size.height))))
|
|
392
|
-
except Exception as exc:
|
|
393
|
-
log_pn(f"sync_root_frame: bounds fallback failed: {exc!r}")
|
|
394
|
-
|
|
395
|
-
def _push_viewport_from_root(self, native_view: Any) -> None:
|
|
396
|
-
try:
|
|
397
|
-
bounds = native_view.bounds
|
|
398
|
-
w, h = float(bounds.size.width), float(bounds.size.height)
|
|
399
|
-
if w <= 0 or h <= 0:
|
|
400
|
-
screen = ObjCClass("UIScreen").mainScreen.bounds
|
|
401
|
-
w, h = float(screen.size.width), float(screen.size.height)
|
|
402
|
-
self.set_viewport_size(w, h)
|
|
403
|
-
except Exception as exc:
|
|
404
|
-
log_pn(f"push_viewport: failed: {exc!r}")
|