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,82 @@
1
+ import UIKit
2
+
3
+ /// Builds `CGAffineTransform`s from the `transform` style prop.
4
+ ///
5
+ /// The prop is a list of single-key dicts: `rotate` (degrees, or a
6
+ /// `"45deg"` / `"0.5rad"` string), `scale`, `scale_x`, `scale_y`,
7
+ /// `translate_x`, `translate_y`, `skew_x`, `skew_y` (degrees).
8
+ public enum PNTransform {
9
+ /// Build a transform from a spec list (or single dict). `nil` is identity.
10
+ public static func make(_ spec: Any?) -> CGAffineTransform {
11
+ var transform = CGAffineTransform.identity
12
+ guard let spec = spec, !(spec is NSNull) else { return transform }
13
+ let entries: [Any] = (spec as? [Any]) ?? [spec]
14
+ for entry in entries {
15
+ guard let dict = entry as? [String: Any] else { continue }
16
+ if let rotate = dict["rotate"] {
17
+ transform = transform.rotated(by: angle(rotate))
18
+ }
19
+ if let scale = PNProps.double(dict["scale"]) {
20
+ transform = transform.scaledBy(x: CGFloat(scale), y: CGFloat(scale))
21
+ }
22
+ if let sx = PNProps.double(dict["scale_x"]) {
23
+ transform = transform.scaledBy(x: CGFloat(sx), y: 1)
24
+ }
25
+ if let sy = PNProps.double(dict["scale_y"]) {
26
+ transform = transform.scaledBy(x: 1, y: CGFloat(sy))
27
+ }
28
+ if dict["translate_x"] != nil || dict["translate_y"] != nil {
29
+ let tx = PNProps.double(dict["translate_x"]) ?? 0
30
+ let ty = PNProps.double(dict["translate_y"]) ?? 0
31
+ transform = transform.translatedBy(x: CGFloat(tx), y: CGFloat(ty))
32
+ }
33
+ if dict["skew_x"] != nil || dict["skew_y"] != nil {
34
+ let skew = CGAffineTransform(
35
+ a: 1, b: tan(angle(dict["skew_y"] ?? 0)),
36
+ c: tan(angle(dict["skew_x"] ?? 0)), d: 1,
37
+ tx: 0, ty: 0
38
+ )
39
+ transform = skew.concatenating(transform)
40
+ }
41
+ }
42
+ return transform
43
+ }
44
+
45
+ /// Whether every component of `t` is finite.
46
+ public static func isFinite(_ t: CGAffineTransform) -> Bool {
47
+ t.a.isFinite && t.b.isFinite && t.c.isFinite && t.d.isFinite && t.tx.isFinite && t.ty.isFinite
48
+ }
49
+
50
+ /// Apply the `transform` prop to `view` (identity when `null`).
51
+ public static func apply(_ view: UIView, spec: Any?) {
52
+ let transform = make(spec)
53
+ guard isFinite(transform) else {
54
+ PNLog.rateLimited(PNLog.components, key: "set_transform:nan", "[set_transform:nan] spec=\(String(describing: spec))")
55
+ view.transform = .identity
56
+ return
57
+ }
58
+ view.transform = transform
59
+ }
60
+
61
+ /// Parse a rotation value into radians (`"90deg"`, `"1.5rad"`, or numeric degrees).
62
+ public static func angle(_ value: Any) -> CGFloat {
63
+ if let text = value as? String {
64
+ let lower = text.lowercased()
65
+ if lower.hasSuffix("deg"), let degrees = Double(lower.dropLast(3)) {
66
+ return CGFloat(degrees * .pi / 180)
67
+ }
68
+ if lower.hasSuffix("rad"), let radians = Double(lower.dropLast(3)) {
69
+ return CGFloat(radians)
70
+ }
71
+ }
72
+ return CGFloat((PNProps.double(value) ?? 0) * .pi / 180)
73
+ }
74
+
75
+ /// Decompose an affine transform into the animatable channels.
76
+ public static func decompose(_ t: CGAffineTransform) -> (translateX: CGFloat, translateY: CGFloat, scaleX: CGFloat, scaleY: CGFloat, rotateDegrees: CGFloat) {
77
+ let scaleX = sqrt(t.a * t.a + t.b * t.b)
78
+ let scaleY = sqrt(t.c * t.c + t.d * t.d)
79
+ let rotation = atan2(t.b, t.a)
80
+ return (t.tx, t.ty, scaleX, scaleY, rotation * 180 / .pi)
81
+ }
82
+ }
@@ -0,0 +1,86 @@
1
+ import UIKit
2
+
3
+ /// `View`, `Row`, and `Column`: a hit-test-aware flex container. All
4
+ /// layout semantics live in the Python layout engine; the manager only
5
+ /// applies visual props and frames.
6
+ public final class PNViewManager: PNComponentManager {
7
+ public override func makeView(props: [String: Any]) -> UIView {
8
+ PNContainerView(frame: .zero)
9
+ }
10
+ }
11
+
12
+ /// `Spacer`: an empty layout placeholder.
13
+ public final class PNSpacerManager: PNComponentManager {
14
+ public override func makeView(props: [String: Any]) -> UIView {
15
+ UIView(frame: .zero)
16
+ }
17
+
18
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
19
+ // Sizing is entirely the layout engine's; nothing visual to apply.
20
+ }
21
+ }
22
+
23
+ /// `SafeAreaView`: a plain container; insets are applied by the layout engine.
24
+ public final class PNSafeAreaViewManager: PNComponentManager {
25
+ public override func makeView(props: [String: Any]) -> UIView {
26
+ PNContainerView(frame: .zero)
27
+ }
28
+ }
29
+
30
+ /// `KeyboardAvoidingView`: a container that keeps the keyboard observer
31
+ /// installed so Python receives keyboard height updates.
32
+ public final class PNKeyboardAvoidingViewManager: PNComponentManager {
33
+ public override func makeView(props: [String: Any]) -> UIView {
34
+ PNKeyboardObserver.shared.start()
35
+ return PNContainerView(frame: .zero)
36
+ }
37
+ }
38
+
39
+ /// Publishes the keyboard height through
40
+ /// `callback("module", 0, "Host", {"event": "keyboard", "payload": {"height": h}})`.
41
+ public final class PNKeyboardObserver {
42
+ public static let shared = PNKeyboardObserver()
43
+
44
+ private var started = false
45
+ private(set) public var height: CGFloat = 0
46
+
47
+ private init() {}
48
+
49
+ /// Begin observing keyboard notifications (idempotent).
50
+ public func start() {
51
+ if started { return }
52
+ started = true
53
+ let center = NotificationCenter.default
54
+ center.addObserver(self, selector: #selector(willShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
55
+ center.addObserver(self, selector: #selector(willChange(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
56
+ center.addObserver(self, selector: #selector(willHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
57
+ }
58
+
59
+ @objc private func willShow(_ note: Notification) {
60
+ publish(Self.keyboardHeight(from: note))
61
+ }
62
+
63
+ @objc private func willChange(_ note: Notification) {
64
+ publish(Self.keyboardHeight(from: note))
65
+ }
66
+
67
+ @objc private func willHide(_ note: Notification) {
68
+ publish(0)
69
+ }
70
+
71
+ private func publish(_ value: CGFloat) {
72
+ let clamped = max(0, value)
73
+ if clamped == height { return }
74
+ height = clamped
75
+ PNModuleEvents.emit(module: "Host", event: "keyboard", payload: ["height": Double(clamped)])
76
+ }
77
+
78
+ static func keyboardHeight(from note: Notification) -> CGFloat {
79
+ guard let frame = (note.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
80
+ return 0
81
+ }
82
+ let screen = PNWindow.screenBounds()
83
+ if screen.height > 0, frame.origin.y >= screen.height { return 0 }
84
+ return frame.height
85
+ }
86
+ }
@@ -0,0 +1,87 @@
1
+ import UIKit
2
+
3
+ /// Per-view state attached to every bridge-created `UIView`.
4
+ ///
5
+ /// Managers are shared per element type, so anything a manager needs
6
+ /// to remember about one view (its merged props, style bookkeeping,
7
+ /// recognizers, suppress flags) lives here. The state is stored as an
8
+ /// associated object so it can be reached from any `UIView` reference.
9
+ public final class PNViewState {
10
+ /// The reconciler tag of the view.
11
+ public let tag: Int64
12
+ /// Element type name.
13
+ public let typeName: String
14
+ /// Every prop the view has received so far (updates merge; `null` removes).
15
+ public var props: [String: Any] = [:]
16
+ /// Free-form storage for manager-specific values (suppress flags, delegates, ...).
17
+ public var extras: [String: Any] = [:]
18
+ /// Strong references the view's manager needs to keep alive (delegates, targets).
19
+ public var retained: [AnyObject] = []
20
+ /// Recognizers installed from the `gestures` prop.
21
+ public var gestureRecognizers: [UIGestureRecognizer] = []
22
+
23
+ // Style bookkeeping used by `PNViewStyler` at frame time.
24
+ var requestedCornerRadius: CGFloat?
25
+ var cornerRadii: [CGFloat]?
26
+ var sideBorderWidths: [CGFloat]?
27
+ var sideBorderColors: [UIColor]?
28
+ var sideBorderLayers: [CALayer?] = [nil, nil, nil, nil]
29
+
30
+ init(tag: Int64, typeName: String) {
31
+ self.tag = tag
32
+ self.typeName = typeName
33
+ }
34
+
35
+ /// Names of the events wired on the element this render (`_pn_events`).
36
+ public var eventNames: Set<String> {
37
+ guard let list = props["_pn_events"] as? [Any] else { return [] }
38
+ return Set(list.compactMap { $0 as? String })
39
+ }
40
+
41
+ /// Whether the element wired a callback named `name`.
42
+ public func hasEvent(_ name: String) -> Bool {
43
+ eventNames.contains(name)
44
+ }
45
+
46
+ /// Typed access to `extras`.
47
+ public func flag(_ key: String) -> Bool {
48
+ (extras[key] as? Bool) ?? false
49
+ }
50
+
51
+ private static var key: UInt8 = 0
52
+
53
+ /// Attach a fresh state object to `view`.
54
+ @discardableResult
55
+ static func attach(_ view: UIView, tag: Int64, typeName: String) -> PNViewState {
56
+ let state = PNViewState(tag: tag, typeName: typeName)
57
+ objc_setAssociatedObject(view, &key, state, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
58
+ return state
59
+ }
60
+
61
+ /// The state attached to `view`, if any.
62
+ public static func existing(for view: UIView) -> PNViewState? {
63
+ objc_getAssociatedObject(view, &key) as? PNViewState
64
+ }
65
+
66
+ static func detach(_ view: UIView) {
67
+ objc_setAssociatedObject(view, &key, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
68
+ }
69
+ }
70
+
71
+ /// Event emission keyed by view.
72
+ public enum PNEvents {
73
+ /// Emit `name` for `view` with positional `args`. Returns Python's
74
+ /// JSON reply for request-style events, else `nil`.
75
+ @discardableResult
76
+ public static func emit(_ view: UIView, _ name: String, _ args: [Any?] = []) -> String? {
77
+ guard let state = PNViewState.existing(for: view) else { return nil }
78
+ return PNBridge.shared.emitEvent(tag: state.tag, name: name, args: args)
79
+ }
80
+
81
+ /// Emit only when the element wired a handler for `name`.
82
+ @discardableResult
83
+ public static func emitIfWired(_ view: UIView, _ name: String, _ args: [Any?] = []) -> String? {
84
+ guard let state = PNViewState.existing(for: view), state.hasEvent(name) else { return nil }
85
+ return PNBridge.shared.emitEvent(tag: state.tag, name: name, args: args)
86
+ }
87
+ }
@@ -0,0 +1,340 @@
1
+ import UIKit
2
+
3
+ /// Applies the visual props every element type shares.
4
+ ///
5
+ /// `applyCommon` is the full container set (background, overflow,
6
+ /// display, opacity, z-index, pointer events, borders, shadows,
7
+ /// transform, accessibility). `applyDecoration` is the subset leaf
8
+ /// controls use (borders, shadow, transform, accessibility, opacity),
9
+ /// mirroring the per-handler behavior of the original iOS backend.
10
+ public enum PNViewStyler {
11
+ static let cornerRadiusKeys = [
12
+ "border_top_left_radius", "border_top_right_radius",
13
+ "border_bottom_left_radius", "border_bottom_right_radius",
14
+ ]
15
+ static let sideWidthKeys = ["border_left_width", "border_top_width", "border_right_width", "border_bottom_width"]
16
+ static let sideColorKeys = ["border_left_color", "border_top_color", "border_right_color", "border_bottom_color"]
17
+ static let shadowKeys = ["shadow_color", "shadow_offset", "shadow_opacity", "shadow_radius", "elevation"]
18
+ static let maskedCornerBits: [CACornerMask] = [
19
+ .layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner,
20
+ ]
21
+ static let allCorners: CACornerMask = [
22
+ .layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner,
23
+ ]
24
+
25
+ // MARK: - Entry points
26
+
27
+ /// Apply every shared visual prop present in `props`.
28
+ public static func applyCommon(_ view: UIView, _ props: [String: Any]) {
29
+ if let color = PNColor.parse(PNProps.value(props, "background_color")) {
30
+ view.backgroundColor = color
31
+ }
32
+ if PNProps.has(props, "overflow") {
33
+ view.clipsToBounds = PNProps.string(PNProps.value(props, "overflow")) == "hidden"
34
+ }
35
+ if PNProps.has(props, "display") {
36
+ view.isHidden = PNProps.string(PNProps.value(props, "display")) == "none"
37
+ }
38
+ if let opacity = PNProps.double(PNProps.value(props, "opacity")) {
39
+ view.alpha = CGFloat(opacity)
40
+ }
41
+ if PNProps.has(props, "z_index") {
42
+ view.layer.zPosition = CGFloat(PNProps.double(PNProps.value(props, "z_index")) ?? 0)
43
+ }
44
+ if PNProps.has(props, "pointer_events") || PNProps.has(props, "hit_slop") {
45
+ applyInteraction(view, props)
46
+ }
47
+ applyBorder(view, props)
48
+ applySideBorders(view, props)
49
+ let merged = PNViewState.existing(for: view)?.props ?? props
50
+ let cornerKeysActive = cornerRadiusKeys.contains { PNProps.value(merged, $0) != nil }
51
+ if cornerRadiusKeys.contains(where: { PNProps.has(props, $0) }) || (PNProps.has(props, "border_radius") && cornerKeysActive) {
52
+ applyCornerRadii(view, merged)
53
+ }
54
+ applyShadow(view, props)
55
+ if PNProps.has(props, "transform") {
56
+ PNTransform.apply(view, spec: PNProps.value(props, "transform"))
57
+ }
58
+ applyAccessibility(view, props)
59
+ }
60
+
61
+ /// The leaf-control subset: border, shadow, transform, accessibility, opacity.
62
+ public static func applyDecoration(_ view: UIView, _ props: [String: Any]) {
63
+ applyBorder(view, props)
64
+ applyShadow(view, props)
65
+ if PNProps.has(props, "transform") {
66
+ PNTransform.apply(view, spec: PNProps.value(props, "transform"))
67
+ }
68
+ applyAccessibility(view, props)
69
+ if let opacity = PNProps.double(PNProps.value(props, "opacity")) {
70
+ view.alpha = CGFloat(opacity)
71
+ }
72
+ }
73
+
74
+ /// Re-run the bounds-dependent parts of styling after a frame change.
75
+ public static func syncFrameDependentStyles(_ view: UIView, size: CGSize) {
76
+ guard let state = PNViewState.existing(for: view) else { return }
77
+ let maxRadius = max(0, min(size.width, size.height) / 2)
78
+ if let requested = state.requestedCornerRadius, maxRadius > 0 {
79
+ view.layer.cornerRadius = min(requested, maxRadius)
80
+ } else if state.requestedCornerRadius == nil, view.layer.cornerRadius > 0, maxRadius > 0 {
81
+ view.layer.cornerRadius = min(view.layer.cornerRadius, maxRadius)
82
+ }
83
+ updateCornerMask(view, state: state, size: size)
84
+ updateSideBorderLayers(view, state: state, size: size)
85
+ }
86
+
87
+ // MARK: - Borders
88
+
89
+ static func applyBorder(_ view: UIView, _ props: [String: Any]) {
90
+ let layer = view.layer
91
+ if let radius = PNProps.double(PNProps.value(props, "border_radius")) {
92
+ let requested = CGFloat(max(0, radius))
93
+ PNViewState.existing(for: view)?.requestedCornerRadius = requested
94
+ let size = view.bounds.size
95
+ var value = requested
96
+ if size.width > 0, size.height > 0 {
97
+ value = min(requested, min(size.width, size.height) / 2)
98
+ }
99
+ layer.cornerRadius = value
100
+ // Rounded corners clip implicitly (the React Native default).
101
+ layer.masksToBounds = true
102
+ } else if PNProps.has(props, "border_radius") {
103
+ PNViewState.existing(for: view)?.requestedCornerRadius = nil
104
+ layer.cornerRadius = 0
105
+ }
106
+ if let width = PNProps.double(PNProps.value(props, "border_width")) {
107
+ layer.borderWidth = CGFloat(width)
108
+ } else if PNProps.has(props, "border_width") {
109
+ layer.borderWidth = 0
110
+ }
111
+ if let color = PNColor.parse(PNProps.value(props, "border_color")) {
112
+ layer.borderColor = color.cgColor
113
+ }
114
+ }
115
+
116
+ static func applySideBorders(_ view: UIView, _ props: [String: Any]) {
117
+ guard sideWidthKeys.contains(where: { PNProps.value(props, $0) != nil }),
118
+ let state = PNViewState.existing(for: view)
119
+ else { return }
120
+ let merged = state.props
121
+ let baseWidth = CGFloat(PNProps.double(PNProps.value(merged, "border_width")) ?? 0)
122
+ let baseColor = PNColor.parse(PNProps.value(merged, "border_color")) ?? .black
123
+ state.sideBorderWidths = sideWidthKeys.map { key in
124
+ CGFloat(PNProps.double(PNProps.value(merged, key)) ?? Double(baseWidth))
125
+ }
126
+ state.sideBorderColors = sideColorKeys.map { key in
127
+ PNColor.parse(PNProps.value(merged, key)) ?? baseColor
128
+ }
129
+ view.layer.borderWidth = 0
130
+ let size = view.bounds.size
131
+ if size.width > 0, size.height > 0 {
132
+ updateSideBorderLayers(view, state: state, size: size)
133
+ }
134
+ }
135
+
136
+ static func updateSideBorderLayers(_ view: UIView, state: PNViewState, size: CGSize) {
137
+ guard let widths = state.sideBorderWidths, let colors = state.sideBorderColors else { return }
138
+ let frames = [
139
+ CGRect(x: 0, y: 0, width: widths[0], height: size.height),
140
+ CGRect(x: 0, y: 0, width: size.width, height: widths[1]),
141
+ CGRect(x: size.width - widths[2], y: 0, width: widths[2], height: size.height),
142
+ CGRect(x: 0, y: size.height - widths[3], width: size.width, height: widths[3]),
143
+ ]
144
+ for i in 0..<4 {
145
+ if widths[i] <= 0 {
146
+ state.sideBorderLayers[i]?.removeFromSuperlayer()
147
+ state.sideBorderLayers[i] = nil
148
+ continue
149
+ }
150
+ let layer = state.sideBorderLayers[i] ?? {
151
+ let created = CALayer()
152
+ view.layer.addSublayer(created)
153
+ state.sideBorderLayers[i] = created
154
+ return created
155
+ }()
156
+ layer.backgroundColor = colors[i].cgColor
157
+ layer.frame = frames[i]
158
+ }
159
+ }
160
+
161
+ // MARK: - Per-corner radii
162
+
163
+ static func applyCornerRadii(_ view: UIView, _ merged: [String: Any]) {
164
+ guard let state = PNViewState.existing(for: view) else { return }
165
+ let anyCorner = cornerRadiusKeys.contains { PNProps.value(merged, $0) != nil }
166
+ if !anyCorner {
167
+ if state.cornerRadii != nil {
168
+ state.cornerRadii = nil
169
+ view.layer.mask = nil
170
+ view.layer.maskedCorners = allCorners
171
+ applyBorder(view, merged)
172
+ }
173
+ return
174
+ }
175
+ let base = CGFloat(max(0, PNProps.double(PNProps.value(merged, "border_radius")) ?? 0))
176
+ let radii: [CGFloat] = cornerRadiusKeys.map { key in
177
+ guard let value = PNProps.double(PNProps.value(merged, key)), value.isFinite else { return base }
178
+ return CGFloat(max(0, value))
179
+ }
180
+ let distinct = Set(radii.filter { $0 > 0 })
181
+ if distinct.count <= 1 {
182
+ state.cornerRadii = nil
183
+ let value = distinct.first ?? 0
184
+ var mask: CACornerMask = []
185
+ for (bit, r) in zip(maskedCornerBits, radii) where r > 0 {
186
+ mask.insert(bit)
187
+ }
188
+ view.layer.mask = nil
189
+ view.layer.cornerRadius = value
190
+ view.layer.maskedCorners = mask.isEmpty ? allCorners : mask
191
+ view.layer.masksToBounds = true
192
+ state.requestedCornerRadius = value
193
+ return
194
+ }
195
+ state.requestedCornerRadius = nil
196
+ state.cornerRadii = radii
197
+ view.layer.cornerRadius = 0
198
+ view.layer.maskedCorners = allCorners
199
+ let size = view.bounds.size
200
+ if size.width > 0, size.height > 0 {
201
+ updateCornerMask(view, state: state, size: size)
202
+ }
203
+ }
204
+
205
+ static func updateCornerMask(_ view: UIView, state: PNViewState, size: CGSize) {
206
+ guard let radii = state.cornerRadii, size.width > 0, size.height > 0 else { return }
207
+ var scale: CGFloat = 1
208
+ let (tl0, tr0, bl0, br0) = (radii[0], radii[1], radii[2], radii[3])
209
+ for (sum, extent) in [(tl0 + tr0, size.width), (bl0 + br0, size.width), (tl0 + bl0, size.height), (tr0 + br0, size.height)]
210
+ where sum > extent && extent > 0 {
211
+ scale = min(scale, extent / sum)
212
+ }
213
+ let (tl, tr, bl, br) = (tl0 * scale, tr0 * scale, bl0 * scale, br0 * scale)
214
+ let w = size.width, h = size.height
215
+ let path = UIBezierPath()
216
+ let halfPi = CGFloat.pi / 2
217
+ path.move(to: CGPoint(x: tl, y: 0))
218
+ path.addLine(to: CGPoint(x: w - tr, y: 0))
219
+ if tr > 0 { path.addArc(withCenter: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: -halfPi, endAngle: 0, clockwise: true) }
220
+ path.addLine(to: CGPoint(x: w, y: h - br))
221
+ if br > 0 { path.addArc(withCenter: CGPoint(x: w - br, y: h - br), radius: br, startAngle: 0, endAngle: halfPi, clockwise: true) }
222
+ path.addLine(to: CGPoint(x: bl, y: h))
223
+ if bl > 0 { path.addArc(withCenter: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: halfPi, endAngle: .pi, clockwise: true) }
224
+ path.addLine(to: CGPoint(x: 0, y: tl))
225
+ if tl > 0 { path.addArc(withCenter: CGPoint(x: tl, y: tl), radius: tl, startAngle: .pi, endAngle: .pi + halfPi, clockwise: true) }
226
+ path.close()
227
+ let shape = CAShapeLayer()
228
+ shape.frame = CGRect(origin: .zero, size: size)
229
+ shape.path = path.cgPath
230
+ view.layer.mask = shape
231
+ }
232
+
233
+ // MARK: - Shadow
234
+
235
+ static func applyShadow(_ view: UIView, _ props: [String: Any]) {
236
+ let layer = view.layer
237
+ let hasShadow = shadowKeys.contains { PNProps.value(props, $0) != nil }
238
+ if hasShadow, PNProps.string(PNProps.value(props, "overflow")) != "hidden" {
239
+ layer.masksToBounds = false
240
+ view.clipsToBounds = false
241
+ }
242
+ if let color = PNColor.parse(PNProps.value(props, "shadow_color")) {
243
+ layer.shadowColor = color.cgColor
244
+ }
245
+ if let opacity = PNProps.double(PNProps.value(props, "shadow_opacity")) {
246
+ layer.shadowOpacity = Float(opacity)
247
+ }
248
+ if let radius = PNProps.double(PNProps.value(props, "shadow_radius")) {
249
+ layer.shadowRadius = CGFloat(radius)
250
+ }
251
+ if let offset = PNProps.value(props, "shadow_offset") {
252
+ let (dx, dy) = shadowOffset(offset)
253
+ layer.shadowOffset = CGSize(width: dx, height: dy)
254
+ }
255
+ }
256
+
257
+ /// Coerce a `shadow_offset` / `text_shadow_offset` value to `(dx, dy)`.
258
+ public static func shadowOffset(_ value: Any?) -> (CGFloat, CGFloat) {
259
+ if let dict = value as? [String: Any] {
260
+ return (CGFloat(PNProps.double(dict["width"]) ?? 0), CGFloat(PNProps.double(dict["height"]) ?? 0))
261
+ }
262
+ if let list = value as? [Any], list.count >= 2 {
263
+ return (CGFloat(PNProps.double(list[0]) ?? 0), CGFloat(PNProps.double(list[1]) ?? 0))
264
+ }
265
+ return (0, 0)
266
+ }
267
+
268
+ // MARK: - Interaction
269
+
270
+ static func applyInteraction(_ view: UIView, _ props: [String: Any]) {
271
+ guard let container = view as? PNContainerView else {
272
+ PNLog.once(PNLog.components, key: "interaction:\(PNViewState.existing(for: view)?.tag ?? 0)",
273
+ "pointer_events / hit_slop only work on container elements; ignored")
274
+ return
275
+ }
276
+ if PNProps.has(props, "pointer_events") {
277
+ let mode = PNProps.string(PNProps.value(props, "pointer_events"))
278
+ container.pointerEvents = ["none", "box_none", "box_only"].contains(mode ?? "") ? mode : nil
279
+ }
280
+ if PNProps.has(props, "hit_slop") {
281
+ container.hitSlop = hitSlop(PNProps.value(props, "hit_slop"))
282
+ }
283
+ }
284
+
285
+ /// Normalize a `hit_slop` prop into edge insets.
286
+ public static func hitSlop(_ value: Any?) -> UIEdgeInsets {
287
+ if let dict = value as? [String: Any] {
288
+ return UIEdgeInsets(
289
+ top: CGFloat(PNProps.double(dict["top"]) ?? 0), left: CGFloat(PNProps.double(dict["left"]) ?? 0),
290
+ bottom: CGFloat(PNProps.double(dict["bottom"]) ?? 0), right: CGFloat(PNProps.double(dict["right"]) ?? 0)
291
+ )
292
+ }
293
+ if let uniform = PNProps.double(value) {
294
+ let v = CGFloat(uniform)
295
+ return UIEdgeInsets(top: v, left: v, bottom: v, right: v)
296
+ }
297
+ return .zero
298
+ }
299
+
300
+ // MARK: - Accessibility
301
+
302
+ static let traitByRole: [String: UIAccessibilityTraits] = [
303
+ "button": .button, "link": .link, "image": .image, "search": .searchField,
304
+ "keyboard_key": .keyboardKey, "static_text": .staticText, "summary_element": .summaryElement,
305
+ "adjustable": .adjustable, "header": .header, "selected": .selected, "checkbox": .button, "none": [],
306
+ ]
307
+
308
+ static func applyAccessibility(_ view: UIView, _ props: [String: Any]) {
309
+ if PNProps.has(props, "accessible") {
310
+ view.isAccessibilityElement = PNProps.bool(PNProps.value(props, "accessible")) ?? false
311
+ }
312
+ if PNProps.has(props, "accessibility_label") {
313
+ view.accessibilityLabel = PNProps.string(PNProps.value(props, "accessibility_label")) ?? ""
314
+ }
315
+ if PNProps.has(props, "accessibility_hint") {
316
+ view.accessibilityHint = PNProps.string(PNProps.value(props, "accessibility_hint")) ?? ""
317
+ }
318
+ if PNProps.has(props, "test_id") {
319
+ view.accessibilityIdentifier = PNProps.string(PNProps.value(props, "test_id"))
320
+ }
321
+ let role = PNProps.string(PNProps.value(props, "accessibility_role"))
322
+ let state = PNProps.dict(PNProps.value(props, "accessibility_state"))
323
+ if role == nil, state == nil { return }
324
+ var traits: UIAccessibilityTraits = []
325
+ if let role = role {
326
+ traits.formUnion(traitByRole[role.lowercased()] ?? [])
327
+ }
328
+ if let state = state {
329
+ if PNProps.bool(state["selected"]) == true || PNProps.bool(state["checked"]) == true {
330
+ traits.insert(.selected)
331
+ }
332
+ if PNProps.bool(state["disabled"]) == true { traits.insert(.notEnabled) }
333
+ if PNProps.bool(state["busy"]) == true { traits.insert(.updatesFrequently) }
334
+ if let expanded = PNProps.bool(state["expanded"]) {
335
+ view.accessibilityValue = expanded ? "expanded" : "collapsed"
336
+ }
337
+ }
338
+ view.accessibilityTraits = traits
339
+ }
340
+ }