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,261 @@
1
+ package com.pythonnative.runtime
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.util.Log
6
+ import com.pythonnative.runtime.animation.PNAnimator
7
+ import com.pythonnative.runtime.bridge.JsonUtil
8
+ import com.pythonnative.runtime.bridge.MainThread
9
+ import com.pythonnative.runtime.bridge.PNRegistry
10
+ import com.pythonnative.runtime.bridge.TransactionApplier
11
+ import com.pythonnative.runtime.bridge.ViewRegistry
12
+ import com.pythonnative.runtime.modules.BuiltinModules
13
+ import com.pythonnative.runtime.modules.ModuleEnvelope
14
+ import com.pythonnative.runtime.modules.Promise
15
+ import org.json.JSONObject
16
+ import java.util.Locale
17
+
18
+ /**
19
+ * The single Java class Python touches.
20
+ *
21
+ * Every entry point mirrors one C symbol of the iOS bridge and is
22
+ * expected to run on the main thread (Python's asyncio loop lives
23
+ * there). Calls from other threads are logged but still executed so a
24
+ * misbehaving caller fails loudly in logcat instead of silently.
25
+ */
26
+ object PNBridge {
27
+ /** Logcat tag used by the whole runtime. */
28
+ const val TAG = "PythonNative"
29
+
30
+ /** Protocol version compiled into this library. */
31
+ const val PROTOCOL_VERSION = 2
32
+
33
+ private val pythonQueue = java.util.concurrent.Executors.newSingleThreadExecutor { runnable ->
34
+ Thread(runnable, "PythonNative-events").apply { isDaemon = true }
35
+ }
36
+ @Volatile private var host: PythonHost? = null
37
+ private var activity: Activity? = null
38
+ private val hostReadyListeners = ArrayList<Runnable>()
39
+ private val modulePromises = java.util.concurrent.ConcurrentHashMap<Long, Promise>()
40
+
41
+ /** Tag to view bookkeeping shared by the applier and managers. */
42
+ val registry = ViewRegistry()
43
+
44
+ private data class Message(val kind: String, val tag: Long, val name: String, val payload: String) {
45
+ val continuous get() = kind == "event" && (name in setOf("on_scroll", "on_selection_change", "on_gesture_update") ||
46
+ name.startsWith("gesture:") && JSONObject(payload).optJSONArray("args")?.optJSONObject(0)?.optString("state") == "changed")
47
+ }
48
+ private val mailbox = java.util.ArrayDeque<Message>()
49
+ private var draining = false
50
+
51
+ private val commits = com.pythonnative.runtime.bridge.CommitState()
52
+ private val applier by lazy { TransactionApplier(registry) }
53
+
54
+ /** The protocol version Python must match before starting. */
55
+ @JvmStatic
56
+ fun protocolVersion(): Int = PROTOCOL_VERSION
57
+
58
+ /** Install the Python callback target; flushes callbacks queued before it existed. */
59
+ @JvmStatic
60
+ fun setHost(host: PythonHost) {
61
+ this.host = host
62
+ val pending = ArrayList(hostReadyListeners)
63
+ hostReadyListeners.clear()
64
+ for (listener in pending) {
65
+ try {
66
+ listener.run()
67
+ } catch (e: Exception) {
68
+ Log.e(TAG, "host-ready listener failed", e)
69
+ }
70
+ }
71
+ }
72
+
73
+ /** Whether a [PythonHost] has been installed. */
74
+ fun hasHost(): Boolean = host != null
75
+
76
+ /** Run `runnable` now if a host is installed, otherwise once [setHost] is called. */
77
+ fun runWhenHostReady(runnable: Runnable) {
78
+ if (host != null) runnable.run() else hostReadyListeners.add(runnable)
79
+ }
80
+
81
+ /** Record the current activity; modules and managers derive their `Context` from it. */
82
+ @JvmStatic
83
+ fun setContext(activity: Activity) {
84
+ this.activity = activity
85
+ PNRegistry.ensureBuiltins()
86
+ BuiltinModules.onContextAttached(activity)
87
+ }
88
+
89
+ /** Forget `activity` if it is the current one (called from `Activity.onDestroy`). */
90
+ @JvmStatic
91
+ fun clearContext(activity: Activity) {
92
+ if (this.activity === activity) this.activity = null
93
+ }
94
+
95
+ /** The current activity, or `null` before [setContext]. */
96
+ fun activity(): Activity? = activity
97
+
98
+ /** The context used to create views; throws before [setContext]. */
99
+ fun context(): Context =
100
+ activity ?: throw IllegalStateException("PNBridge.setContext(activity) has not been called")
101
+
102
+ /** Display density of the current context (1.0 when no context is attached). */
103
+ fun density(): Float {
104
+ val ctx = activity ?: return 1f
105
+ val d = ctx.resources.displayMetrics.density
106
+ return if (d > 0f) d else 1f
107
+ }
108
+
109
+ // ------------------------------------------------------------------
110
+ // Python -> native
111
+ // ------------------------------------------------------------------
112
+
113
+ /** Apply one serialized transaction (a JSON array of ops). */
114
+ @JvmStatic
115
+ fun apply(transactionJson: String): String {
116
+ if (!MainThread.isMain()) return MainThread.call { apply(transactionJson) }
117
+ return commits.apply(transactionJson, applier)
118
+ }
119
+
120
+ /**
121
+ * Measure the view with `tag` under the given constraints (dp; `1e6`
122
+ * means unconstrained) and return `"w,h"` in dp.
123
+ */
124
+ @JvmStatic
125
+ fun measure(tag: Long, maxWidth: Double, maxHeight: Double): String {
126
+ if (!MainThread.isMain()) return MainThread.call { measure(tag, maxWidth, maxHeight) }
127
+ val record = registry.get(tag)
128
+ if (record == null) {
129
+ Log.w(TAG, "measure: unknown tag $tag")
130
+ return "0,0"
131
+ }
132
+ val size = try {
133
+ record.manager.measure(record.view, maxWidth, maxHeight)
134
+ } catch (e: Exception) {
135
+ Log.e(TAG, "measure failed for ${record.typeName}#$tag", e)
136
+ floatArrayOf(0f, 0f)
137
+ }
138
+ return String.format(Locale.US, "%.3f,%.3f", size[0], size[1])
139
+ }
140
+
141
+ /** Run an imperative command on one view and return its JSON result. */
142
+ @JvmStatic
143
+ fun command(tag: Long, name: String, argsJson: String): String? {
144
+ if (!MainThread.isMain()) return MainThread.call { command(tag, name, argsJson) }
145
+ val record = registry.get(tag) ?: return null
146
+ return try {
147
+ val args = if (argsJson.isBlank()) JSONObject() else JSONObject(argsJson)
148
+ JsonUtil.encode(record.manager.command(record.view, name, args))
149
+ } catch (e: Exception) {
150
+ Log.e(TAG, "command '$name' failed for ${record.typeName}#$tag", e)
151
+ null
152
+ }
153
+ }
154
+
155
+ /** Handle an animation request (`set`, `start`, or `cancel`) for one view. */
156
+ @JvmStatic
157
+ fun animate(tag: Long, requestJson: String): String? {
158
+ if (!MainThread.isMain()) return MainThread.call { animate(tag, requestJson) }
159
+ val record = registry.get(tag) ?: return null
160
+ return try {
161
+ PNAnimator.handle(record, JSONObject(requestJson))
162
+ } catch (e: Exception) {
163
+ Log.e(TAG, "animate failed for ${record.typeName}#$tag: $requestJson", e)
164
+ null
165
+ }
166
+ }
167
+
168
+ /** Call a native module method; `argsJson` is `{"call_id": n, "args": {...}}`. */
169
+ @JvmStatic
170
+ fun call(module: String, method: String, argsJson: String): String? {
171
+ if (!MainThread.isMain()) return MainThread.call { call(module, method, argsJson) }
172
+ val (callId, args) = try {
173
+ ModuleEnvelope.decodeCall(argsJson)
174
+ } catch (e: Exception) {
175
+ return ModuleEnvelope.error("malformed call envelope: ${e.message}", "bad_request")
176
+ }
177
+ if (method == "_pn_cancel") {
178
+ modulePromises.remove(args.optLong("call_id"))?.takeIf { it.module == module }?.cancel()
179
+ return ModuleEnvelope.ok(null)
180
+ }
181
+ if (module == "Runtime") return JSONObject().put("ok", true).put("value", JSONObject()
182
+ .put("protocol", 2).put("yoga", "3.2.1").put("schema", com.pythonnative.generated.PNContracts.fingerprint)
183
+ .put("animation_graph", true).put("logical_lists", true).put("native_layout", true)).toString()
184
+ if (module == "Layout") return JSONObject().put("ok", true).put("value",
185
+ com.pythonnative.runtime.layout.NativeLayout.compute(args)).toString()
186
+ val target = PNRegistry.module(module)
187
+ ?: return ModuleEnvelope.error("unknown module '$module'", "unknown_module")
188
+ val promise = Promise(callId, module)
189
+ if (callId > 0) {
190
+ modulePromises[callId] = promise
191
+ promise.onFinished = { modulePromises.remove(callId) }
192
+ }
193
+ try {
194
+ target.call(method, args, promise)
195
+ } catch (e: Exception) {
196
+ Log.e(TAG, "module $module.$method threw", e)
197
+ if (!promise.isSettled) promise.reject(e.message ?: e.toString(), "exception")
198
+ }
199
+ return promise.markReturned()
200
+ }
201
+
202
+ // ------------------------------------------------------------------
203
+ // native -> Python
204
+ // ------------------------------------------------------------------
205
+
206
+ /**
207
+ * Deliver one callback to Python. Returns `null` when no host is
208
+ * installed or the Python handler raised (the error is logged).
209
+ */
210
+ fun callPython(kind: String, tag: Long, name: String, payloadJson: String): String? {
211
+ val target = host
212
+ if (target == null) {
213
+ Log.w(TAG, "callPython($kind, $tag, $name) dropped: no PythonHost installed")
214
+ return null
215
+ }
216
+ if (kind == "event") com.pythonnative.runtime.animation.AnimationGraph.event(tag, name, org.json.JSONArray(payloadJson))
217
+ var editRevision = 0L
218
+ if (kind == "event" && name == "on_change") registry.get(tag)?.let { record ->
219
+ if (record.typeName == "TextInput") {
220
+ editRevision = ((record.state["edit_revision"] as? Number)?.toLong() ?: 0L) + 1L
221
+ record.state["edit_revision"] = editRevision
222
+ }
223
+ }
224
+ val payload = if (kind == "event") commits.event(org.json.JSONArray(payloadJson), editRevision) else payloadJson
225
+ val message = Message(kind, tag, name, payload)
226
+ val schedule = synchronized(mailbox) {
227
+ val last = mailbox.peekLast()
228
+ if (message.continuous && last?.continuous == true && last.tag == tag && last.name == name) mailbox.removeLast()
229
+ mailbox.addLast(message)
230
+ val needed = !draining
231
+ draining = true
232
+ needed
233
+ }
234
+ if (schedule) pythonQueue.execute {
235
+ while (true) {
236
+ val next = synchronized(mailbox) {
237
+ mailbox.pollFirst().also { if (it == null) draining = false }
238
+ } ?: break
239
+ try { target.callback(next.kind, next.tag, next.name, next.payload) }
240
+ catch (error: Exception) { Log.e(TAG, "Python callback ${next.kind}/${next.name} raised", error) }
241
+ }
242
+ }
243
+ return null
244
+ }
245
+
246
+ // ------------------------------------------------------------------
247
+ // Threading helpers
248
+ // ------------------------------------------------------------------
249
+
250
+ /** Whether the caller is on the main thread. */
251
+ fun isMainThread(): Boolean = MainThread.isMain()
252
+
253
+ /** Post `runnable` to the main thread. */
254
+ fun post(runnable: Runnable) = MainThread.post(runnable)
255
+
256
+ private fun assertMain(entry: String) {
257
+ if (!MainThread.isMain()) {
258
+ Log.w(TAG, "PNBridge.$entry called off the main thread (${Thread.currentThread().name})")
259
+ }
260
+ }
261
+ }
@@ -0,0 +1,20 @@
1
+ package com.pythonnative.runtime
2
+
3
+ /**
4
+ * The reverse direction of the bridge: native to Python.
5
+ *
6
+ * The app template installs an implementation that forwards to
7
+ * `pythonnative.bridge.native_callback` through Chaquopy. The runtime
8
+ * library itself never touches Chaquopy, which keeps it unit-testable
9
+ * with plain JUnit.
10
+ *
11
+ * `kind` is one of `event`, `module`, `host`, `animation`, or `pump`;
12
+ * see the bridge protocol documentation for the payload of each.
13
+ */
14
+ interface PythonHost {
15
+ /**
16
+ * Deliver one callback to Python and return its (JSON-encoded)
17
+ * result, or `null` when the handler produced nothing.
18
+ */
19
+ fun callback(kind: String, tag: Long, name: String, payloadJson: String): String?
20
+ }
@@ -0,0 +1,108 @@
1
+ package com.pythonnative.runtime.animation
2
+
3
+ import com.pythonnative.runtime.PNBridge
4
+ import org.json.JSONArray
5
+ import org.json.JSONObject
6
+ import kotlin.math.*
7
+
8
+ /** Expressions and input bindings live on the UI thread, independent of Python. */
9
+ object AnimationGraph {
10
+ private data class Graph(val nodes: List<JSONObject>, val bindings: JSONArray)
11
+ private val graphs = HashMap<Long, Graph>()
12
+ private val values = HashMap<Long, Double>()
13
+ private val previous = HashMap<Long, Double>()
14
+ private val output = HashMap<Long, Any>()
15
+
16
+ fun install(spec: JSONObject) {
17
+ val id = spec.getLong("id")
18
+ val bindings = spec.getJSONArray("bindings")
19
+ val raw = spec.getJSONArray("nodes")
20
+ val nodes = (0 until raw.length()).map { raw.getJSONObject(it) }
21
+ val ids = nodes.map { it.getLong("id") }.toSet()
22
+ graphs.entries.removeAll { (_, graph) -> graph.nodes.any { it.getLong("id") in ids } }
23
+ if (bindings.length() == 0) { collect(); return }
24
+ for (node in nodes) {
25
+ val key = node.getLong("id")
26
+ values.putIfAbsent(key, node.optDouble("value", 0.0))
27
+ previous.putIfAbsent(key, node.optDouble("previous", 0.0))
28
+ }
29
+ graphs[id] = Graph(nodes, bindings)
30
+ collect()
31
+ evaluate()
32
+ }
33
+ fun value(id: Long): Double = values[id] ?: 0.0
34
+ fun set(id: Long, value: Double) { if (value.isFinite()) { values[id] = value; evaluate() } }
35
+ fun event(tag: Long, name: String, args: JSONArray) {
36
+ val payload = args.optJSONObject(0) ?: return
37
+ val props = PNBridge.registry.get(tag)?.props ?: return
38
+ val fields = if (name.startsWith("gesture:")) {
39
+ val index = name.removePrefix("gesture:").toIntOrNull() ?: return
40
+ props.optJSONArray("gestures")?.optJSONObject(index)?.optJSONObject("animated_events")?.optJSONObject(payload.optString("state"))
41
+ } else props.optJSONObject("_pn_animated_events")?.optJSONObject(name)
42
+ if (fields == null) return
43
+ for (field in fields.keys()) if (payload.opt(field) is Number) values[fields.getLong(field)] = payload.getDouble(field)
44
+ evaluate()
45
+ }
46
+ fun forget(tag: Long) {
47
+ for ((id, graph) in graphs.toMap()) {
48
+ val kept = JSONArray()
49
+ for (i in 0 until graph.bindings.length()) if (graph.bindings.getJSONArray(i).getLong(0) != tag) kept.put(graph.bindings.getJSONArray(i))
50
+ if (kept.length() == 0) graphs.remove(id) else graphs[id] = Graph(graph.nodes, kept)
51
+ }
52
+ collect()
53
+ }
54
+ private fun collect() {
55
+ val live = graphs.values.flatMap { it.nodes }.map { it.getLong("id") }.toSet()
56
+ values.keys.retainAll(live); previous.keys.retainAll(live); output.keys.retainAll(live)
57
+ }
58
+ private fun input(node: JSONObject): Double = if (node.has("node")) values[node.getLong("node")] ?: 0.0 else node.optDouble("constant", 0.0)
59
+ private fun evaluate() {
60
+ for (graph in graphs.values) {
61
+ for (node in graph.nodes) {
62
+ val id = node.getLong("id")
63
+ val raw = node.optJSONArray("inputs") ?: JSONArray()
64
+ val inputs = (0 until raw.length()).map { input(raw.getJSONObject(it)) }
65
+ val a = inputs.getOrElse(0) { 0.0 }; val b = inputs.getOrElse(1) { 0.0 }
66
+ val value = when (node.getString("kind")) {
67
+ "value" -> values[id] ?: 0.0
68
+ "add" -> a + b; "subtract" -> a - b; "multiply" -> a * b
69
+ "divide" -> if (b == 0.0) 0.0 else a / b
70
+ "modulo" -> if (b == 0.0) 0.0 else a % b
71
+ "negate" -> -a
72
+ "diff_clamp" -> {
73
+ val next = ((values[id] ?: 0.0) + a - (previous[id] ?: a)).coerceIn(node.getDouble("minimum"), node.getDouble("maximum"))
74
+ previous[id] = a; next
75
+ }
76
+ "interpolate" -> interpolate(node, a, id)
77
+ else -> 0.0
78
+ }
79
+ values[id] = if (value.isFinite()) value else 0.0
80
+ if (!node.optBoolean("color")) output[id] = values[id]!!
81
+ }
82
+ for (i in 0 until graph.bindings.length()) {
83
+ val binding = graph.bindings.getJSONArray(i)
84
+ val record = PNBridge.registry.get(binding.getLong(0)) ?: continue
85
+ record.manager.setAnimatedProperty(record.view, binding.getString(1), output[binding.getLong(2)])
86
+ }
87
+ }
88
+ }
89
+ private fun interpolate(node: JSONObject, input: Double, id: Long): Double {
90
+ val ranges = node.getJSONArray("ranges"); val outputs = node.getJSONArray("outputs")
91
+ val first = ranges.getDouble(0); val last = ranges.getDouble(ranges.length() - 1)
92
+ var x = input
93
+ val mode = if (x < first) node.getString("left") else if (x > last) node.getString("right") else "extend"
94
+ if (mode == "identity") return x
95
+ if (mode == "clamp") x = x.coerceIn(first, last)
96
+ var index = 0
97
+ while (index < ranges.length() - 2 && x >= ranges.getDouble(index + 1)) index++
98
+ val span = ranges.getDouble(index + 1) - ranges.getDouble(index)
99
+ val t = if (span == 0.0) 0.0 else (x - ranges.getDouble(index)) / span
100
+ if (node.optBoolean("color")) {
101
+ val from = outputs.getJSONArray(index); val to = outputs.getJSONArray(index + 1)
102
+ val channels = (0..3).map { (from.getDouble(it) + (to.getDouble(it) - from.getDouble(it)) * t.coerceIn(0.0, 1.0)).roundToInt().coerceIn(0, 255) }
103
+ output[id] = "#" + channels.joinToString("") { "%02X".format(it) }
104
+ return 0.0
105
+ }
106
+ return outputs.getDouble(index) + (outputs.getDouble(index + 1) - outputs.getDouble(index)) * t
107
+ }
108
+ }
@@ -0,0 +1,141 @@
1
+ package com.pythonnative.runtime.animation
2
+
3
+ import org.json.JSONArray
4
+ import org.json.JSONObject
5
+ import kotlin.math.max
6
+ import kotlin.math.sqrt
7
+
8
+ /**
9
+ * Pure-Kotlin decoding of animation specs (`pn.Animated.timing`,
10
+ * `spring`, `decay`) and their mapping to Android animator parameters.
11
+ * Kept free of Android types so the math is unit-testable.
12
+ */
13
+ object AnimationSpecs {
14
+ /** `FlingAnimation` scales `friction` by this constant internally (`DragForce`). */
15
+ const val FLING_FRICTION_SCALE = 4.2
16
+
17
+ /** Easing curves identical to the Python ticker's `_EASINGS`. */
18
+ val EASINGS: Map<String, (Float) -> Float> = mapOf(
19
+ "linear" to { t -> t },
20
+ "ease" to { t -> 3f * t * t - 2f * t * t * t },
21
+ "ease_in" to { t -> t * t },
22
+ "ease_out" to { t -> 1f - (1f - t) * (1f - t) },
23
+ "ease_in_out" to { t -> 3f * t * t - 2f * t * t * t },
24
+ "ease_in_quad" to { t -> t * t },
25
+ "ease_out_quad" to { t -> 1f - (1f - t) * (1f - t) },
26
+ "bounce" to { t -> bounceOut(t) },
27
+ )
28
+
29
+ /** Decoded `timing` parameters. */
30
+ data class Timing(val from: Double, val to: Double, val durationMs: Long, val easing: Any?)
31
+
32
+ /** Decoded `spring` parameters mapped to `SpringForce` terms. */
33
+ data class Spring(
34
+ val from: Double,
35
+ val to: Double,
36
+ /** `stiffness / mass`, so the natural frequency matches the Python integrator. */
37
+ val stiffness: Double,
38
+ /** `damping / (2 * sqrt(stiffness * mass))`. */
39
+ val dampingRatio: Double,
40
+ /** Units per second. */
41
+ val initialVelocity: Double,
42
+ )
43
+
44
+ /** Decoded `decay` parameters mapped to `FlingAnimation` terms. */
45
+ data class Decay(
46
+ val from: Double,
47
+ /** Units per second (Python specs use units per millisecond). */
48
+ val startVelocity: Double,
49
+ /** `FlingAnimation.setFriction` value reproducing `v(t) = v0 * exp(-k * 1000 * t)`. */
50
+ val friction: Double,
51
+ /** Where the value settles: `from + v0 / (k * 1000)`. */
52
+ val projectedFinal: Double,
53
+ )
54
+
55
+ fun kind(spec: JSONObject): String = spec.optString("kind", "")
56
+
57
+ fun timing(spec: JSONObject): Timing = Timing(
58
+ from = spec.optDouble("from", 0.0),
59
+ to = spec.optDouble("to", 0.0),
60
+ durationMs = max(0L, spec.optDouble("duration_ms", 300.0).toLong()),
61
+ easing = spec.opt("easing"),
62
+ )
63
+
64
+ fun spring(spec: JSONObject): Spring {
65
+ val stiffness = max(1e-6, spec.optDouble("stiffness", 100.0))
66
+ val damping = max(0.0, spec.optDouble("damping", 10.0))
67
+ val mass = max(1e-6, spec.optDouble("mass", 1.0))
68
+ return Spring(
69
+ from = spec.optDouble("from", 0.0),
70
+ to = spec.optDouble("to", 0.0),
71
+ stiffness = stiffness / mass,
72
+ dampingRatio = dampingRatio(stiffness, damping, mass),
73
+ initialVelocity = spec.optDouble("initial_velocity", 0.0),
74
+ )
75
+ }
76
+
77
+ /** `damping / (2 * sqrt(stiffness * mass))`. */
78
+ fun dampingRatio(stiffness: Double, damping: Double, mass: Double): Double =
79
+ damping / (2.0 * sqrt(max(1e-12, stiffness * mass)))
80
+
81
+ fun decay(spec: JSONObject): Decay {
82
+ val v0PerMs = spec.optDouble("velocity", 0.0)
83
+ val k = max(1e-6, spec.optDouble("deceleration", 0.997))
84
+ val from = spec.optDouble("from", 0.0)
85
+ return Decay(
86
+ from = from,
87
+ startVelocity = v0PerMs * 1000.0,
88
+ friction = decayFriction(k),
89
+ projectedFinal = from + v0PerMs / (k * 1000.0),
90
+ )
91
+ }
92
+
93
+ /**
94
+ * The Python ticker decays velocity as `exp(-k * dt_ms)`, that is
95
+ * `exp(-k * 1000 * t_s)`. `FlingAnimation` decays as
96
+ * `exp(-4.2 * friction * t_s)`, so `friction = k * 1000 / 4.2`.
97
+ */
98
+ fun decayFriction(deceleration: Double): Double = deceleration * 1000.0 / FLING_FRICTION_SCALE
99
+
100
+ /**
101
+ * Resolve an easing value. Names map to [EASINGS]; a four-number
102
+ * array is returned as cubic-bezier control points (`DoubleArray`)
103
+ * for the caller to build a `PathInterpolator`. Unknown names fall
104
+ * back to `ease_in_out` like the Python ticker.
105
+ */
106
+ fun resolveEasing(easing: Any?): Any {
107
+ if (easing is JSONArray && easing.length() == 4) {
108
+ return DoubleArray(4) { easing.optDouble(it, 0.0) }
109
+ }
110
+ if (easing is String) {
111
+ val trimmed = easing.trim()
112
+ if (trimmed.startsWith("cubic-bezier(") || trimmed.startsWith("cubic_bezier(")) {
113
+ val inner = trimmed.substringAfter('(').substringBeforeLast(')')
114
+ val parts = inner.split(',').mapNotNull { it.trim().toDoubleOrNull() }
115
+ if (parts.size == 4) return parts.toDoubleArray()
116
+ }
117
+ return EASINGS[trimmed] ?: EASINGS.getValue("ease_in_out")
118
+ }
119
+ return EASINGS.getValue("ease_in_out")
120
+ }
121
+
122
+ private fun bounceOut(t: Float): Float {
123
+ val n1 = 7.5625f
124
+ val d1 = 2.75f
125
+ return when {
126
+ t < 1f / d1 -> n1 * t * t
127
+ t < 2f / d1 -> {
128
+ val u = t - 1.5f / d1
129
+ n1 * u * u + 0.75f
130
+ }
131
+ t < 2.5f / d1 -> {
132
+ val u = t - 2.25f / d1
133
+ n1 * u * u + 0.9375f
134
+ }
135
+ else -> {
136
+ val u = t - 2.625f / d1
137
+ n1 * u * u + 0.984375f
138
+ }
139
+ }
140
+ }
141
+ }