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,312 @@
1
+ import UIKit
2
+
3
+ /// Drives the `animate(tag, request)` protocol for animatable props
4
+ /// (`opacity`, `background_color`, `color`, `translate_x`, `translate_y`,
5
+ /// `scale`, `scale_x`, `scale_y`, `rotate`).
6
+ ///
7
+ /// - `set` applies one Python-driven frame immediately.
8
+ /// - `start` runs `timing` / `spring` with `UIViewPropertyAnimator` and
9
+ /// `decay` with a display-link integrator; completion posts
10
+ /// `callback("animation", 0, "", {"id": n, "finished": bool})`.
11
+ /// - `cancel` stops the animation and returns the presentation value.
12
+ public final class PNAnimator {
13
+ public static let shared = PNAnimator()
14
+
15
+ /// Prop names accepted by `set` and `start`.
16
+ public static let animatableProps: Set<String> = [
17
+ "opacity", "background_color", "color", "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate",
18
+ ]
19
+
20
+ final class Running {
21
+ weak var view: UIView?
22
+ let prop: String
23
+ var animator: UIViewPropertyAnimator?
24
+ var decay: PNDecayDriver?
25
+ init(view: UIView, prop: String) {
26
+ self.view = view
27
+ self.prop = prop
28
+ }
29
+ }
30
+
31
+ private var running: [Int64: Running] = [:]
32
+
33
+ private init() {}
34
+
35
+ // MARK: - Request routing
36
+
37
+ /// Handle one decoded `animate` request for the view with `tag`.
38
+ public func handle(tag: Int64, request: [String: Any]) -> Any? {
39
+ let op = PNProps.string(request["op"]) ?? ""
40
+ switch op {
41
+ case "graph":
42
+ PNAnimationGraph.install(request["graph"] as? [String: Any] ?? [:])
43
+ return nil
44
+ case "set":
45
+ guard let record = PNViewRegistry.shared.resolve(tag), let prop = PNProps.string(request["prop"]) else { return nil }
46
+ record.manager.setAnimatedProperty(view: record.view, prop: prop, value: request["value"])
47
+ return nil
48
+ case "start":
49
+ guard let record = PNViewRegistry.shared.resolve(tag), let prop = PNProps.string(request["prop"]),
50
+ let id = PNProps.int(request["id"])
51
+ else { return ["ok": false] }
52
+ let spec = PNProps.dict(request["spec"]) ?? [:]
53
+ if prop.hasPrefix("_pn_graph:"), let node = Int64(prop.dropFirst(10)) {
54
+ return ["ok": PNAnimationGraph.start(Int64(id), node: node, spec: spec)]
55
+ }
56
+ let ok = record.manager.startAnimation(view: record.view, id: Int64(id), prop: prop, spec: spec)
57
+ return ["ok": ok]
58
+ case "cancel":
59
+ guard let id = PNProps.int(request["id"]) else { return nil }
60
+ if let value = PNAnimationGraph.cancel(Int64(id)) { return ["value": value] }
61
+ let value: Any?
62
+ if let record = PNViewRegistry.shared.resolve(tag) {
63
+ value = record.manager.cancelAnimation(view: record.view, id: Int64(id))
64
+ } else {
65
+ value = cancel(id: Int64(id))
66
+ }
67
+ guard let value = value else { return nil }
68
+ return ["value": value]
69
+ default:
70
+ PNLog.rateLimited(PNLog.animation, key: "op:\(op)", "unknown animate op '\(op)'")
71
+ return nil
72
+ }
73
+ }
74
+
75
+ // MARK: - Frame application
76
+
77
+ /// Apply one value of `prop` to `view` immediately.
78
+ public func applyValue(view: UIView, prop: String, value: Any?) {
79
+ if prop.hasPrefix("_pn_graph:"), let node = Int64(prop.dropFirst(10)), let value = PNProps.double(value) {
80
+ PNAnimationGraph.set(node, value)
81
+ return
82
+ }
83
+ switch prop {
84
+ case "opacity":
85
+ if let v = PNProps.double(value) { view.alpha = CGFloat(v) }
86
+ case "background_color":
87
+ if let color = PNColor.parse(value) { view.backgroundColor = color }
88
+ case "color":
89
+ guard let color = PNColor.parse(value) else { return }
90
+ if let label = view as? UILabel {
91
+ label.textColor = color
92
+ } else if let field = view as? UITextField {
93
+ field.textColor = color
94
+ } else if let textView = view as? UITextView {
95
+ textView.textColor = color
96
+ } else if let button = view as? UIButton {
97
+ button.setTitleColor(color, for: .normal)
98
+ }
99
+ case "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate":
100
+ guard let v = PNProps.double(value), v.isFinite else { return }
101
+ PNTransform.apply(view, spec: [[prop: v]])
102
+ default:
103
+ PNLog.once(PNLog.animation, key: "prop:\(prop)", "ignoring unknown animated prop '\(prop)'")
104
+ }
105
+ }
106
+
107
+ /// Read the value of `prop` currently on screen (presentation layer when animating).
108
+ public func presentationValue(view: UIView, prop: String) -> Any? {
109
+ let layer = view.layer.presentation() ?? view.layer
110
+ switch prop {
111
+ case "opacity":
112
+ return Double(layer.opacity)
113
+ case "background_color":
114
+ return layer.backgroundColor.map { PNColor.hexString(UIColor(cgColor: $0)) }
115
+ case "translate_x", "translate_y", "scale", "scale_x", "scale_y", "rotate":
116
+ let t = layer.affineTransform()
117
+ let parts = PNTransform.decompose(t)
118
+ switch prop {
119
+ case "translate_x": return Double(parts.translateX)
120
+ case "translate_y": return Double(parts.translateY)
121
+ case "scale", "scale_x": return Double(parts.scaleX)
122
+ case "scale_y": return Double(parts.scaleY)
123
+ default: return Double(parts.rotateDegrees)
124
+ }
125
+ default:
126
+ return nil
127
+ }
128
+ }
129
+
130
+ // MARK: - Start / cancel
131
+
132
+ /// Start a native animation. Returns `false` when the spec must be
133
+ /// ticked by Python instead.
134
+ public func start(view: UIView, id: Int64, prop: String, spec: [String: Any]) -> Bool {
135
+ guard PNAnimator.animatableProps.contains(prop), Thread.isMainThread else { return false }
136
+ let kind = PNProps.string(spec["kind"]) ?? ""
137
+ let entry = Running(view: view, prop: prop)
138
+ switch kind {
139
+ case "timing", "spring":
140
+ guard spec["to"] != nil else { return false }
141
+ if let from = spec["from"] { applyValue(view: view, prop: prop, value: from) }
142
+ let timing = PNAnimator.timingParameters(kind: kind, spec: spec)
143
+ let animator = UIViewPropertyAnimator(duration: timing.duration, timingParameters: timing.parameters)
144
+ animator.isUserInteractionEnabled = true
145
+ animator.addAnimations { [weak self, weak view] in
146
+ guard let self = self, let view = view else { return }
147
+ self.applyValue(view: view, prop: prop, value: spec["to"])
148
+ }
149
+ animator.addCompletion { [weak self] position in
150
+ guard let self = self, self.running[id] != nil else { return }
151
+ self.running.removeValue(forKey: id)
152
+ PNAnimator.reportCompletion(id: id, finished: position == .end)
153
+ }
154
+ entry.animator = animator
155
+ running[id] = entry
156
+ let delay = max(0, (PNProps.double(spec["delay_ms"]) ?? 0) / 1000)
157
+ animator.startAnimation(afterDelay: delay)
158
+ return true
159
+ case "decay":
160
+ guard let from = PNProps.double(spec["from"]), let velocity = PNProps.double(spec["velocity"]) else { return false }
161
+ let deceleration = max(1e-6, PNProps.double(spec["deceleration"]) ?? 0.997)
162
+ let driver = PNDecayDriver(from: from, velocity: velocity, deceleration: deceleration) { [weak self, weak view] value in
163
+ guard let self = self, let view = view else { return }
164
+ self.applyValue(view: view, prop: prop, value: value)
165
+ } completion: { [weak self] in
166
+ guard let self = self, self.running[id] != nil else { return }
167
+ self.running.removeValue(forKey: id)
168
+ PNAnimator.reportCompletion(id: id, finished: true)
169
+ }
170
+ entry.decay = driver
171
+ running[id] = entry
172
+ driver.start()
173
+ return true
174
+ default:
175
+ return false
176
+ }
177
+ }
178
+
179
+ /// Stop animation `id`, leaving the view at its presentation value.
180
+ /// Returns that value (or `nil` when the animation isn't running).
181
+ public func cancel(id: Int64) -> Any? {
182
+ guard let entry = running.removeValue(forKey: id) else { return nil }
183
+ var value: Any?
184
+ if let view = entry.view {
185
+ value = presentationValue(view: view, prop: entry.prop)
186
+ }
187
+ if let animator = entry.animator {
188
+ animator.stopAnimation(true)
189
+ }
190
+ if let decay = entry.decay {
191
+ value = decay.currentValue
192
+ decay.stop()
193
+ }
194
+ if let view = entry.view, let value = value {
195
+ view.layer.removeAllAnimations()
196
+ applyValue(view: view, prop: entry.prop, value: value)
197
+ }
198
+ return value
199
+ }
200
+
201
+ /// Drop bookkeeping for animations targeting `view` (on destroy).
202
+ public func forget(view: UIView) {
203
+ for (id, entry) in running where entry.view === view || entry.view == nil {
204
+ entry.animator?.stopAnimation(true)
205
+ entry.decay?.stop()
206
+ running.removeValue(forKey: id)
207
+ }
208
+ }
209
+
210
+ // MARK: - Helpers
211
+
212
+ static func reportCompletion(id: Int64, finished: Bool) {
213
+ PNBridge.shared.callPython(kind: "animation", tag: 0, name: "", payload: PNJSON.encode(["id": id, "finished": finished]))
214
+ }
215
+
216
+ /// Resolve `timing` / `spring` specs to UIKit timing parameters.
217
+ static func timingParameters(kind: String, spec: [String: Any]) -> (duration: TimeInterval, parameters: UITimingCurveProvider) {
218
+ if kind == "spring" {
219
+ let stiffness = max(1e-3, PNProps.double(spec["stiffness"]) ?? 100)
220
+ let damping = max(1e-3, PNProps.double(spec["damping"]) ?? 10)
221
+ let mass = max(1e-3, PNProps.double(spec["mass"]) ?? 1)
222
+ let omega0 = sqrt(stiffness / mass)
223
+ let zeta = damping / (2 * sqrt(stiffness * mass))
224
+ let duration = zeta < 1 ? min(10, max(0.15, 4 / max(0.05, zeta * omega0))) : min(10, max(0.15, 4 / omega0))
225
+ let distance = abs((PNProps.double(spec["to"]) ?? 0) - (PNProps.double(spec["from"]) ?? 0))
226
+ let v0 = PNProps.double(spec["initial_velocity"]) ?? 0
227
+ let normalized = distance > 1e-9 ? v0 / distance : 0
228
+ let parameters = UISpringTimingParameters(
229
+ mass: CGFloat(mass), stiffness: CGFloat(stiffness), damping: CGFloat(damping),
230
+ initialVelocity: CGVector(dx: normalized, dy: normalized)
231
+ )
232
+ return (duration, parameters)
233
+ }
234
+ let duration = max(0, (PNProps.double(spec["duration_ms"]) ?? 300) / 1000)
235
+ return (duration, curve(for: spec["easing"]))
236
+ }
237
+
238
+ /// Map an easing name (or `[x1, y1, x2, y2]` control points) to a timing curve.
239
+ static func curve(for easing: Any?) -> UITimingCurveProvider {
240
+ if let points = easing as? [Any], points.count == 4 {
241
+ let values = points.compactMap { PNProps.double($0) }
242
+ if values.count == 4 {
243
+ return UICubicTimingParameters(
244
+ controlPoint1: CGPoint(x: values[0], y: values[1]), controlPoint2: CGPoint(x: values[2], y: values[3])
245
+ )
246
+ }
247
+ }
248
+ switch PNProps.string(easing) ?? "ease_in_out" {
249
+ case "linear": return UICubicTimingParameters(animationCurve: .linear)
250
+ case "ease_in", "ease_in_quad": return UICubicTimingParameters(animationCurve: .easeIn)
251
+ case "ease_out", "ease_out_quad": return UICubicTimingParameters(animationCurve: .easeOut)
252
+ case "ease", "ease_in_out": return UICubicTimingParameters(animationCurve: .easeInOut)
253
+ case "bounce":
254
+ // No CoreAnimation equivalent; approximate with an overshoot curve.
255
+ return UICubicTimingParameters(controlPoint1: CGPoint(x: 0.34, y: 1.56), controlPoint2: CGPoint(x: 0.64, y: 1))
256
+ default: return UICubicTimingParameters(animationCurve: .easeInOut)
257
+ }
258
+ }
259
+ }
260
+
261
+ /// Integrates the `decay` model on a `CADisplayLink`, mirroring the
262
+ /// Python ticker: `v(t) = v0 * e^(-k * 1000 * t)` with `t` in seconds and
263
+ /// `v0` in units per second, so the closed-form position is
264
+ /// `from + v0 / (k * 1000) * (1 - e^(-k * 1000 * t))`.
265
+ final class PNDecayDriver {
266
+ private(set) var currentValue: Double
267
+ private let from: Double
268
+ private let velocity0: Double
269
+ private let k: Double
270
+ private let frame: (Double) -> Void
271
+ private let completion: () -> Void
272
+ private var link: CADisplayLink?
273
+ private var startTime: CFTimeInterval = 0
274
+ private let restVelocity = 0.001
275
+
276
+ init(from: Double, velocity: Double, deceleration: Double, frame: @escaping (Double) -> Void, completion: @escaping () -> Void) {
277
+ self.from = from
278
+ currentValue = from
279
+ velocity0 = velocity
280
+ k = deceleration
281
+ self.frame = frame
282
+ self.completion = completion
283
+ }
284
+
285
+ func start() {
286
+ startTime = CACurrentMediaTime()
287
+ if abs(velocity0) < restVelocity {
288
+ completion()
289
+ return
290
+ }
291
+ let link = CADisplayLink(target: self, selector: #selector(tick(_:)))
292
+ link.add(to: .main, forMode: .common)
293
+ self.link = link
294
+ }
295
+
296
+ func stop() {
297
+ link?.invalidate()
298
+ link = nil
299
+ }
300
+
301
+ @objc private func tick(_ link: CADisplayLink) {
302
+ let elapsed = max(0, link.targetTimestamp - startTime)
303
+ let decayFactor = exp(-k * 1000 * elapsed)
304
+ let velocity = velocity0 * decayFactor
305
+ currentValue = from + velocity0 / (k * 1000) * (1 - decayFactor)
306
+ frame(currentValue)
307
+ if abs(velocity) < restVelocity {
308
+ stop()
309
+ completion()
310
+ }
311
+ }
312
+ }
@@ -0,0 +1,255 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// The version of the wire protocol compiled into this library. Python
5
+ /// refuses to start when `pythonnative.bridge.PROTOCOL_VERSION` differs.
6
+ public let PNProtocolVersion: Int32 = 2
7
+
8
+ /// The C signature Python registers through `pn_bridge_set_callback`.
9
+ ///
10
+ /// `(kind, tag, name, payload_json) -> optional JSON string owned by
11
+ /// Python and valid until the next callback returns`.
12
+ public typealias PNCallbackFn = @convention(c) (
13
+ UnsafePointer<CChar>?, Int64, UnsafePointer<CChar>?, UnsafePointer<CChar>?
14
+ ) -> UnsafePointer<CChar>?
15
+
16
+ /// Swift-side entry point for the reverse direction (native -> Python) and
17
+ /// the owner of the registered C callback.
18
+ public final class PNBridge {
19
+ public static let shared = PNBridge()
20
+
21
+ private let pythonQueue = DispatchQueue(label: "dev.pythonnative.events")
22
+ private struct Message {
23
+ let kind: String; let tag: Int64; let name: String; let payload: String
24
+ var continuous: Bool {
25
+ guard kind == "event" else { return false }
26
+ if ["on_scroll", "on_selection_change", "on_gesture_update"].contains(name) { return true }
27
+ if name.hasPrefix("gesture:"), let args = PNJSON.decodeObject(payload)["args"] as? [[String: Any]] {
28
+ return args.first?["state"] as? String == "changed"
29
+ }
30
+ return false
31
+ }
32
+ }
33
+ private let mailboxLock = NSLock()
34
+ private var mailbox: [Message] = []
35
+ private var draining = false
36
+ private var callback: PNCallbackFn?
37
+ private var registrationWaiters: [() -> Void] = []
38
+
39
+ private init() {
40
+ // Reference every exported entry point so the linker never
41
+ // dead-strips them out of the host executable; Python resolves
42
+ // them through `dlsym` at runtime.
43
+ _ = PNBridge.exportedSymbols
44
+ }
45
+
46
+ /// Whether Python has registered its callback yet.
47
+ public var hasCallback: Bool { callback != nil }
48
+
49
+ /// Install (or clear) the Python callback. Runs any pending
50
+ /// `whenCallbackRegistered` blocks when a callback becomes available.
51
+ public func setCallback(_ fn: PNCallbackFn?) {
52
+ callback = fn
53
+ guard fn != nil else { return }
54
+ let waiters = registrationWaiters
55
+ registrationWaiters = []
56
+ for waiter in waiters { waiter() }
57
+ }
58
+
59
+ /// Run `block` immediately if the callback is registered, otherwise
60
+ /// once it is. Used to buffer deep links and other early events.
61
+ public func whenCallbackRegistered(_ block: @escaping () -> Void) {
62
+ if callback != nil {
63
+ block()
64
+ } else {
65
+ registrationWaiters.append(block)
66
+ }
67
+ }
68
+
69
+ /// Invoke the Python callback. Returns the JSON string Python returned
70
+ /// (copied immediately), or `nil` when Python returned NULL or no
71
+ /// callback is registered.
72
+ @discardableResult
73
+ public func callPython(kind: String, tag: Int64, name: String, payload: String) -> String? {
74
+ let message = Message(kind: kind, tag: tag, name: name, payload: payload)
75
+ mailboxLock.lock()
76
+ if message.continuous, let last = mailbox.last, last.continuous,
77
+ last.tag == tag, last.name == name {
78
+ mailbox[mailbox.count - 1] = message
79
+ } else { mailbox.append(message) }
80
+ let schedule = !draining
81
+ draining = true
82
+ mailboxLock.unlock()
83
+ if schedule { pythonQueue.async { [self] in drainMailbox() } }
84
+ return nil
85
+ }
86
+
87
+ private func drainMailbox() {
88
+ while true {
89
+ mailboxLock.lock()
90
+ if mailbox.isEmpty { draining = false; mailboxLock.unlock(); return }
91
+ let message = mailbox.removeFirst()
92
+ mailboxLock.unlock()
93
+ guard let callback = callback else { continue }
94
+ message.kind.withCString { kind in
95
+ message.name.withCString { name in
96
+ message.payload.withCString { payload in
97
+ _ = callback(kind, message.tag, name, payload)
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ static func onUI<T>(_ body: () -> T) -> T {
105
+ if Thread.isMainThread { return body() }
106
+ return DispatchQueue.main.sync(execute: body)
107
+ }
108
+
109
+ /// Emit a view event: `callback("event", tag, name, args_array_json)`.
110
+ @discardableResult
111
+ public func emitEvent(tag: Int64, name: String, args: [Any?]) -> String? {
112
+ PNAnimationGraph.event(tag, name, args)
113
+ var editRevision = 0
114
+ if name == "on_change", let view = PNViewRegistry.shared.view(for: tag),
115
+ view is UITextInput, let state = PNViewState.existing(for: view) {
116
+ editRevision = (state.extras["edit_revision"] as? Int ?? 0) + 1
117
+ state.extras["edit_revision"] = editRevision
118
+ }
119
+ return callPython(kind: "event", tag: tag, name: name, payload: PNCommit.event(args, editRevision: editRevision))
120
+ }
121
+
122
+ /// Log when an entry point is reached off the main thread. Python
123
+ /// always calls from the main thread, so this only guards misuse.
124
+ static func ensureMainThread(_ context: String) {
125
+ if !Thread.isMainThread {
126
+ PNLog.rateLimited(PNLog.bridge, key: "off-main:\(context)", "\(context) called off the main thread; running inline")
127
+ }
128
+ }
129
+
130
+ /// Copy a Swift string into a `strdup`'d buffer Python frees via `pn_bridge_free`.
131
+ static func duplicate(_ string: String?) -> UnsafeMutablePointer<CChar>? {
132
+ guard let string = string else { return nil }
133
+ return strdup(string)
134
+ }
135
+
136
+ private static let exportedSymbols: [Any] = [
137
+ pn_bridge_apply as Any,
138
+ pn_bridge_measure as Any,
139
+ pn_bridge_command as Any,
140
+ pn_bridge_animate as Any,
141
+ pn_bridge_call as Any,
142
+ pn_bridge_free as Any,
143
+ pn_bridge_set_callback as Any,
144
+ pn_bridge_protocol_version as Any,
145
+ ]
146
+ }
147
+
148
+ // MARK: - C exports
149
+
150
+ /// Apply one serialized transaction (a JSON array of ops).
151
+ @_cdecl("pn_bridge_apply")
152
+ public func pn_bridge_apply(_ transactionJSON: UnsafePointer<CChar>?) -> UnsafeMutablePointer<CChar>? {
153
+ guard let transactionJSON = transactionJSON else { return nil }
154
+ let json = String(cString: transactionJSON)
155
+ return PNBridge.onUI { PNBridge.duplicate(PNCommit.apply(json)) }
156
+ }
157
+
158
+ /// Measure the view with `tag` under the given constraints (`1e6` means unconstrained).
159
+ @_cdecl("pn_bridge_measure")
160
+ public func pn_bridge_measure(
161
+ _ tag: Int64, _ maxW: Double, _ maxH: Double,
162
+ _ outW: UnsafeMutablePointer<Double>?, _ outH: UnsafeMutablePointer<Double>?
163
+ ) {
164
+ PNBridge.onUI {
165
+
166
+ PNBridge.ensureMainThread("pn_bridge_measure")
167
+ var size = CGSize.zero
168
+ if let record = PNViewRegistry.shared.resolve(tag) {
169
+ size = record.manager.measure(view: record.view, maxW: CGFloat(maxW), maxH: CGFloat(maxH))
170
+ }
171
+ outW?.pointee = size.width.isFinite ? Double(size.width) : 0
172
+ outH?.pointee = size.height.isFinite ? Double(size.height) : 0
173
+ }
174
+ }
175
+
176
+ /// Run an imperative command on one view; returns an optional JSON result.
177
+ @_cdecl("pn_bridge_command")
178
+ public func pn_bridge_command(
179
+ _ tag: Int64, _ name: UnsafePointer<CChar>?, _ argsJSON: UnsafePointer<CChar>?
180
+ ) -> UnsafeMutablePointer<CChar>? {
181
+ return PNBridge.onUI {
182
+
183
+ PNBridge.ensureMainThread("pn_bridge_command")
184
+ guard let name = name, let record = PNViewRegistry.shared.resolve(tag) else { return nil }
185
+ let args = PNJSON.decodeObject(argsJSON.map { String(cString: $0) })
186
+ let result = record.manager.command(view: record.view, name: String(cString: name), args: args)
187
+ guard let result = result else { return nil }
188
+ return PNBridge.duplicate(PNJSON.encode(result))
189
+ }
190
+ }
191
+
192
+ /// Drive a view's animatable properties (`set` / `start` / `cancel`).
193
+ @_cdecl("pn_bridge_animate")
194
+ public func pn_bridge_animate(
195
+ _ tag: Int64, _ requestJSON: UnsafePointer<CChar>?
196
+ ) -> UnsafeMutablePointer<CChar>? {
197
+ return PNBridge.onUI {
198
+
199
+ PNBridge.ensureMainThread("pn_bridge_animate")
200
+ let request = PNJSON.decodeObject(requestJSON.map { String(cString: $0) })
201
+ let result = PNAnimator.shared.handle(tag: tag, request: request)
202
+ guard let result = result else { return nil }
203
+ return PNBridge.duplicate(PNJSON.encode(result))
204
+ }
205
+ }
206
+
207
+ /// Call a native module method. The result is `{"ok":..}`, `{"pending":true}`, or an error envelope.
208
+ @_cdecl("pn_bridge_call")
209
+ public func pn_bridge_call(
210
+ _ module: UnsafePointer<CChar>?, _ method: UnsafePointer<CChar>?, _ argsJSON: UnsafePointer<CChar>?
211
+ ) -> UnsafeMutablePointer<CChar>? {
212
+ return PNBridge.onUI {
213
+
214
+ PNBridge.ensureMainThread("pn_bridge_call")
215
+ guard let module = module, let method = method else {
216
+ return PNBridge.duplicate(PNJSON.encode(["ok": false, "error": "missing module or method"]))
217
+ }
218
+ let envelope = PNJSON.decodeObject(argsJSON.map { String(cString: $0) })
219
+ if String(cString: module) == "Runtime" {
220
+ return PNBridge.duplicate(PNJSON.encode(["ok": true, "value": [
221
+ "protocol": 2, "yoga": "3.2.1", "schema": PNContracts.fingerprint,
222
+ "animation_graph": true, "logical_lists": true, "native_layout": true]]))
223
+ }
224
+ if String(cString: module) == "Layout" {
225
+ return PNBridge.duplicate(PNJSON.encode(["ok": true, "value": PNLayout.compute(envelope["args"] as? [String: Any] ?? [:])]))
226
+ }
227
+ let result = PNModuleDispatcher.shared.call(
228
+ module: String(cString: module), method: String(cString: method), envelope: envelope
229
+ )
230
+ return PNBridge.duplicate(PNJSON.encode(result))
231
+ }
232
+ }
233
+
234
+ /// Free a string previously returned by a `pn_bridge_*` function.
235
+ @_cdecl("pn_bridge_free")
236
+ public func pn_bridge_free(_ ptr: UnsafeMutablePointer<CChar>?) {
237
+ free(ptr)
238
+ }
239
+
240
+ /// Register the Python callback used for every native -> Python message.
241
+ @_cdecl("pn_bridge_set_callback")
242
+ public func pn_bridge_set_callback(_ fn: PNCallbackFn?) {
243
+ PNBridge.onUI {
244
+
245
+ PNBridge.ensureMainThread("pn_bridge_set_callback")
246
+ PNRegistry.shared.ensureBuiltins()
247
+ PNBridge.shared.setCallback(fn)
248
+ }
249
+ }
250
+
251
+ /// The protocol version this library speaks.
252
+ @_cdecl("pn_bridge_protocol_version")
253
+ public func pn_bridge_protocol_version() -> Int32 {
254
+ PNProtocolVersion
255
+ }
@@ -0,0 +1,104 @@
1
+ import Foundation
2
+
3
+ /// Validates complete surface transactions before invoking a view manager.
4
+ enum PNCommit {
5
+ private static var application = ""
6
+ private static var surface = 0
7
+ private static var revision = 0
8
+ private static var parents: [Int64: Int64] = [:]
9
+ private static var live: Set<Int64> = []
10
+ private static var types: [Int64: String] = [:]
11
+ private static var failed = false
12
+
13
+ private static var sequence = 0
14
+ static func event(_ args: [Any?], editRevision: Int = 0) -> String {
15
+ sequence += 1
16
+ return PNJSON.encode(["application": application, "surface": surface,
17
+ "revision": revision, "sequence": sequence, "args": args, "edit_revision": editRevision])
18
+ }
19
+
20
+ static func apply(_ json: String) -> String {
21
+ let envelope = PNJSON.decodeObject(json)
22
+ let app = envelope["application"] as? String ?? ""
23
+ let target = envelope["surface"] as? Int ?? 0
24
+ let next = envelope["revision"] as? Int ?? 0
25
+ func error(_ message: String) -> String {
26
+ PNJSON.encode(["ok": false, "application": app, "surface": target,
27
+ "revision": next, "error": message])
28
+ }
29
+ guard envelope["version"] as? Int == 2, !app.isEmpty, target > 0,
30
+ let raw = envelope["ops"] as? [[Any]] else { return error("invalid v2 envelope") }
31
+ let replacing = app != application
32
+ guard next == (replacing ? 1 : revision + 1), replacing || (!failed && target == surface)
33
+ else { return error("stale revision or failed surface") }
34
+ var tags: Set<Int64> = replacing ? [] : live
35
+ var links: [Int64: Int64] = replacing ? [:] : parents
36
+ var names: [Int64: String] = replacing ? [:] : types
37
+ var decoded: [PNTransaction.Op] = []
38
+ do {
39
+ for (index, parts) in raw.enumerated() {
40
+ guard let code = parts.first as? String,
41
+ parts.count == ["c": 4, "u": 3, "i": 4, "d": 2, "f": 6][code],
42
+ let number = parts[1] as? NSNumber, number.doubleValue > 0,
43
+ number.doubleValue.rounded() == number.doubleValue else {
44
+ throw PNTransaction.DecodeError.malformedOp(index: index)
45
+ }
46
+ let tag = number.int64Value
47
+ if code == "c" {
48
+ guard !tags.contains(tag), let type = parts[2] as? String, !type.isEmpty,
49
+ parts[3] is [String: Any] else { return error("invalid create") }
50
+ guard PNRegistry.shared.componentNames.contains(type) else { return error("unknown component") }
51
+ guard PNContracts.validate(type, parts[3] as! [String: Any]) else { return error("invalid typed props") }
52
+ tags.insert(tag)
53
+ names[tag] = type
54
+ } else {
55
+ guard tags.contains(tag) else { return error("unknown tag") }
56
+ if code == "u" {
57
+ guard let props = parts[2] as? [String: Any] else { return error("invalid props") }
58
+ if let type = names[tag], !PNContracts.validate(type, props, partial: true) { return error("invalid typed update") }
59
+ }
60
+ if code == "i" {
61
+ guard let child = parts[2] as? Int64, tags.contains(child),
62
+ let position = parts[3] as? Int, position >= 0 else { return error("invalid insertion") }
63
+ var ancestor: Int64? = tag
64
+ while let current = ancestor {
65
+ if current == child { return error("cycle") }
66
+ ancestor = links[current]
67
+ }
68
+ let siblings = links.filter { $0.value == tag && $0.key != child }.count
69
+ guard position <= siblings else { return error("insertion index exceeds child count") }
70
+ links[child] = tag
71
+ }
72
+ if code == "d" {
73
+ guard !links.values.contains(tag) else { return error("destroy children first") }
74
+ tags.remove(tag)
75
+ names.removeValue(forKey: tag)
76
+ links.removeValue(forKey: tag)
77
+ }
78
+ if code == "f" {
79
+ for value in parts[2...] {
80
+ guard let number = value as? NSNumber, number.doubleValue.isFinite else { return error("invalid frame") }
81
+ }
82
+ guard (parts[4] as! NSNumber).doubleValue >= 0,
83
+ (parts[5] as! NSNumber).doubleValue >= 0 else { return error("negative size") }
84
+ }
85
+ }
86
+ decoded.append(try PNTransaction.decodeOp(parts, index: index))
87
+ }
88
+ } catch { return PNJSON.encode(["ok": false, "error": String(describing: error)]) }
89
+ if replacing {
90
+ for tag in live { PNTransaction.apply([.destroy(tag: tag)]) }
91
+ PNLayout.reset()
92
+ }
93
+ PNTransaction.apply(decoded)
94
+ PNLayout.observe(decoded)
95
+ application = app
96
+ surface = target
97
+ revision = next
98
+ live = tags
99
+ types = names
100
+ parents = links
101
+ failed = false
102
+ return PNJSON.encode(["ok": true, "application": app, "surface": target, "revision": next])
103
+ }
104
+ }