pythonnative 0.22.1__py3-none-any.whl → 0.23.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.
Files changed (25) hide show
  1. pythonnative/__init__.py +16 -2
  2. pythonnative/appearance.py +124 -0
  3. pythonnative/components.py +708 -38
  4. pythonnative/hooks.py +41 -0
  5. pythonnative/images.py +196 -0
  6. pythonnative/mutations.py +3 -12
  7. pythonnative/native_views/__init__.py +1 -7
  8. pythonnative/native_views/android.py +573 -45
  9. pythonnative/native_views/desktop.py +78 -19
  10. pythonnative/native_views/ios.py +739 -51
  11. pythonnative/preview.py +31 -0
  12. pythonnative/project/config.py +1 -9
  13. pythonnative/screen.py +85 -0
  14. pythonnative/style.py +65 -6
  15. pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +2 -1
  16. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt +47 -0
  17. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt +67 -0
  18. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +172 -0
  19. pythonnative/virtual_rows.py +137 -0
  20. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/METADATA +1 -1
  21. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/RECORD +25 -19
  22. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/WHEEL +1 -1
  23. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/entry_points.txt +0 -0
  24. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/licenses/LICENSE +0 -0
  25. {pythonnative-0.22.1.dist-info → pythonnative-0.23.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,137 @@
1
+ """Row subtrees for natively virtualized lists.
2
+
3
+ Native virtualized lists (Android ``RecyclerView`` via
4
+ ``PNVirtualListView``, iOS ``UITableView``) own row lifecycle on the
5
+ platform side: the platform decides when a row enters the viewport,
6
+ hands PythonNative an empty native container, and recycles that
7
+ container when the row scrolls away.
8
+
9
+ Each visible row hosts a full PythonNative subtree driven by its own
10
+ nested [`Reconciler`][pythonnative.reconciler.Reconciler]. The
11
+ [`RowSubtree`][pythonnative.virtual_rows.RowSubtree] class wraps that
12
+ per-row reconciler: it mounts the row element into fresh native views,
13
+ re-reconciles in place when the platform rebinds the same container to
14
+ a different row index (recycling), and tears everything down when the
15
+ list is destroyed.
16
+
17
+ State inside rows works: hooks like ``use_state`` mark the row's own
18
+ reconciler dirty and a guarded flush re-renders just that row.
19
+ """
20
+
21
+ from typing import Any, Callable, Dict
22
+
23
+ from .element import Element
24
+
25
+ __all__ = ["RowSubtree", "RowHostPool"]
26
+
27
+
28
+ class RowSubtree:
29
+ """One list row's mounted PythonNative subtree.
30
+
31
+ Owns a nested reconciler whose viewport is the row's cell size.
32
+ The native root view produced by
33
+ [`mount`][pythonnative.virtual_rows.RowSubtree.mount] is what the
34
+ platform handler attaches to the recycled cell container.
35
+ """
36
+
37
+ def __init__(self) -> None:
38
+ from .native_views import get_registry
39
+ from .reconciler import Reconciler
40
+
41
+ self._reconciler = Reconciler(get_registry())
42
+ # Guarded synchronous flush so ``use_state`` inside a row
43
+ # re-renders the row. Re-entrant requests (a setter firing
44
+ # during the flush itself) are queued and drained after.
45
+ self._flushing = False
46
+ self._flush_queued = False
47
+ self._reconciler._screen_re_render = self._request_flush
48
+ self.native_root: Any = None
49
+
50
+ def _request_flush(self) -> None:
51
+ if self._flushing:
52
+ self._flush_queued = True
53
+ return
54
+ self._flushing = True
55
+ try:
56
+ for _ in range(8):
57
+ self._flush_queued = False
58
+ self.native_root = self._reconciler.flush_dirty()
59
+ if not self._flush_queued:
60
+ break
61
+ finally:
62
+ self._flushing = False
63
+
64
+ def mount(self, element: Element, width: float, height: float) -> Any:
65
+ """Mount ``element`` at the given cell size; returns the native root."""
66
+ self.native_root = self._reconciler.mount(element)
67
+ self._reconciler.set_viewport_size(float(width), float(height))
68
+ return self.native_root
69
+
70
+ def rebind(self, element: Element, width: float, height: float) -> Any:
71
+ """Reconcile the subtree to ``element`` (cell recycled to a new row).
72
+
73
+ Cheaper than unmount + mount when consecutive rows share a
74
+ shape, which is the overwhelmingly common case in lists.
75
+ Returns the (possibly replaced) native root.
76
+ """
77
+ self._reconciler.set_viewport_size(float(width), float(height))
78
+ self.native_root = self._reconciler.reconcile(element)
79
+ return self.native_root
80
+
81
+ def unmount(self) -> None:
82
+ """Destroy the subtree and release its native views."""
83
+ try:
84
+ self._reconciler.unmount()
85
+ except Exception:
86
+ pass
87
+ self.native_root = None
88
+
89
+
90
+ class RowHostPool:
91
+ """Per-list bookkeeping of row subtrees keyed by native container.
92
+
93
+ Platform handlers create one pool per virtualized list view. Keys
94
+ are platform-stable container identities (Java identity hash on
95
+ Android, ``contentView`` pointer on iOS).
96
+ """
97
+
98
+ def __init__(self) -> None:
99
+ self._subtrees: Dict[int, RowSubtree] = {}
100
+
101
+ def bind(
102
+ self,
103
+ container_key: int,
104
+ make_element: Callable[[], Element],
105
+ width: float,
106
+ height: float,
107
+ ) -> Any:
108
+ """Mount or rebind the subtree for ``container_key``.
109
+
110
+ Returns the subtree's native root. The caller must (re)attach
111
+ it to the container: platforms strip a recycled cell's
112
+ children before rebinding, so even an unchanged root needs
113
+ re-attachment.
114
+ """
115
+ element = make_element()
116
+ subtree = self._subtrees.get(container_key)
117
+ if subtree is None:
118
+ subtree = RowSubtree()
119
+ self._subtrees[container_key] = subtree
120
+ return subtree.mount(element, width, height)
121
+ return subtree.rebind(element, width, height)
122
+
123
+ def release(self, container_key: int) -> None:
124
+ """Unmount the subtree for one recycled container, if any."""
125
+ subtree = self._subtrees.pop(container_key, None)
126
+ if subtree is not None:
127
+ subtree.unmount()
128
+
129
+ def release_all(self) -> None:
130
+ """Unmount every subtree (list destroyed)."""
131
+ subtrees = list(self._subtrees.values())
132
+ self._subtrees.clear()
133
+ for subtree in subtrees:
134
+ subtree.unmount()
135
+
136
+ def __len__(self) -> int:
137
+ return len(self._subtrees)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.22.1
3
+ Version: 0.23.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -1,26 +1,29 @@
1
- pythonnative/__init__.py,sha256=ZdUkmJeUWu3oWKFzUxJ6O56P8_7nGgqirhQp_csapHQ,8551
1
+ pythonnative/__init__.py,sha256=vDBibKua43iWhsjL-R5bptS2tJKdkf69RqYs2o61NAw,8861
2
2
  pythonnative/_ios_log.py,sha256=Oi7V28VxcVoZyrpAirvLeEmUW18McqnU87V4d37Zzlw,2582
3
3
  pythonnative/alerts.py,sha256=mIANysFlaHwL5EqKnvNcyiJN9rGiZi9XDrD9Jpz1RFM,9340
4
4
  pythonnative/animated.py,sha256=IBokHVfp_Ud5VC8b4drY9DX8A4uG9wqRUjcW3y-EtkI,36233
5
- pythonnative/components.py,sha256=0BjsgQUVbiUZkpsX_0vxpuRs8zGV7P9V2HEnZHFEcAE,85248
5
+ pythonnative/appearance.py,sha256=vMkDRIz9ptaprMmlPmiYJLVwrDslfnc1bhwyE27M2ZI,3891
6
+ pythonnative/components.py,sha256=zz3SOZDXABRXHE3bmS56Z9v24X0ewebHnbO_y3r5KOc,115170
6
7
  pythonnative/element.py,sha256=W9varJj0Cl9HpckL8BcsC1u4ryUQOPVMrvetro4ilAE,2725
7
8
  pythonnative/events.py,sha256=H3XmZW99M16Q1OiRq1NbS6e-Pdopi6piCh6qdkmEu40,7632
8
9
  pythonnative/gestures.py,sha256=6qe_s3Cv2M5_V-_PgAQ5y9QbYV812B0xVmh8dRDHYMY,29940
9
- pythonnative/hooks.py,sha256=spKlbJSFQNg1jKTR9BFwyAf9soYDE5A8_L43W4cu9wU,38987
10
+ pythonnative/hooks.py,sha256=bM0t1WgkRhJ1myovY0AQY_QsHkO66lkeq4hFfRg8d58,40150
10
11
  pythonnative/hot_reload.py,sha256=g-PKXRw7IXZgLyuGwjQmztH10JjT8ULM-szrWWTKe7U,25024
12
+ pythonnative/images.py,sha256=JFjBJdZzZoQmAOfQ7QHqi7dYJ75XTs3u2lweQHGbdPc,6466
11
13
  pythonnative/layout.py,sha256=Ck_FCLSk0g4hdOzHo0c50cbvLe8Q8caQAxywXgnFhKM,49077
12
- pythonnative/mutations.py,sha256=aIhA4ESajEIPb1FxGJ6KkJ19vhs6wo1w82A5d52ql9s,4021
14
+ pythonnative/mutations.py,sha256=whmxO6ENWjm-yYH0QteoCV1Gdw_v-fJrhicMQJ5A0rk,3827
13
15
  pythonnative/navigation.py,sha256=4U57lHqiJJbqRJdTdvveRHKJ58BJhrwvkDOSoFt02I8,35566
14
16
  pythonnative/net.py,sha256=8D7EyEC8-JkYxlrtVfTDApOGzzdcA4P4kaCdYBBZI8E,8082
15
17
  pythonnative/platform.py,sha256=WtjxR04CqVhSnjJu-3upk8bwXYaSWr2N0MOVoNov-h0,4994
16
18
  pythonnative/platform_metrics.py,sha256=1r1kMbgS9LcVTbdpP5qRD4oHEvw70te5BXEyd_16Ihc,8961
17
- pythonnative/preview.py,sha256=ZBXOtXuFBeaRXdClhkpIhcQWsLu9fho0qChfPxnbr7w,16982
19
+ pythonnative/preview.py,sha256=bzwW_LgdnCFx8lCBiqlEjwMUa1ggFLYPWmHoMWqyKtg,18041
18
20
  pythonnative/reconciler.py,sha256=QFIFG3KjgoPas8LVDHRWouWEEtunegPz-jl6i5Hwno8,57082
19
21
  pythonnative/runtime.py,sha256=Ij3xxr_RyWESX7jP7b-RpsYjqjsJi1OpoV3WL3WTBWU,18245
20
- pythonnative/screen.py,sha256=GxDA8xE1GG35Evf4XwMq6tLTzcggkHN26HtVJFLeHOQ,64834
22
+ pythonnative/screen.py,sha256=AtoQP37HIWrIReecAvQXWHVx9MfW8tTO6AaBWlvroZI,68876
21
23
  pythonnative/storage.py,sha256=pp2mqlAy7wPr8vNwtcmalxc2658lD6z7PU4ilbnxAwE,12001
22
- pythonnative/style.py,sha256=lvJEWm9Gkp77_QQ1PMyum9CLEXFkyqT_7MtCyPZYMsA,15159
24
+ pythonnative/style.py,sha256=kB3tkxJ7LfRr52fodbA2n_OpvSeQAtgMhIVC6oMQISw,17290
23
25
  pythonnative/utils.py,sha256=-hwe_YS19ebpjeygdl3dGeVsYzO4G74rYD53svSi0rI,7593
26
+ pythonnative/virtual_rows.py,sha256=4YK3CeZobW-6F6GCXzGNFfmOaAiigmH4HXtqjYDRbyU,5056
24
27
  pythonnative/cli/__init__.py,sha256=NM1psvKe8jT0vzp8Ak4MMoygZz4P_msk5g-YEsY8xLk,232
25
28
  pythonnative/cli/pn.py,sha256=a0DXYHvcDZtfsmC91mIKgCFTO1Y8T-kkS68RH8_7kaY,28191
26
29
  pythonnative/native_modules/__init__.py,sha256=pgigpHuzT-rqwcjlwJvu93_4L8Nozal1HJid0S_JlmM,2636
@@ -38,15 +41,15 @@ pythonnative/native_modules/notifications.py,sha256=WVtzdimc_aGfnxU6syCFPkjHF9YR
38
41
  pythonnative/native_modules/permissions.py,sha256=ABIWiCuLIiZJmfA-B7dnw56_Iz7ApGzdtLAs6X694QU,6704
39
42
  pythonnative/native_modules/secure_store.py,sha256=PsnUwHzUPt-CzCAwM6BhidOBi9yofRD2uNtQIqAGofU,5953
40
43
  pythonnative/native_modules/share.py,sha256=B9ovknmnjeQvGQ8MQ14Hmq6E1x-JA469APMburJ0KUg,4727
41
- pythonnative/native_views/__init__.py,sha256=8RK0o8pixuqyHhiT5HMWDiya2vHBRJhntjjXzB2HB-U,15638
42
- pythonnative/native_views/android.py,sha256=RAly8MHQMWt2SY6GNqQB_FYeZpKmivo5Ja7N5LAuAo8,109796
44
+ pythonnative/native_views/__init__.py,sha256=93v7pt0_7f8BWqOiwSvlq20F1oJLfCnmQBvLlCDA-Xk,15340
45
+ pythonnative/native_views/android.py,sha256=2pgsv569rC2eWdHGoIrr1kmI-qB4ZklrMV2G9AO6OpQ,130838
43
46
  pythonnative/native_views/base.py,sha256=NFDlEIoKCINzcVkf2uobT9TgujPb1RClLS3TzwH72MY,9668
44
- pythonnative/native_views/desktop.py,sha256=731Uz-TPb0lGhPAcAo_8l-8B62lgZIr8h_H1BViSFGw,57116
45
- pythonnative/native_views/ios.py,sha256=ySsTKYRcVLfbaw60U6yNTguEOSYqXLC_8xy59G4hjA4,141502
47
+ pythonnative/native_views/desktop.py,sha256=GnZEchJLUcH74jG_21CCzjdYcEmm-5etUX2D0-_WNtQ,59735
48
+ pythonnative/native_views/ios.py,sha256=fgskhuENQO-aiw527xOrveeRClX8uFt5UdqKSCsBT_8,169080
46
49
  pythonnative/project/__init__.py,sha256=kAhWLONs53H1L7xVtczLmzYcQSOOkdRZTAJnIMhpBts,2064
47
50
  pythonnative/project/android.py,sha256=SX0uu4-tz4WdYAf9sbycQPSmoQ8fbtAADeUWk9G8ybM,19127
48
51
  pythonnative/project/builder.py,sha256=DokY-YidsDeWYgaixrpD3Kzp7_XudibltI8X_WdBIcE,19096
49
- pythonnative/project/config.py,sha256=LeaRgwXznuk2hazbM2aOIDGeZ6FBoSkUBVmony5Wg-Q,24210
52
+ pythonnative/project/config.py,sha256=jH9_MnE5kmKGe3yA4tfPHic5LlKcWys58jxOmUSJTd4,23839
50
53
  pythonnative/project/doctor.py,sha256=ETg-lvEyaNzaxgsDn9f4DBapNKBttMHA-9kwgX-yb3g,7943
51
54
  pythonnative/project/icons.py,sha256=GaXpktQ9v4v2JZPM3ppueNPmwjTuFiLanHJDaZbLGuo,7945
52
55
  pythonnative/project/ios.py,sha256=DHQs_Wf_uJ9eoI3gqGmcjFuF2shwMwa8mLKGtuTDffw,14004
@@ -62,9 +65,12 @@ pythonnative/templates/android_template/settings.gradle,sha256=GKZiYUYWsaXxaiKOB
62
65
  pythonnative/templates/android_template/app/build.gradle,sha256=zHYbYXjKUiW-ZSYXT2GfveyVnjOCNLXvXpPxI76O5t4,1864
63
66
  pythonnative/templates/android_template/app/proguard-rules.pro,sha256=Vv2WDPIl9spA-YKxOl27DYvD394T_3ZCKCXGBw0KGJA,750
64
67
  pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt,sha256=Am8Yla3i1eR_ac5FVgPU_RsuMrCbyT79h1BcajGE-zI,693
65
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml,sha256=MdWrXxOrwUjnqtDbV952NI4nVF2dTUX9xwSS8chhd9I,940
68
+ pythonnative/templates/android_template/app/src/main/AndroidManifest.xml,sha256=eprO6TQuZN0UEVsMcg2oXDaPUuVuYJWMg3GScNsfC8k,995
66
69
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt,sha256=1dSBEX-AB2aUPxFFDnWwcmVHRi00T-iwbXi4yt4RQk8,1481
67
70
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt,sha256=mDfzKTTXRP-zbSnfIPxAYyjtr9_IyyQwXM-dovcfVbg,1723
71
+ pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt,sha256=R2PC6BA3VCaKET0XV33tXTFf4Ht9udvvLu9TT_RfWp4,1819
72
+ pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt,sha256=ejdFhpzn0-2TZa0nvG41r2m0xoejU2fXXuJSmO6tnBU,2205
73
+ pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java,sha256=1ve74nvOFqsduyaLOGPb8Sd9Jrwz-sELRB9y1NMN1AI,6072
68
74
  pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt,sha256=lSxWYH6cGDpGalo0kDzTAT5H3NuhJVLT7BQYcfCydOc,5138
69
75
  pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml,sha256=7UI8c6b0Ck0pCfCQHmBSezqAfNWeG1WTvKrhgIscYyE,5606
70
76
  pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml,sha256=AdGmpsEjTrf-Jw0JfrKD1yucla5RGIhvG2VzqtKA8fc,1702
@@ -105,9 +111,9 @@ pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/x
105
111
  pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift,sha256=YnwzZx7yXB13xKAXEGNgz17VuhWeqkHTRTtBJ2Vu3_E,1238
106
112
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift,sha256=HLRr3cmouRqQeXG13WrZ2XSAmgMgCsNXfZpbwkxeFcs,1385
107
113
  pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift,sha256=f5JrG0uVtLMeJQy26Yyz7Om-JUkT220osqcbeIVkj2g,815
108
- pythonnative-0.22.1.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
109
- pythonnative-0.22.1.dist-info/METADATA,sha256=8AdKrxbqYwpGdyEc69TN-a6_4T1Sg20NDHkMv1sEbV4,10347
110
- pythonnative-0.22.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
111
- pythonnative-0.22.1.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
112
- pythonnative-0.22.1.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
113
- pythonnative-0.22.1.dist-info/RECORD,,
114
+ pythonnative-0.23.0.dist-info/licenses/LICENSE,sha256=A69iG7TIAe6KkGQf6xoVHkc5JSZtOr5eRSvC5iuivnI,1067
115
+ pythonnative-0.23.0.dist-info/METADATA,sha256=t0952y_u74dbBtuyPdG1VHdQO3E8M8Iqi_EAxtmPGH4,10347
116
+ pythonnative-0.23.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
117
+ pythonnative-0.23.0.dist-info/entry_points.txt,sha256=iUtDawWSAJAEyWTycpZxDuYz73ol31butpzDIEAgPO0,48
118
+ pythonnative-0.23.0.dist-info/top_level.txt,sha256=kT4SEATY2ywzrZ2Pgea6_zxyym44Q_PbOsUoOYjJLFE,13
119
+ pythonnative-0.23.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (83.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5