pythonnative 0.40.0__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. pythonnative/__init__.py +411 -0
  2. pythonnative/_ios_log.py +92 -0
  3. pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
  4. pythonnative/alerts.py +236 -0
  5. pythonnative/animated.py +1687 -0
  6. pythonnative/animation_graph.py +78 -0
  7. pythonnative/appearance.py +124 -0
  8. pythonnative/bootstrap.py +100 -0
  9. pythonnative/bridge/__init__.py +302 -0
  10. pythonnative/bridge/android.py +78 -0
  11. pythonnative/bridge/codec.py +170 -0
  12. pythonnative/bridge/commits.py +114 -0
  13. pythonnative/bridge/fake.py +243 -0
  14. pythonnative/bridge/ios.py +138 -0
  15. pythonnative/bridge/web.py +489 -0
  16. pythonnative/cli/__init__.py +7 -0
  17. pythonnative/cli/pn.py +1214 -0
  18. pythonnative/component.py +271 -0
  19. pythonnative/components/__init__.py +111 -0
  20. pythonnative/components/_base.py +166 -0
  21. pythonnative/components/controls.py +521 -0
  22. pythonnative/components/layout.py +518 -0
  23. pythonnative/components/lists.py +785 -0
  24. pythonnative/components/media.py +204 -0
  25. pythonnative/components/overlays.py +107 -0
  26. pythonnative/components/pressable.py +252 -0
  27. pythonnative/components/structural.py +167 -0
  28. pythonnative/components/text.py +291 -0
  29. pythonnative/devclient.py +824 -0
  30. pythonnative/devserver/__init__.py +34 -0
  31. pythonnative/devserver/server.py +718 -0
  32. pythonnative/devserver/static/animation_graph.js +82 -0
  33. pythonnative/devserver/static/bridge.js +147 -0
  34. pythonnative/devserver/static/colors.js +70 -0
  35. pythonnative/devserver/static/host.js +552 -0
  36. pythonnative/devserver/static/index.html +60 -0
  37. pythonnative/devserver/static/layout.js +85 -0
  38. pythonnative/devserver/static/preview.css +415 -0
  39. pythonnative/devserver/static/renderer.js +1994 -0
  40. pythonnative/devserver/static/shell.js +345 -0
  41. pythonnative/devserver/static/yoga/LICENSE +21 -0
  42. pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
  43. pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
  44. pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
  45. pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
  46. pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
  47. pythonnative/devserver/static/yoga/src/index.js +16 -0
  48. pythonnative/devserver/static/yoga/src/index.js.map +1 -0
  49. pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
  50. pythonnative/devserver/static/yoga/src/load.js +17 -0
  51. pythonnative/devserver/static/yoga/src/load.js.map +1 -0
  52. pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
  53. pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
  54. pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
  55. pythonnative/devserver/watcher.py +267 -0
  56. pythonnative/devserver/ws.py +421 -0
  57. pythonnative/diagnostics.py +241 -0
  58. pythonnative/element.py +159 -0
  59. pythonnative/equality.py +19 -0
  60. pythonnative/events.py +231 -0
  61. pythonnative/gestures.py +1333 -0
  62. pythonnative/hooks.py +1621 -0
  63. pythonnative/hosts/__init__.py +63 -0
  64. pythonnative/hosts/base.py +596 -0
  65. pythonnative/hosts/native.py +302 -0
  66. pythonnative/hot_reload.py +585 -0
  67. pythonnative/layout.py +328 -0
  68. pythonnative/mutations.py +142 -0
  69. pythonnative/native/android/build.gradle +52 -0
  70. pythonnative/native/android/gradle.properties +3 -0
  71. pythonnative/native/android/settings.gradle +12 -0
  72. pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
  73. pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
  74. pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
  75. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
  76. pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
  77. pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
  78. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
  79. pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
  80. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
  81. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
  82. pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
  83. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
  84. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
  85. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
  86. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
  87. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
  88. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
  89. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
  90. pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
  91. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
  92. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
  93. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
  94. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
  95. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
  96. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
  97. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
  98. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
  99. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
  100. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
  101. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
  102. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
  103. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
  104. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
  105. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
  106. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
  107. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
  108. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
  109. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
  110. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
  111. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
  112. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
  113. pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
  114. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
  115. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
  116. pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
  117. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
  118. pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
  119. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
  120. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
  121. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
  122. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
  123. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
  124. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
  125. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
  126. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
  127. pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
  128. pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
  129. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
  130. pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
  131. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
  132. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
  133. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
  134. pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
  135. pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
  136. pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
  137. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
  138. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
  139. pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
  140. pythonnative/native/ios/Package.swift +23 -0
  141. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
  142. pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
  143. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
  144. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
  145. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
  146. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
  147. pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
  148. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
  149. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
  150. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
  151. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
  152. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
  153. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
  154. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
  155. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
  156. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
  157. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
  158. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
  159. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
  160. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
  161. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
  162. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
  163. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
  164. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
  165. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
  166. pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
  167. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
  168. pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
  169. pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
  170. pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
  171. pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
  172. pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
  173. pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
  174. pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
  175. pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
  176. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
  177. pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
  178. pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
  179. pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
  180. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
  181. pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
  182. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
  183. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
  184. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
  185. pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
  186. pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
  187. pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
  188. pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
  189. pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
  190. pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
  191. pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
  192. pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
  193. pythonnative/native/yoga/LICENSE +21 -0
  194. pythonnative/native/yoga/Package.swift +9 -0
  195. pythonnative/native/yoga/VENDORED.md +2 -0
  196. pythonnative/native/yoga/include/PNStyle.h +10 -0
  197. pythonnative/native/yoga/include/module.modulemap +5 -0
  198. pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
  199. pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
  200. pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
  201. pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
  202. pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
  203. pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
  204. pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
  205. pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
  206. pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
  207. pythonnative/native/yoga/python.cpp +16 -0
  208. pythonnative/native/yoga/style.cpp +272 -0
  209. pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
  210. pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
  211. pythonnative/native/yoga/yoga/YGConfig.h +158 -0
  212. pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
  213. pythonnative/native/yoga/yoga/YGEnums.h +142 -0
  214. pythonnative/native/yoga/yoga/YGMacros.h +99 -0
  215. pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
  216. pythonnative/native/yoga/yoga/YGNode.h +304 -0
  217. pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
  218. pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
  219. pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
  220. pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
  221. pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
  222. pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
  223. pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
  224. pythonnative/native/yoga/yoga/YGValue.h +84 -0
  225. pythonnative/native/yoga/yoga/Yoga.h +21 -0
  226. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
  227. pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
  228. pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
  229. pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
  230. pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
  231. pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
  232. pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
  233. pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
  234. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
  235. pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
  236. pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
  237. pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
  238. pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
  239. pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
  240. pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
  241. pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
  242. pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
  243. pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
  244. pythonnative/native/yoga/yoga/config/Config.h +92 -0
  245. pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
  246. pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
  247. pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
  248. pythonnative/native/yoga/yoga/debug/Log.h +34 -0
  249. pythonnative/native/yoga/yoga/enums/Align.h +47 -0
  250. pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
  251. pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
  252. pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
  253. pythonnative/native/yoga/yoga/enums/Display.h +41 -0
  254. pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
  255. pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
  256. pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
  257. pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
  258. pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
  259. pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
  260. pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
  261. pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
  262. pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
  263. pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
  264. pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
  265. pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
  266. pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
  267. pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
  268. pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
  269. pythonnative/native/yoga/yoga/event/event.cpp +87 -0
  270. pythonnative/native/yoga/yoga/event/event.h +130 -0
  271. pythonnative/native/yoga/yoga/module.modulemap +21 -0
  272. pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
  273. pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
  274. pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
  275. pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
  276. pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
  277. pythonnative/native/yoga/yoga/node/Node.h +337 -0
  278. pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
  279. pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
  280. pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
  281. pythonnative/native/yoga/yoga/style/Style.h +757 -0
  282. pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
  283. pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
  284. pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
  285. pythonnative/native_modules/__init__.py +124 -0
  286. pythonnative/native_modules/app_state.py +99 -0
  287. pythonnative/native_modules/battery.py +75 -0
  288. pythonnative/native_modules/biometrics.py +42 -0
  289. pythonnative/native_modules/camera.py +65 -0
  290. pythonnative/native_modules/clipboard.py +46 -0
  291. pythonnative/native_modules/fallback.py +324 -0
  292. pythonnative/native_modules/file_system.py +145 -0
  293. pythonnative/native_modules/haptics.py +75 -0
  294. pythonnative/native_modules/linking.py +123 -0
  295. pythonnative/native_modules/location.py +75 -0
  296. pythonnative/native_modules/net_info.py +112 -0
  297. pythonnative/native_modules/notifications.py +129 -0
  298. pythonnative/native_modules/permissions.py +108 -0
  299. pythonnative/native_modules/registry.py +526 -0
  300. pythonnative/native_modules/secure_store.py +52 -0
  301. pythonnative/native_modules/share.py +51 -0
  302. pythonnative/native_views/__init__.py +375 -0
  303. pythonnative/native_views/base.py +316 -0
  304. pythonnative/native_views/bridge_backend.py +277 -0
  305. pythonnative/navigation/__init__.py +103 -0
  306. pythonnative/navigation/container.py +154 -0
  307. pythonnative/navigation/handle.py +540 -0
  308. pythonnative/navigation/hooks.py +133 -0
  309. pythonnative/navigation/host.py +58 -0
  310. pythonnative/navigation/linking.py +200 -0
  311. pythonnative/navigation/navigators.py +659 -0
  312. pythonnative/navigation/screen.py +149 -0
  313. pythonnative/navigation/state.py +269 -0
  314. pythonnative/net.py +248 -0
  315. pythonnative/platform.py +166 -0
  316. pythonnative/platform_metrics.py +252 -0
  317. pythonnative/preview.py +287 -0
  318. pythonnative/profiling.py +101 -0
  319. pythonnative/project/__init__.py +68 -0
  320. pythonnative/project/android.py +557 -0
  321. pythonnative/project/builder.py +817 -0
  322. pythonnative/project/config.py +773 -0
  323. pythonnative/project/deps.py +719 -0
  324. pythonnative/project/devices.py +304 -0
  325. pythonnative/project/doctor.py +283 -0
  326. pythonnative/project/fingerprint.py +171 -0
  327. pythonnative/project/icons.py +247 -0
  328. pythonnative/project/ios.py +325 -0
  329. pythonnative/project/lockfile.py +100 -0
  330. pythonnative/project/permissions.py +361 -0
  331. pythonnative/project/plugins.py +519 -0
  332. pythonnative/project/runtime_assets.py +183 -0
  333. pythonnative/py.typed +0 -0
  334. pythonnative/query.py +153 -0
  335. pythonnative/reconciler/__init__.py +29 -0
  336. pythonnative/reconciler/boundaries.py +375 -0
  337. pythonnative/reconciler/children.py +88 -0
  338. pythonnative/reconciler/core.py +1165 -0
  339. pythonnative/reconciler/layout_pass.py +393 -0
  340. pythonnative/reconciler/vnode.py +266 -0
  341. pythonnative/refresh.py +55 -0
  342. pythonnative/runtime.py +306 -0
  343. pythonnative/scheduler.py +159 -0
  344. pythonnative/sdk/__init__.py +153 -0
  345. pythonnative/sdk/_components.py +427 -0
  346. pythonnative/sdk/builtins.py +91 -0
  347. pythonnative/sdk/codegen.py +149 -0
  348. pythonnative/sdk/module_codegen.py +184 -0
  349. pythonnative/sdk/schema.py +224 -0
  350. pythonnative/sdk/templates/contracts.kt +46 -0
  351. pythonnative/sdk/templates/contracts.swift +46 -0
  352. pythonnative/storage.py +184 -0
  353. pythonnative/style.py +841 -0
  354. pythonnative/suspense.py +183 -0
  355. pythonnative/templates/android_template/app/build.gradle +75 -0
  356. pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
  357. pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
  358. pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
  359. pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
  360. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
  361. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
  362. pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  363. pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
  364. pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
  365. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  366. pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  367. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  368. pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  369. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  370. pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  371. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  372. pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  373. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  374. pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  375. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  376. pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  377. pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
  378. pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
  379. pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
  380. pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
  381. pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
  382. pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
  383. pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
  384. pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
  385. pythonnative/templates/android_template/build.gradle +10 -0
  386. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  387. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
  388. pythonnative/templates/android_template/gradle.properties +23 -0
  389. pythonnative/templates/android_template/gradlew +185 -0
  390. pythonnative/templates/android_template/gradlew.bat +89 -0
  391. pythonnative/templates/android_template/settings.gradle +17 -0
  392. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
  393. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  394. pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
  395. pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
  396. pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
  397. pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
  398. pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
  399. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
  400. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
  401. pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
  402. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
  403. pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  404. pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
  405. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
  406. pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
  407. pythonnative/testing/__init__.py +53 -0
  408. pythonnative/testing/backend.py +276 -0
  409. pythonnative/testing/harness.py +369 -0
  410. pythonnative/utils.py +145 -0
  411. pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
  412. pythonnative-0.40.0.dist-info/METADATA +150 -0
  413. pythonnative-0.40.0.dist-info/RECORD +418 -0
  414. pythonnative-0.40.0.dist-info/WHEEL +5 -0
  415. pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
  416. pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
  417. pythonnative-0.40.0.dist-info/top_level.txt +1 -0
  418. pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
@@ -0,0 +1,281 @@
1
+ import UIKit
2
+
3
+ /// `Switch`: `UISwitch` emitting `on_change(bool)`.
4
+ public final class PNSwitchManager: PNComponentManager {
5
+ public override func makeView(props: [String: Any]) -> UIView {
6
+ UISwitch(frame: .zero)
7
+ }
8
+
9
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
10
+ let view = super.createView(tag: tag, props: props)
11
+ if let control = view as? UISwitch {
12
+ PNActionTarget.attach(control, events: .valueChanged) { [weak control] in
13
+ guard let control = control, PNViewState.existing(for: control)?.flag("suppress") != true else { return }
14
+ PNEvents.emit(control, "on_change", [control.isOn])
15
+ }
16
+ }
17
+ return view
18
+ }
19
+
20
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
21
+ guard let control = view as? UISwitch, let state = PNViewState.existing(for: control) else { return }
22
+ if PNProps.has(props, "value") {
23
+ let value = PNProps.bool(PNProps.value(props, "value")) ?? false
24
+ if control.isOn != value {
25
+ state.extras["suppress"] = true
26
+ control.setOn(value, animated: !initial)
27
+ state.extras["suppress"] = false
28
+ }
29
+ }
30
+ if let color = PNColor.parse(PNProps.value(props, "on_tint_color") ?? PNProps.value(props, "tint_color")) {
31
+ control.onTintColor = color
32
+ }
33
+ if let color = PNColor.parse(PNProps.value(props, "thumb_color")) {
34
+ control.thumbTintColor = color
35
+ }
36
+ if PNProps.has(props, "disabled") {
37
+ control.isEnabled = !(PNProps.bool(PNProps.value(props, "disabled")) ?? false)
38
+ }
39
+ PNViewStyler.applyAccessibility(control, props)
40
+ }
41
+
42
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
43
+ CGSize(width: 51, height: 31)
44
+ }
45
+
46
+ /// `UISwitch` ignores the size part of `frame`, but the base class sets
47
+ /// `bounds` directly, which would stretch its hit area (and accessibility
48
+ /// frame) across a stretched flex cross axis. Pin the box to the natural
49
+ /// size at the layout origin so taps land on the visible control.
50
+ public override func setFrame(view: UIView, x: Double, y: Double, w: Double, h: Double) {
51
+ let natural = view.intrinsicContentSize
52
+ let pw = natural.width > 0 ? min(w, Double(natural.width)) : w
53
+ let ph = natural.height > 0 ? min(h, Double(natural.height)) : h
54
+ super.setFrame(view: view, x: x, y: y, w: pw, h: ph)
55
+ }
56
+ }
57
+
58
+ /// `Slider`: `UISlider` emitting `on_change(float)`.
59
+ public final class PNSliderManager: PNComponentManager {
60
+ public override func makeView(props: [String: Any]) -> UIView {
61
+ UISlider(frame: .zero)
62
+ }
63
+
64
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
65
+ let view = super.createView(tag: tag, props: props)
66
+ if let slider = view as? UISlider {
67
+ PNActionTarget.attach(slider, events: .valueChanged) { [weak slider] in
68
+ guard let slider = slider, PNViewState.existing(for: slider)?.flag("suppress") != true else { return }
69
+ PNEvents.emit(slider, "on_change", [Double(slider.value)])
70
+ }
71
+ PNActionTarget.attach(slider, events: [.touchUpInside, .touchUpOutside, .touchCancel]) { [weak slider] in
72
+ guard let slider = slider else { return }
73
+ PNEvents.emitIfWired(slider, "on_sliding_complete", [Double(slider.value)])
74
+ }
75
+ }
76
+ return view
77
+ }
78
+
79
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
80
+ guard let slider = view as? UISlider, let state = PNViewState.existing(for: slider) else { return }
81
+ if let min = PNProps.double(PNProps.value(props, "min_value")) { slider.minimumValue = Float(min) }
82
+ if let max = PNProps.double(PNProps.value(props, "max_value")) { slider.maximumValue = Float(max) }
83
+ if let value = PNProps.double(PNProps.value(props, "value")), abs(Double(slider.value) - value) > 1e-9 {
84
+ state.extras["suppress"] = true
85
+ slider.setValue(Float(value), animated: !initial)
86
+ state.extras["suppress"] = false
87
+ }
88
+ if let color = PNColor.parse(PNProps.value(props, "minimum_track_color") ?? PNProps.value(props, "tint_color")) {
89
+ slider.minimumTrackTintColor = color
90
+ }
91
+ if let color = PNColor.parse(PNProps.value(props, "maximum_track_color")) { slider.maximumTrackTintColor = color }
92
+ if let color = PNColor.parse(PNProps.value(props, "thumb_color")) { slider.thumbTintColor = color }
93
+ if PNProps.has(props, "disabled") { slider.isEnabled = !(PNProps.bool(PNProps.value(props, "disabled")) ?? false) }
94
+ PNViewStyler.applyAccessibility(slider, props)
95
+ }
96
+
97
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
98
+ let w = maxW.isFinite && maxW < 1e6 ? maxW : 200
99
+ return CGSize(width: max(w, 100), height: 34)
100
+ }
101
+ }
102
+
103
+ /// `ActivityIndicator`: `UIActivityIndicatorView`.
104
+ public final class PNActivityIndicatorManager: PNComponentManager {
105
+ public override func makeView(props: [String: Any]) -> UIView {
106
+ let style: UIActivityIndicatorView.Style = PNProps.string(PNProps.value(props, "size")) == "large" ? .large : .medium
107
+ let view = UIActivityIndicatorView(style: style)
108
+ view.hidesWhenStopped = true
109
+ return view
110
+ }
111
+
112
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
113
+ guard let indicator = view as? UIActivityIndicatorView else { return }
114
+ if !initial, let size = PNProps.string(PNProps.value(props, "size")) {
115
+ indicator.style = size == "large" ? .large : .medium
116
+ }
117
+ if let color = PNColor.parse(PNProps.value(props, "color")) { indicator.color = color }
118
+ if PNProps.has(props, "animating") || initial {
119
+ let animating = PNProps.bool(PNProps.value(props, "animating")) ?? true
120
+ if animating { indicator.startAnimating() } else { indicator.stopAnimating() }
121
+ }
122
+ if PNProps.has(props, "hides_when_stopped") {
123
+ indicator.hidesWhenStopped = PNProps.bool(PNProps.value(props, "hides_when_stopped")) ?? true
124
+ }
125
+ PNViewStyler.applyAccessibility(indicator, props)
126
+ }
127
+
128
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
129
+ let size = view.intrinsicContentSize
130
+ return size.width > 0 ? size : CGSize(width: 20, height: 20)
131
+ }
132
+ }
133
+
134
+ /// `ProgressBar`: a determinate `UIProgressView`, or a spinning
135
+ /// `UIActivityIndicatorView` when `indeterminate` is set at creation.
136
+ public final class PNProgressBarManager: PNComponentManager {
137
+ public override func makeView(props: [String: Any]) -> UIView {
138
+ if PNProps.bool(PNProps.value(props, "indeterminate")) == true {
139
+ let spinner = UIActivityIndicatorView(style: .medium)
140
+ spinner.startAnimating()
141
+ return spinner
142
+ }
143
+ return UIProgressView(progressViewStyle: .default)
144
+ }
145
+
146
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
147
+ if let spinner = view as? UIActivityIndicatorView {
148
+ if let color = PNColor.parse(PNProps.value(props, "color")) { spinner.color = color }
149
+ spinner.startAnimating()
150
+ } else if let bar = view as? UIProgressView {
151
+ if let value = PNProps.double(PNProps.value(props, "value") ?? PNProps.value(props, "progress")) {
152
+ bar.setProgress(Float(max(0, min(1, value))), animated: !initial)
153
+ }
154
+ if let color = PNColor.parse(PNProps.value(props, "color")) { bar.progressTintColor = color }
155
+ if let color = PNColor.parse(PNProps.value(props, "track_color")) { bar.trackTintColor = color }
156
+ }
157
+ PNViewStyler.applyAccessibility(view, props)
158
+ }
159
+
160
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
161
+ if view is UIActivityIndicatorView { return CGSize(width: 20, height: 20) }
162
+ let w = maxW.isFinite && maxW < 1e6 ? maxW : 200
163
+ return CGSize(width: max(w, 40), height: 4)
164
+ }
165
+ }
166
+
167
+ /// `SegmentedControl`: `UISegmentedControl` emitting `on_change(index)`.
168
+ public final class PNSegmentedControlManager: PNComponentManager {
169
+ public override func makeView(props: [String: Any]) -> UIView {
170
+ let segments = ((PNProps.value(props, "segments") as? [Any]) ?? []).map { PNProps.string($0) ?? "" }
171
+ return UISegmentedControl(items: segments)
172
+ }
173
+
174
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
175
+ let view = super.createView(tag: tag, props: props)
176
+ if let control = view as? UISegmentedControl {
177
+ PNActionTarget.attach(control, events: .valueChanged) { [weak control] in
178
+ guard let control = control, PNViewState.existing(for: control)?.flag("suppress") != true else { return }
179
+ PNEvents.emit(control, "on_change", [control.selectedSegmentIndex])
180
+ }
181
+ }
182
+ return view
183
+ }
184
+
185
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
186
+ guard let control = view as? UISegmentedControl, let state = PNViewState.existing(for: control) else { return }
187
+ let merged = state.props
188
+ var rebuilt = false
189
+ if let segments = PNProps.value(props, "segments") as? [Any] {
190
+ let titles = segments.map { PNProps.string($0) ?? "" }
191
+ if !initial, titles != (state.extras["segments"] as? [String]) {
192
+ state.extras["suppress"] = true
193
+ control.removeAllSegments()
194
+ for (i, title) in titles.enumerated() {
195
+ control.insertSegment(withTitle: title, at: i, animated: false)
196
+ }
197
+ state.extras["suppress"] = false
198
+ rebuilt = true
199
+ }
200
+ state.extras["segments"] = titles
201
+ }
202
+ if rebuilt || initial || PNProps.value(props, "selected_index") != nil {
203
+ state.extras["suppress"] = true
204
+ control.selectedSegmentIndex = PNProps.int(PNProps.value(merged, "selected_index")) ?? 0
205
+ state.extras["suppress"] = false
206
+ }
207
+ if let color = PNColor.parse(PNProps.value(props, "tint_color")) {
208
+ control.selectedSegmentTintColor = color
209
+ control.tintColor = color
210
+ }
211
+ if PNProps.has(props, "disabled") {
212
+ control.isEnabled = !(PNProps.bool(PNProps.value(props, "disabled")) ?? false)
213
+ }
214
+ PNViewStyler.applyAccessibility(control, props)
215
+ }
216
+ }
217
+
218
+ /// `DatePicker`: compact `UIDatePicker` emitting ISO-style strings.
219
+ public final class PNDatePickerManager: PNComponentManager {
220
+ static let formats = ["date": "yyyy-MM-dd", "time": "HH:mm", "datetime": "yyyy-MM-dd'T'HH:mm"]
221
+ static var formatters: [String: DateFormatter] = [:]
222
+
223
+ static func formatter(mode: String) -> DateFormatter {
224
+ let format = formats[mode] ?? formats["date"]!
225
+ if let cached = formatters[format] { return cached }
226
+ let formatter = DateFormatter()
227
+ formatter.dateFormat = format
228
+ formatter.locale = Locale(identifier: "en_US_POSIX")
229
+ formatters[format] = formatter
230
+ return formatter
231
+ }
232
+
233
+ public override func makeView(props: [String: Any]) -> UIView {
234
+ let picker = UIDatePicker(frame: .zero)
235
+ picker.pnApplyCompactStyle()
236
+ return picker
237
+ }
238
+
239
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
240
+ let view = super.createView(tag: tag, props: props)
241
+ if let picker = view as? UIDatePicker {
242
+ PNActionTarget.attach(picker, events: .valueChanged) { [weak picker] in
243
+ guard let picker = picker, let state = PNViewState.existing(for: picker), !state.flag("suppress") else { return }
244
+ let mode = PNProps.string(PNProps.value(state.props, "mode")) ?? "date"
245
+ PNEvents.emit(picker, "on_change", [PNDatePickerManager.formatter(mode: mode).string(from: picker.date)])
246
+ }
247
+ }
248
+ return view
249
+ }
250
+
251
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
252
+ guard let picker = view as? UIDatePicker, let state = PNViewState.existing(for: picker) else { return }
253
+ let mode = PNProps.string(PNProps.value(state.props, "mode")) ?? "date"
254
+ if PNProps.value(props, "mode") != nil {
255
+ switch mode {
256
+ case "time": picker.datePickerMode = .time
257
+ case "datetime": picker.datePickerMode = .dateAndTime
258
+ default: picker.datePickerMode = .date
259
+ }
260
+ }
261
+ let formatter = PNDatePickerManager.formatter(mode: mode)
262
+ if PNProps.has(props, "minimum") {
263
+ picker.minimumDate = PNProps.string(PNProps.value(props, "minimum")).flatMap { formatter.date(from: $0) }
264
+ }
265
+ if PNProps.has(props, "maximum") {
266
+ picker.maximumDate = PNProps.string(PNProps.value(props, "maximum")).flatMap { formatter.date(from: $0) }
267
+ }
268
+ if let value = PNProps.string(PNProps.value(props, "value")), !value.isEmpty, let date = formatter.date(from: value) {
269
+ state.extras["suppress"] = true
270
+ picker.setDate(date, animated: false)
271
+ state.extras["suppress"] = false
272
+ }
273
+ if PNProps.has(props, "disabled") {
274
+ picker.isEnabled = !(PNProps.bool(PNProps.value(props, "disabled")) ?? false)
275
+ }
276
+ if let color = PNColor.parse(PNProps.value(props, "tint_color")) {
277
+ picker.tintColor = color
278
+ }
279
+ PNViewStyler.applyAccessibility(picker, props)
280
+ }
281
+ }
@@ -0,0 +1,222 @@
1
+ import CryptoKit
2
+ import UIKit
3
+
4
+ /// `Image`: a `UIImageView` loading from http(s) URLs (with memory and
5
+ /// disk caching), local file paths, bundle asset names, and base64 data URIs.
6
+ public final class PNImageManager: PNComponentManager {
7
+ public override func makeView(props: [String: Any]) -> UIView {
8
+ let view = UIImageView(frame: .zero)
9
+ view.clipsToBounds = true
10
+ view.contentMode = .scaleAspectFit
11
+ return view
12
+ }
13
+
14
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
15
+ guard let imageView = view as? UIImageView else { return }
16
+ if let tint = PNColor.parse(PNProps.value(props, "tint_color") ?? PNProps.value(props, "tint")) {
17
+ imageView.tintColor = tint
18
+ if let image = imageView.image {
19
+ imageView.image = image.withRenderingMode(.alwaysTemplate)
20
+ }
21
+ }
22
+ if let placeholder = PNColor.parse(PNProps.value(props, "placeholder_color")) {
23
+ imageView.backgroundColor = placeholder
24
+ }
25
+ if let source = PNProps.string(PNProps.value(props, "source")), !source.isEmpty {
26
+ load(imageView, source: source)
27
+ }
28
+ if let mode = PNProps.string(PNProps.value(props, "scale_type") ?? PNProps.value(props, "resize_mode")) {
29
+ imageView.contentMode = PNImageManager.contentMode(mode)
30
+ }
31
+ PNViewStyler.applyCommon(imageView, props)
32
+ }
33
+
34
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
35
+ guard let image = (view as? UIImageView)?.image else { return .zero }
36
+ var size = image.size
37
+ if maxW.isFinite, maxW > 0, size.width > maxW {
38
+ let scale = maxW / size.width
39
+ size = CGSize(width: maxW, height: size.height * scale)
40
+ }
41
+ return size
42
+ }
43
+
44
+ static func contentMode(_ name: String) -> UIView.ContentMode {
45
+ switch name {
46
+ case "cover": return .scaleAspectFill
47
+ case "stretch": return .scaleToFill
48
+ case "center": return .center
49
+ case "repeat": return .scaleAspectFill
50
+ default: return .scaleAspectFit
51
+ }
52
+ }
53
+
54
+ // MARK: - Loading
55
+
56
+ private func load(_ imageView: UIImageView, source: String) {
57
+ guard let state = PNViewState.existing(for: imageView) else { return }
58
+ state.extras["pending_source"] = source
59
+ if source.hasPrefix("data:") {
60
+ loadDataURI(imageView, source)
61
+ } else if source.hasPrefix("http://") || source.hasPrefix("https://") {
62
+ PNImageLoader.shared.fetch(source) { [weak imageView] result in
63
+ guard let imageView = imageView, PNViewState.existing(for: imageView)?.extras["pending_source"] as? String == source else { return }
64
+ switch result {
65
+ case .success(let data):
66
+ if let image = PNImageManager.decode(data, targetSize: imageView.bounds.size) {
67
+ self.setImage(imageView, image)
68
+ PNEvents.emit(imageView, "on_load", [["width": Double(image.size.width), "height": Double(image.size.height)]])
69
+ } else {
70
+ PNEvents.emit(imageView, "on_error", ["decode failed"])
71
+ }
72
+ case .failure(let error):
73
+ PNEvents.emit(imageView, "on_error", [error.localizedDescription])
74
+ }
75
+ }
76
+ } else {
77
+ var image = UIImage(named: source)
78
+ if image == nil {
79
+ let path = source.hasPrefix("file://") ? (URL(string: source)?.path ?? source) : source
80
+ image = UIImage(contentsOfFile: path)
81
+ }
82
+ if let image = image {
83
+ setImage(imageView, image)
84
+ PNEvents.emit(imageView, "on_load", [["width": Double(image.size.width), "height": Double(image.size.height)]])
85
+ } else {
86
+ PNEvents.emit(imageView, "on_error", ["image '\(source)' not found"])
87
+ }
88
+ }
89
+ }
90
+
91
+ private func setImage(_ imageView: UIImageView, _ image: UIImage) {
92
+ let tinted = PNProps.value(mergedProps(imageView), "tint_color") ?? PNProps.value(mergedProps(imageView), "tint")
93
+ imageView.image = tinted != nil ? image.withRenderingMode(.alwaysTemplate) : image
94
+ if let state = PNViewState.existing(for: imageView) { PNLayout.invalidate(state.tag) }
95
+ }
96
+
97
+ private func loadDataURI(_ imageView: UIImageView, _ source: String) {
98
+ guard let comma = source.firstIndex(of: ","),
99
+ let data = Data(base64Encoded: String(source[source.index(after: comma)...]), options: [.ignoreUnknownCharacters]),
100
+ let image = UIImage(data: data)
101
+ else {
102
+ PNEvents.emit(imageView, "on_error", ["data URI decode failed"])
103
+ return
104
+ }
105
+ setImage(imageView, image)
106
+ PNEvents.emit(imageView, "on_load", [["width": Double(image.size.width), "height": Double(image.size.height)]])
107
+ }
108
+
109
+ /// Decode `data`, downsampling when the bitmap is more than 2x the target.
110
+ static func decode(_ data: Data, targetSize: CGSize) -> UIImage? {
111
+ guard let image = UIImage(data: data) else { return nil }
112
+ guard targetSize.width > 0, targetSize.height > 0 else { return image }
113
+ let size = image.size
114
+ if size.width <= targetSize.width * 2, size.height <= targetSize.height * 2 { return image }
115
+ let scale = min(targetSize.width * 2 / size.width, targetSize.height * 2 / size.height)
116
+ let newSize = CGSize(width: size.width * scale, height: size.height * scale)
117
+ let renderer = UIGraphicsImageRenderer(size: newSize)
118
+ return renderer.image { _ in image.draw(in: CGRect(origin: .zero, size: newSize)) }
119
+ }
120
+ }
121
+
122
+ /// Shared image pipeline: memory LRU + disk cache under `Caches/pn_images`,
123
+ /// request deduplication, delivery on the main queue.
124
+ public final class PNImageLoader {
125
+ public static let shared = PNImageLoader()
126
+
127
+ public enum LoadError: Error, LocalizedError {
128
+ case badURL
129
+ case http(Int)
130
+ case empty
131
+
132
+ public var errorDescription: String? {
133
+ switch self {
134
+ case .badURL: return "invalid image URL"
135
+ case .http(let code): return "HTTP \(code)"
136
+ case .empty: return "empty response"
137
+ }
138
+ }
139
+ }
140
+
141
+ private let memory = NSCache<NSString, NSData>()
142
+ private var inflight: [String: [(Result<Data, Error>) -> Void]] = [:]
143
+ private let session: URLSession
144
+ private let queue = DispatchQueue(label: "com.pythonnative.images", qos: .utility)
145
+ private lazy var directory: URL = {
146
+ let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSTemporaryDirectory())
147
+ let dir = caches.appendingPathComponent("pn_images", isDirectory: true)
148
+ try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
149
+ return dir
150
+ }()
151
+
152
+ private init() {
153
+ memory.totalCostLimit = 16 * 1024 * 1024
154
+ let config = URLSessionConfiguration.default
155
+ config.requestCachePolicy = .returnCacheDataElseLoad
156
+ session = URLSession(configuration: config)
157
+ }
158
+
159
+ /// Fetch `url`, delivering the raw bytes on the main queue.
160
+ public func fetch(_ url: String, completion: @escaping (Result<Data, Error>) -> Void) {
161
+ if let cached = memory.object(forKey: url as NSString) {
162
+ completion(.success(cached as Data))
163
+ return
164
+ }
165
+ if inflight[url] != nil {
166
+ inflight[url]?.append(completion)
167
+ return
168
+ }
169
+ inflight[url] = [completion]
170
+ let path = cachePath(for: url)
171
+ queue.async {
172
+ if let data = try? Data(contentsOf: path), !data.isEmpty {
173
+ self.deliver(url, .success(data))
174
+ return
175
+ }
176
+ guard let remote = URL(string: url) else {
177
+ self.deliver(url, .failure(LoadError.badURL))
178
+ return
179
+ }
180
+ let task = self.session.dataTask(with: remote) { data, response, error in
181
+ if let error = error {
182
+ self.deliver(url, .failure(error))
183
+ return
184
+ }
185
+ if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {
186
+ self.deliver(url, .failure(LoadError.http(http.statusCode)))
187
+ return
188
+ }
189
+ guard let data = data, !data.isEmpty else {
190
+ self.deliver(url, .failure(LoadError.empty))
191
+ return
192
+ }
193
+ try? data.write(to: path, options: [.atomic])
194
+ self.deliver(url, .success(data))
195
+ }
196
+ task.resume()
197
+ }
198
+ }
199
+
200
+ /// Remove every cached image (memory and disk).
201
+ public func clear() {
202
+ memory.removeAllObjects()
203
+ try? FileManager.default.removeItem(at: directory)
204
+ try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
205
+ }
206
+
207
+ private func deliver(_ url: String, _ result: Result<Data, Error>) {
208
+ DispatchQueue.main.async {
209
+ if case .success(let data) = result {
210
+ self.memory.setObject(data as NSData, forKey: url as NSString, cost: data.count)
211
+ }
212
+ let waiters = self.inflight.removeValue(forKey: url) ?? []
213
+ for waiter in waiters { waiter(result) }
214
+ }
215
+ }
216
+
217
+ private func cachePath(for url: String) -> URL {
218
+ let digest = SHA256.hash(data: Data(url.utf8)).map { String(format: "%02x", $0) }.joined()
219
+ let ext = URL(string: url)?.pathExtension ?? ""
220
+ return directory.appendingPathComponent(ext.isEmpty ? digest : "\(digest).\(ext)")
221
+ }
222
+ }