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,355 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.graphics.Rect
4
+ import android.graphics.drawable.GradientDrawable
5
+ import android.os.Build
6
+ import android.view.TouchDelegate
7
+ import android.view.View
8
+ import android.view.ViewGroup
9
+ import com.pythonnative.runtime.PNBridge
10
+ import com.pythonnative.runtime.bridge.JsonUtil
11
+ import com.pythonnative.runtime.bridge.PNLog
12
+ import com.pythonnative.runtime.bridge.ViewRecord
13
+ import com.pythonnative.runtime.bridge.num
14
+ import com.pythonnative.runtime.bridge.obj
15
+ import com.pythonnative.runtime.bridge.str
16
+ import com.pythonnative.runtime.bridge.value
17
+ import com.pythonnative.runtime.views.PNAccessibilityDelegate
18
+ import com.pythonnative.runtime.views.PNBorderDrawable
19
+ import com.pythonnative.runtime.views.PNFrameLayout
20
+ import org.json.JSONArray
21
+ import org.json.JSONObject
22
+ import kotlin.math.roundToInt
23
+
24
+ /**
25
+ * Applies the visual props every element supports: background and
26
+ * borders, opacity, overflow, display, z-order, pointer events, hit
27
+ * slop, shadows, transforms, and accessibility.
28
+ *
29
+ * Border-related props are baked into a single background drawable,
30
+ * so the styler keeps the merged "visual" subset per view and re-bakes
31
+ * it whenever one of those keys changes.
32
+ */
33
+ object ViewStyler {
34
+ private val sideWidthKeys = listOf("border_left_width", "border_top_width", "border_right_width", "border_bottom_width")
35
+ private val sideColorKeys = listOf("border_left_color", "border_top_color", "border_right_color", "border_bottom_color")
36
+ // Order matches GradientDrawable.setCornerRadii: TL, TR, BR, BL.
37
+ private val cornerKeys = listOf(
38
+ "border_top_left_radius", "border_top_right_radius", "border_bottom_right_radius", "border_bottom_left_radius",
39
+ )
40
+ private val drawableKeys: List<String> =
41
+ listOf("background_color", "border_radius", "border_width", "border_color") + cornerKeys + sideWidthKeys + sideColorKeys
42
+
43
+ private fun density(): Float = PNBridge.density()
44
+ private fun px(dp: Double): Float = (dp * density()).toFloat()
45
+ private fun pxI(dp: Double): Int = (dp * density()).roundToInt()
46
+
47
+ private fun record(view: View): ViewRecord? = PNBridge.registry.recordFor(view)
48
+
49
+ /** Apply the common visual props present in `props` to `view`. */
50
+ fun apply(view: View, props: JSONObject) {
51
+ val record = record(view)
52
+ if (drawableKeys.any { props.has(it) }) {
53
+ val visual = visualProps(record)
54
+ for (key in drawableKeys) {
55
+ if (props.has(key)) visual.put(key, props.opt(key) ?: JSONObject.NULL)
56
+ }
57
+ applyBorder(view, visual)
58
+ }
59
+ if (props.has("overflow")) {
60
+ val clip = props.str("overflow") == "hidden"
61
+ if (view is ViewGroup) {
62
+ view.clipChildren = clip
63
+ view.clipToPadding = clip
64
+ }
65
+ }
66
+ if (props.has("display")) {
67
+ view.visibility = if (props.str("display") == "none") View.GONE else View.VISIBLE
68
+ }
69
+ props.num("opacity")?.let { view.alpha = it.toFloat() }
70
+ if (props.has("z_index")) {
71
+ val z = props.num("z_index")
72
+ view.z = if (z != null) px(z) else 0f
73
+ }
74
+ if (props.has("pointer_events")) applyPointerEvents(view, props.str("pointer_events"))
75
+ if (props.has("hit_slop")) updateHitSlop(view)
76
+ applyShadow(view, props)
77
+ applyTransform(view, props)
78
+ applyAccessibility(view, props)
79
+ }
80
+
81
+ private fun visualProps(record: ViewRecord?): JSONObject {
82
+ val existing = record?.state?.get("visual") as? JSONObject
83
+ if (existing != null) return existing
84
+ val fresh = JSONObject()
85
+ record?.state?.put("visual", fresh)
86
+ return fresh
87
+ }
88
+
89
+ // ------------------------------------------------------------------
90
+ // Background and borders
91
+ // ------------------------------------------------------------------
92
+
93
+ /** Re-bake the background drawable from the merged visual props. */
94
+ fun applyBorder(view: View, visual: JSONObject) {
95
+ if (applySideBorder(view, visual)) return
96
+ val hasBorder = listOf("border_radius", "border_width", "border_color").any { visual.has(it) } ||
97
+ cornerKeys.any { visual.has(it) }
98
+ val hasBg = visual.value("background_color") != null
99
+ if (!hasBorder && !hasBg) {
100
+ if (view.background is GradientDrawable || view.background is PNBorderDrawable) view.background = null
101
+ return
102
+ }
103
+ val drawable = GradientDrawable()
104
+ PNColor.parse(visual.value("background_color"))?.let { drawable.setColor(it) }
105
+ if (cornerKeys.any { visual.value(it) != null }) {
106
+ val base = visual.num("border_radius") ?: 0.0
107
+ val radii = FloatArray(8)
108
+ for ((i, key) in cornerKeys.withIndex()) {
109
+ val r = px(visual.num(key) ?: base)
110
+ radii[i * 2] = r
111
+ radii[i * 2 + 1] = r
112
+ }
113
+ drawable.cornerRadii = radii
114
+ } else {
115
+ visual.num("border_radius")?.let { drawable.cornerRadius = px(it) }
116
+ }
117
+ if (visual.value("border_width") != null || visual.value("border_color") != null) {
118
+ val width = visual.num("border_width") ?: 1.0
119
+ val color = PNColor.parseOr(visual.value("border_color"), 0xFF000000.toInt())
120
+ drawable.setStroke(pxI(width), color)
121
+ }
122
+ view.background = drawable
123
+ view.invalidate()
124
+ }
125
+
126
+ private fun applySideBorder(view: View, visual: JSONObject): Boolean {
127
+ if (sideWidthKeys.none { visual.value(it) != null }) return false
128
+ val baseWidth = visual.num("border_width") ?: 0.0
129
+ val baseColor = visual.value("border_color") ?: "#000000"
130
+ val widths = FloatArray(4) { i -> px(visual.num(sideWidthKeys[i]) ?: baseWidth) }
131
+ val colors = IntArray(4) { i -> PNColor.parseOr(visual.value(sideColorKeys[i]) ?: baseColor, 0xFF000000.toInt()) }
132
+ val bg = PNColor.parse(visual.value("background_color"))
133
+ val radius = px(visual.num("border_radius") ?: 0.0)
134
+ view.background = PNBorderDrawable(bg != null, bg ?: 0, radius, widths, colors)
135
+ view.invalidate()
136
+ return true
137
+ }
138
+
139
+ /**
140
+ * Apply one animated `background_color` frame without losing borders:
141
+ * mutate the existing drawable's fill in place when possible.
142
+ */
143
+ fun setAnimatedBackground(view: View, value: Any?) {
144
+ val color = PNColor.parse(value) ?: return
145
+ val record = record(view)
146
+ val visual = record?.state?.get("visual") as? JSONObject
147
+ val hasBorder = visual != null && drawableKeys.any { it != "background_color" && visual.value(it) != null }
148
+ if (hasBorder && visual != null) {
149
+ visual.put("background_color", String.format("#%08X", color))
150
+ when (val bg = view.background) {
151
+ is GradientDrawable -> bg.setColor(color)
152
+ is PNBorderDrawable -> bg.setFillColor(color)
153
+ else -> applyBorder(view, visual)
154
+ }
155
+ return
156
+ }
157
+ view.setBackgroundColor(color)
158
+ }
159
+
160
+ // ------------------------------------------------------------------
161
+ // Shadow / elevation
162
+ // ------------------------------------------------------------------
163
+
164
+ private fun applyShadow(view: View, props: JSONObject) {
165
+ var elevation = props.num("elevation")
166
+ if (elevation == null && props.has("shadow_radius")) elevation = props.num("shadow_radius")
167
+ if (elevation == null && (props.value("shadow_color") != null || props.value("shadow_opacity") != null)) {
168
+ // Shadow requested without an explicit size: Material card-like default.
169
+ elevation = 4.0
170
+ }
171
+ if (elevation == null) return
172
+ view.elevation = px(elevation)
173
+ val color = props.value("shadow_color") ?: return
174
+ if (Build.VERSION.SDK_INT < 28) return
175
+ var argb = PNColor.parse(color) ?: return
176
+ props.num("shadow_opacity")?.let { argb = PNColor.withAlpha(argb, it) }
177
+ view.outlineAmbientShadowColor = argb
178
+ view.outlineSpotShadowColor = argb
179
+ }
180
+
181
+ // ------------------------------------------------------------------
182
+ // Transform
183
+ // ------------------------------------------------------------------
184
+
185
+ private fun applyTransform(view: View, props: JSONObject) {
186
+ if (!props.has("transform")) return
187
+ val spec = props.value("transform")
188
+ if (spec == null) {
189
+ view.rotation = 0f
190
+ view.rotationX = 0f
191
+ view.rotationY = 0f
192
+ view.scaleX = 1f
193
+ view.scaleY = 1f
194
+ view.translationX = 0f
195
+ view.translationY = 0f
196
+ return
197
+ }
198
+ val entries: List<Any?> = when (spec) {
199
+ is JSONArray -> JsonUtil.toList(spec)
200
+ else -> listOf(spec)
201
+ }
202
+ for (entry in entries) {
203
+ val e = entry as? JSONObject ?: continue
204
+ try {
205
+ if (e.has("rotate")) view.rotation = angleDegrees(e.value("rotate"))
206
+ if (e.has("rotate_x")) view.rotationX = angleDegrees(e.value("rotate_x"))
207
+ if (e.has("rotate_y")) view.rotationY = angleDegrees(e.value("rotate_y"))
208
+ if (e.has("rotate_z")) view.rotation = angleDegrees(e.value("rotate_z"))
209
+ e.num("scale")?.let { view.scaleX = it.toFloat(); view.scaleY = it.toFloat() }
210
+ e.num("scale_x")?.let { view.scaleX = it.toFloat() }
211
+ e.num("scale_y")?.let { view.scaleY = it.toFloat() }
212
+ e.num("translate_x")?.let { view.translationX = px(it) }
213
+ e.num("translate_y")?.let { view.translationY = px(it) }
214
+ if (e.has("skew_x") || e.has("skew_y")) {
215
+ // Android views have no skew transform; approximate with rotation around the axis.
216
+ PNLog.once("skew", "transform skew_x/skew_y is not supported on Android and is ignored")
217
+ }
218
+ } catch (ex: Exception) {
219
+ PNLog.swallowed("ViewStyler.applyTransform", ex)
220
+ }
221
+ }
222
+ }
223
+
224
+ /** Parse `"45deg"`, `"0.5rad"`, or a bare number (degrees) into degrees. */
225
+ fun angleDegrees(value: Any?): Float {
226
+ return when (value) {
227
+ is Number -> value.toFloat()
228
+ is String -> {
229
+ val s = value.trim()
230
+ when {
231
+ s.endsWith("deg") -> s.dropLast(3).toFloatOrNull() ?: 0f
232
+ s.endsWith("rad") -> Math.toDegrees((s.dropLast(3).toDoubleOrNull() ?: 0.0)).toFloat()
233
+ else -> s.toFloatOrNull() ?: 0f
234
+ }
235
+ }
236
+ else -> 0f
237
+ }
238
+ }
239
+
240
+ // ------------------------------------------------------------------
241
+ // Accessibility
242
+ // ------------------------------------------------------------------
243
+
244
+ /** Apply accessibility props (label / accessible / state / live region / role / test_id). */
245
+ fun applyAccessibility(view: View, props: JSONObject) {
246
+ if (props.has("accessible")) {
247
+ view.importantForAccessibility = if (JsonUtil.truthy(props.value("accessible"))) {
248
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES
249
+ } else {
250
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
251
+ }
252
+ }
253
+ if (props.has("accessibility_label")) {
254
+ view.contentDescription = props.str("accessibility_label")
255
+ }
256
+ if (props.has("accessibility_hint") && Build.VERSION.SDK_INT >= 28) {
257
+ view.tooltipText = props.str("accessibility_hint")
258
+ }
259
+ if (props.has("accessibility_live_region")) {
260
+ view.accessibilityLiveRegion = when (props.str("accessibility_live_region")?.lowercase()) {
261
+ "polite" -> View.ACCESSIBILITY_LIVE_REGION_POLITE
262
+ "assertive" -> View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
263
+ else -> View.ACCESSIBILITY_LIVE_REGION_NONE
264
+ }
265
+ }
266
+ val state = props.obj("accessibility_state")
267
+ if (state != null && state.has("selected")) {
268
+ view.isSelected = JsonUtil.truthy(state.value("selected"))
269
+ }
270
+ val testId = props.str("test_id")
271
+ val role = props.str("accessibility_role")
272
+ if (testId == null && state == null && role == null) return
273
+ val record = record(view)
274
+ var delegate = record?.state?.get("a11y_delegate") as? PNAccessibilityDelegate
275
+ if (delegate == null) {
276
+ delegate = PNAccessibilityDelegate()
277
+ view.accessibilityDelegate = delegate
278
+ record?.state?.put("a11y_delegate", delegate)
279
+ }
280
+ if (testId != null) {
281
+ delegate.testId = testId
282
+ view.tag = testId
283
+ }
284
+ if (role != null) delegate.role = role
285
+ if (state != null) {
286
+ delegate.stateDisabled = optBool(state, "disabled")
287
+ delegate.stateSelected = optBool(state, "selected")
288
+ delegate.stateChecked = optBool(state, "checked")
289
+ delegate.stateBusy = optBool(state, "busy")
290
+ delegate.stateExpanded = optBool(state, "expanded")
291
+ }
292
+ }
293
+
294
+ private fun optBool(obj: JSONObject, key: String): Boolean? {
295
+ val v = obj.value(key) ?: return null
296
+ return JsonUtil.truthy(v)
297
+ }
298
+
299
+ // ------------------------------------------------------------------
300
+ // Pointer events and hit slop
301
+ // ------------------------------------------------------------------
302
+
303
+ /** Whether `pointer_events` disables the view's own touch handling. */
304
+ fun pointerEventsBlocked(view: View): Boolean {
305
+ val mode = record(view)?.props?.str("pointer_events") ?: return false
306
+ return mode == "none" || mode == "box_none"
307
+ }
308
+
309
+ private fun applyPointerEvents(view: View, value: String?) {
310
+ val mode = value ?: "auto"
311
+ val state = record(view)?.state
312
+ (view as? PNFrameLayout)?.setPointerEventsMode(mode)
313
+ when (mode) {
314
+ "none", "box_none" -> {
315
+ state?.let { if (!it.containsKey("pe_was_clickable")) it["pe_was_clickable"] = view.isClickable }
316
+ view.isClickable = false
317
+ }
318
+ "box_only" -> {
319
+ state?.let { if (!it.containsKey("pe_was_clickable")) it["pe_was_clickable"] = view.isClickable }
320
+ view.isClickable = true
321
+ }
322
+ else -> {
323
+ val was = state?.remove("pe_was_clickable") as? Boolean
324
+ if (was != null) view.isClickable = was
325
+ }
326
+ }
327
+ }
328
+
329
+ private fun hitSlopInsets(value: Any?): DoubleArray? {
330
+ return when (value) {
331
+ null, JSONObject.NULL -> null
332
+ is JSONObject -> doubleArrayOf(
333
+ value.num("top") ?: 0.0, value.num("left") ?: 0.0, value.num("bottom") ?: 0.0, value.num("right") ?: 0.0,
334
+ )
335
+ else -> JsonUtil.toDoubleOrNull(value)?.let { doubleArrayOf(it, it, it, it) }
336
+ }
337
+ }
338
+
339
+ /** Extend the view's touch target via a `TouchDelegate` on its parent (needs a frame). */
340
+ fun updateHitSlop(view: View) {
341
+ val record = record(view) ?: return
342
+ val frame = record.frame ?: return
343
+ val insets = hitSlopInsets(record.props.value("hit_slop"))
344
+ val parent = view.parent as? ViewGroup ?: return
345
+ if (insets == null) {
346
+ if (record.state.remove("hit_slop_active") != null) parent.touchDelegate = null
347
+ return
348
+ }
349
+ val (top, left, bottom, right) = insets
350
+ val (x, y, w, h) = frame
351
+ val rect = Rect(pxI(x - left), pxI(y - top), pxI(x + w + right), pxI(y + h + bottom))
352
+ parent.touchDelegate = TouchDelegate(rect, view)
353
+ record.state["hit_slop_active"] = true
354
+ }
355
+ }
@@ -0,0 +1,159 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+ import android.widget.FrameLayout
7
+ import androidx.recyclerview.widget.DiffUtil
8
+ import androidx.recyclerview.widget.LinearLayoutManager
9
+ import androidx.recyclerview.widget.ListAdapter
10
+ import androidx.recyclerview.widget.RecyclerView
11
+ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
12
+ import com.pythonnative.runtime.PNBridge
13
+ import com.pythonnative.runtime.bridge.ViewRecord
14
+ import org.json.JSONObject
15
+ import kotlin.math.roundToInt
16
+
17
+ /** Keyed native recycling; Python prepares logical children asynchronously. */
18
+ class VirtualListManager : ComponentManager() {
19
+ private data class Item(val key: String, val revision: Int, val estimate: Double)
20
+ private class Holder(val container: FrameLayout) : RecyclerView.ViewHolder(container) { var key = "" }
21
+ private inner class ListView(context: Context) : SwipeRefreshLayout(context) {
22
+ val recycler = RecyclerView(context)
23
+ val roots = HashMap<String, View>()
24
+ val heights = HashMap<String, Double>()
25
+ val holders = HashMap<String, Holder>()
26
+ var horizontal = false
27
+ val manager = LinearLayoutManager(context)
28
+ val rows = object : ListAdapter<Item, Holder>(object : DiffUtil.ItemCallback<Item>() {
29
+ override fun areItemsTheSame(a: Item, b: Item) = a.key == b.key
30
+ override fun areContentsTheSame(a: Item, b: Item) = a == b
31
+ }) {
32
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder = Holder(FrameLayout(context))
33
+ override fun onBindViewHolder(holder: Holder, position: Int) {
34
+ holders.remove(holder.key)
35
+ val item = getItem(position)
36
+ holder.key = item.key
37
+ holders[item.key] = holder
38
+ attach(holder, item)
39
+ fire(this@ListView, "on_bind_row", JSONObject().put("index", position).put("key", item.key)
40
+ .put("revision", item.revision).put("width", width / PNBridge.density()))
41
+ }
42
+ override fun onViewRecycled(holder: Holder) {
43
+ holders.remove(holder.key)
44
+ holder.container.removeAllViews()
45
+ }
46
+ }
47
+ init {
48
+ addView(recycler, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
49
+ recycler.layoutManager = manager
50
+ recycler.adapter = rows
51
+ recycler.itemAnimator = null
52
+ isEnabled = false
53
+ setOnRefreshListener { fire(this, "on_refresh") }
54
+ recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
55
+ override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
56
+ fire(this@ListView, "on_scroll", JSONObject().put("x", recycler.computeHorizontalScrollOffset() / PNBridge.density())
57
+ .put("y", recycler.computeVerticalScrollOffset() / PNBridge.density())
58
+ .put("extent", (if (horizontal) width else height) / PNBridge.density())
59
+ .put("range", (if (horizontal) recycler.computeHorizontalScrollRange() else recycler.computeVerticalScrollRange()) / PNBridge.density())
60
+ .put("first", manager.findFirstVisibleItemPosition()).put("last", manager.findLastVisibleItemPosition()))
61
+ }
62
+ })
63
+ }
64
+ fun attach(holder: Holder, item: Item) {
65
+ val size = ((heights[item.key] ?: item.estimate) * PNBridge.density()).roundToInt().coerceAtLeast(1)
66
+ // Preserve RecyclerView.LayoutParams, which retain the holder identity.
67
+ val params = holder.container.layoutParams as? RecyclerView.LayoutParams
68
+ ?: RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, size)
69
+ params.width = if (horizontal) size else ViewGroup.LayoutParams.MATCH_PARENT
70
+ params.height = if (horizontal) ViewGroup.LayoutParams.MATCH_PARENT else size
71
+ holder.container.layoutParams = params
72
+ holder.container.removeAllViews()
73
+ roots[item.key]?.let { root ->
74
+ (root.parent as? ViewGroup)?.removeView(root)
75
+ holder.container.addView(root, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
76
+ }
77
+ }
78
+ }
79
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = ListView(context)
80
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
81
+ ViewStyler.apply(view, props)
82
+ val list = view as ListView
83
+ val all = PNBridge.registry.recordFor(view)?.props ?: props
84
+ list.horizontal = all.optBoolean("horizontal", false)
85
+ list.manager.orientation = if (list.horizontal) RecyclerView.HORIZONTAL else RecyclerView.VERTICAL
86
+ list.recycler.isVerticalScrollBarEnabled = all.optBoolean("shows_scroll_indicator", true)
87
+ val refresh = all.optJSONObject("refresh_control")
88
+ list.isEnabled = refresh != null && !list.horizontal
89
+ list.isRefreshing = refresh?.optBoolean("refreshing", false) == true
90
+ refresh?.optString("tint_color")?.takeIf { it.isNotEmpty() }?.let { color -> PNColor.parse(color)?.let { list.setColorSchemeColors(it) } }
91
+ if (initial || props.has("keys") || props.has("revision") || props.has("row_heights")) {
92
+ val keys = all.optJSONArray("keys")
93
+ val heights = all.optJSONArray("row_heights")
94
+ val items = (0 until (keys?.length() ?: 0)).map { Item(keys!!.getString(it), all.optInt("revision"), heights?.optDouble(it, 44.0) ?: 44.0) }
95
+ val first = list.manager.findFirstVisibleItemPosition()
96
+ val anchor = list.rows.currentList.getOrNull(first)?.key
97
+ val anchorView = list.manager.findViewByPosition(first)
98
+ val offset = (if (list.horizontal) anchorView?.left else anchorView?.top) ?: 0
99
+ list.rows.submitList(items) {
100
+ val position = items.indexOfFirst { it.key == anchor }
101
+ if (position >= 0) list.manager.scrollToPositionWithOffset(position, offset)
102
+ }
103
+ list.heights.keys.retainAll(items.map { it.key }.toSet())
104
+ }
105
+ }
106
+ override fun insertChild(parent: View, child: View, index: Int) {
107
+ val list = parent as ListView
108
+ val record = PNBridge.registry.recordFor(child) ?: return
109
+ val key = record.props.optString("_pn_list_key")
110
+ list.roots[key] = child
111
+ rowOwners[record.tag] = { width, height ->
112
+ val extent = if (list.horizontal) width else height
113
+ if (extent > 0 && list.heights[key] != extent) {
114
+ list.heights[key] = extent
115
+ list.holders[key]?.let { holder -> list.rows.currentList.find { it.key == key }?.let { list.attach(holder, it) } }
116
+ }
117
+ }
118
+ list.holders[key]?.let { holder -> list.rows.currentList.find { it.key == key }?.let { list.attach(holder, it) } }
119
+ }
120
+ override fun removeChild(parent: View, child: View) {
121
+ val list = parent as ListView
122
+ PNBridge.registry.recordFor(child)?.let { record ->
123
+ list.roots.remove(record.props.optString("_pn_list_key"))
124
+ rowOwners.remove(record.tag)
125
+ }
126
+ (child.parent as? ViewGroup)?.removeView(child)
127
+ }
128
+ override fun teardown(view: View) {
129
+ val list = view as ListView
130
+ for (root in list.roots.values) PNBridge.registry.recordFor(root)?.let { rowOwners.remove(it.tag) }
131
+ list.recycler.adapter = null
132
+ list.roots.clear()
133
+ list.holders.clear()
134
+ }
135
+ override fun measure(view: View, maxWidth: Double, maxHeight: Double): FloatArray =
136
+ floatArrayOf(if (maxWidth < 1e6) maxWidth.toFloat() else 0f, if (maxHeight < 1e6) maxHeight.toFloat() else 0f)
137
+ override fun command(view: View, name: String, args: JSONObject): Any? {
138
+ val list = view as ListView
139
+ val animated = args.optBoolean("animated", true)
140
+ val index = when (name) {
141
+ "scroll_to_end" -> (list.rows.itemCount - 1).coerceAtLeast(0)
142
+ "scroll_to_index" -> args.optInt("index").coerceIn(0, (list.rows.itemCount - 1).coerceAtLeast(0))
143
+ else -> -1
144
+ }
145
+ if (index >= 0) { if (animated) list.recycler.smoothScrollToPosition(index) else list.manager.scrollToPositionWithOffset(index, 0) }
146
+ if (name == "scroll_to_offset") {
147
+ val delta = (args.optDouble(if (list.horizontal) "x" else "y") * PNBridge.density()).roundToInt() -
148
+ (if (list.horizontal) list.recycler.computeHorizontalScrollOffset() else list.recycler.computeVerticalScrollOffset())
149
+ val x = if (list.horizontal) delta else 0
150
+ val y = if (list.horizontal) 0 else delta
151
+ if (animated) list.recycler.smoothScrollBy(x, y) else list.recycler.scrollBy(x, y)
152
+ }
153
+ return null
154
+ }
155
+ companion object {
156
+ private val rowOwners = HashMap<Long, (Double, Double) -> Unit>()
157
+ fun measured(tag: Long, width: Double, height: Double) { rowOwners[tag]?.invoke(width, height) }
158
+ }
159
+ }
@@ -0,0 +1,149 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.graphics.Bitmap
6
+ import android.view.MotionEvent
7
+ import android.view.View
8
+ import android.webkit.WebResourceError
9
+ import android.webkit.WebResourceRequest
10
+ import android.webkit.WebView
11
+ import android.webkit.WebViewClient
12
+ import com.pythonnative.runtime.bridge.JsonUtil
13
+ import com.pythonnative.runtime.bridge.str
14
+ import com.pythonnative.runtime.bridge.value
15
+ import org.json.JSONArray
16
+ import org.json.JSONObject
17
+ import java.net.URLDecoder
18
+
19
+ /**
20
+ * `WebView` element. Fires `on_navigation_state_change`, `on_load`,
21
+ * `on_error`, and `on_message` (via a `window.pythonnative.postMessage`
22
+ * shim that navigates to a `pythonnative://message/` URL). Commands:
23
+ * `load_url`, `reload`, `go_back`, `go_forward`, `stop_loading`,
24
+ * `inject_javascript`, `post_message`.
25
+ */
26
+ class WebViewManager : ComponentManager() {
27
+ private val scheme = "pythonnative://message/"
28
+
29
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
30
+ val wv = WebView(context)
31
+ wv.webViewClient = client(wv)
32
+ return wv
33
+ }
34
+
35
+ @SuppressLint("SetJavaScriptEnabled")
36
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
37
+ val wv = view as WebView
38
+ val merged = propsOf(wv)
39
+ val events = merged.value("_pn_events") as? JSONArray
40
+ val needsJs = merged.value("inject_javascript") != null ||
41
+ (events != null && events.length() > 0) ||
42
+ merged.value("javascript_enabled") != false
43
+ if (needsJs) wv.settings.javaScriptEnabled = true
44
+ if (props.has("javascript_enabled")) wv.settings.javaScriptEnabled = JsonUtil.truthy(props.value("javascript_enabled"))
45
+ if (props.has("dom_storage_enabled")) wv.settings.domStorageEnabled = JsonUtil.truthy(props.value("dom_storage_enabled"))
46
+ if (props.has("user_agent")) wv.settings.userAgentString = props.str("user_agent")
47
+
48
+ val html = props.str("html")
49
+ val url = props.str("url") ?: sourceUrl(props.value("source"))
50
+ // `html` takes precedence over `url` when both are present.
51
+ if (!html.isNullOrEmpty()) {
52
+ wv.loadDataWithBaseURL(props.str("base_url"), html, "text/html", "utf-8", null)
53
+ } else if (!url.isNullOrEmpty()) {
54
+ wv.loadUrl(url)
55
+ }
56
+ if (props.has("scroll_enabled")) applyScrollEnabled(wv, props.value("scroll_enabled"))
57
+ ViewStyler.apply(wv, props)
58
+ }
59
+
60
+ private fun sourceUrl(source: Any?): String? = when (source) {
61
+ is JSONObject -> source.str("uri") ?: source.str("url")
62
+ is String -> source
63
+ else -> null
64
+ }
65
+
66
+ @SuppressLint("ClickableViewAccessibility")
67
+ private fun applyScrollEnabled(wv: WebView, enabled: Any?) {
68
+ if (enabled == false) {
69
+ wv.setOnTouchListener { _, event -> event.action == MotionEvent.ACTION_MOVE }
70
+ } else {
71
+ wv.setOnTouchListener(null)
72
+ }
73
+ }
74
+
75
+ private fun client(wv: WebView): WebViewClient = object : WebViewClient() {
76
+ override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
77
+ fire(wv, "on_navigation_state_change", url ?: "")
78
+ fire(wv, "on_load_start", url ?: "")
79
+ }
80
+
81
+ override fun onPageFinished(view: WebView, url: String?) {
82
+ fire(wv, "on_load", url ?: "")
83
+ if (hasEvent(wv, "on_message")) {
84
+ val shim = "(function(){window.pythonnative=window.pythonnative||{};" +
85
+ "window.pythonnative.postMessage=function(m){" +
86
+ "window.location.href='$scheme'+encodeURIComponent(m);};})();"
87
+ view.evaluateJavascript(shim, null)
88
+ }
89
+ propsOf(wv).str("inject_javascript")?.takeIf { it.isNotEmpty() }?.let { view.evaluateJavascript(it, null) }
90
+ }
91
+
92
+ override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
93
+ if (request.isForMainFrame) {
94
+ fire(wv, "on_error", error.description?.toString() ?: "load error")
95
+ }
96
+ }
97
+
98
+ override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
99
+ val url = request.url?.toString() ?: return false
100
+ return handleUrl(url)
101
+ }
102
+
103
+ @Deprecated("Deprecated in Java")
104
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean = handleUrl(url)
105
+
106
+ private fun handleUrl(url: String): Boolean {
107
+ if (url.startsWith(scheme)) {
108
+ val message = try {
109
+ URLDecoder.decode(url.substring(scheme.length), "UTF-8")
110
+ } catch (e: Exception) {
111
+ url.substring(scheme.length)
112
+ }
113
+ fire(wv, "on_message", message)
114
+ return true
115
+ }
116
+ return false
117
+ }
118
+ }
119
+
120
+ override fun command(view: View, name: String, args: JSONObject): Any? {
121
+ val wv = view as? WebView ?: return null
122
+ when (name) {
123
+ "load_url" -> args.str("url")?.let { wv.loadUrl(it) }
124
+ "reload" -> wv.reload()
125
+ "go_back" -> if (wv.canGoBack()) wv.goBack()
126
+ "go_forward" -> if (wv.canGoForward()) wv.goForward()
127
+ "stop_loading" -> wv.stopLoading()
128
+ "inject_javascript", "evaluate_javascript" -> {
129
+ val script = args.str("script") ?: args.str("javascript") ?: return null
130
+ wv.evaluateJavascript(script, null)
131
+ }
132
+ "post_message" -> {
133
+ val message = JSONObject.quote(args.str("message") ?: "")
134
+ wv.evaluateJavascript("window.dispatchEvent(new MessageEvent('message',{data:$message}));", null)
135
+ }
136
+ "can_go_back" -> return wv.canGoBack()
137
+ "can_go_forward" -> return wv.canGoForward()
138
+ "get_url" -> return wv.url
139
+ }
140
+ return null
141
+ }
142
+
143
+ override fun teardown(view: View) {
144
+ (view as? WebView)?.let {
145
+ it.stopLoading()
146
+ it.destroy()
147
+ }
148
+ }
149
+ }