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,69 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import androidx.core.view.ViewCompat
6
+ import androidx.core.view.WindowInsetsCompat
7
+ import com.pythonnative.runtime.PNBridge
8
+ import com.pythonnative.runtime.modules.ModuleEvents
9
+ import com.pythonnative.runtime.views.PNFrameLayout
10
+ import org.json.JSONObject
11
+ import kotlin.math.max
12
+
13
+ /**
14
+ * Flex container (shared by `View`, `Column`, and `Row`): a bare
15
+ * [PNFrameLayout]. All flex semantics are computed by the Python layout
16
+ * engine and applied through `setFrame`; the container is just a
17
+ * positioning surface with `pointer_events` interception.
18
+ */
19
+ open class ViewManager : ComponentManager() {
20
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = PNFrameLayout(context)
21
+ }
22
+
23
+ /** Empty layout placeholder used as a flexible gap. */
24
+ class SpacerManager : ComponentManager() {
25
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = View(context)
26
+
27
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
28
+ // Spacers carry no visual props of their own.
29
+ }
30
+ }
31
+
32
+ /** Safe-area container: a flex container with `fitsSystemWindows`. */
33
+ class SafeAreaViewManager : ViewManager() {
34
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
35
+ val view = PNFrameLayout(context)
36
+ view.fitsSystemWindows = true
37
+ return view
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Vanilla container that publishes the keyboard height (from window
43
+ * insets) as a `Host` module `keyboard` event so the Python-side
44
+ * component can compute its offset.
45
+ */
46
+ class KeyboardAvoidingViewManager : ViewManager() {
47
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
48
+ val view = PNFrameLayout(context)
49
+ ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
50
+ publishKeyboard(v, insets)
51
+ insets
52
+ }
53
+ view.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
54
+ ViewCompat.getRootWindowInsets(v)?.let { publishKeyboard(v, it) }
55
+ }
56
+ return view
57
+ }
58
+
59
+ private fun publishKeyboard(view: View, insets: WindowInsetsCompat) {
60
+ val density = PNBridge.density()
61
+ val ime = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
62
+ val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
63
+ val height = max(0, ime - bars) / density
64
+ val last = stateOf(view)["keyboard_height"] as? Double
65
+ if (last != null && last == height.toDouble()) return
66
+ stateOf(view)["keyboard_height"] = height.toDouble()
67
+ ModuleEvents.emit("Host", "keyboard", JSONObject().put("height", height.toDouble()))
68
+ }
69
+ }
@@ -0,0 +1,186 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.content.res.ColorStateList
5
+ import android.view.View
6
+ import android.widget.CheckBox
7
+ import android.widget.CompoundButton
8
+ import android.widget.ProgressBar
9
+ import android.widget.SeekBar
10
+ import android.widget.Switch
11
+ import com.pythonnative.runtime.bridge.JsonUtil
12
+ import com.pythonnative.runtime.bridge.num
13
+ import com.pythonnative.runtime.bridge.str
14
+ import com.pythonnative.runtime.bridge.value
15
+ import org.json.JSONObject
16
+
17
+ /** Base for `CompoundButton` widgets: `on_change` wiring with a suppress guard. */
18
+ abstract class CheckedManager : ComponentManager() {
19
+ protected fun bindChecked(button: CompoundButton) {
20
+ button.setOnCheckedChangeListener { b, checked ->
21
+ if (stateOf(b)["suppress"] == true) return@setOnCheckedChangeListener
22
+ fire(b, "on_change", checked)
23
+ }
24
+ }
25
+ }
26
+
27
+ /** `Switch` element. */
28
+ class SwitchManager : CheckedManager() {
29
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
30
+ val sw = Switch(context)
31
+ bindChecked(sw)
32
+ return sw
33
+ }
34
+
35
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
36
+ val sw = view as Switch
37
+ if (props.has("value")) {
38
+ val state = stateOf(sw)
39
+ state["suppress"] = true
40
+ try {
41
+ sw.isChecked = JsonUtil.truthy(props.value("value"))
42
+ } finally {
43
+ state["suppress"] = false
44
+ }
45
+ }
46
+ if (props.has("disabled")) sw.isEnabled = !JsonUtil.truthy(props.value("disabled"))
47
+ if (props.has("enabled")) sw.isEnabled = JsonUtil.truthy(props.value("enabled"))
48
+ PNColor.parse(props.value("on_tint_color") ?: props.value("tint_color"))?.let {
49
+ sw.thumbTintList = ColorStateList(
50
+ arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
51
+ intArrayOf(it, 0xFFFAFAFA.toInt()),
52
+ )
53
+ sw.trackTintList = ColorStateList(
54
+ arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
55
+ intArrayOf(PNColor.withAlpha(it, 0.5), 0x61000000),
56
+ )
57
+ }
58
+ PNColor.parse(props.value("thumb_color"))?.let { sw.thumbTintList = ColorStateList.valueOf(it) }
59
+ ViewStyler.applyAccessibility(sw, props)
60
+ }
61
+ }
62
+
63
+ /** `Checkbox` element with an optional inline label. */
64
+ class CheckboxManager : CheckedManager() {
65
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
66
+ val cb = CheckBox(context)
67
+ bindChecked(cb)
68
+ return cb
69
+ }
70
+
71
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
72
+ val cb = view as CheckBox
73
+ if (props.has("label")) cb.text = props.str("label") ?: ""
74
+ if (props.has("value")) {
75
+ val state = stateOf(cb)
76
+ state["suppress"] = true
77
+ try {
78
+ cb.isChecked = JsonUtil.truthy(props.value("value"))
79
+ } finally {
80
+ state["suppress"] = false
81
+ }
82
+ }
83
+ if (props.has("disabled")) cb.isEnabled = !JsonUtil.truthy(props.value("disabled"))
84
+ PNColor.parse(props.value("color"))?.let { cb.buttonTintList = ColorStateList.valueOf(it) }
85
+ ViewStyler.applyAccessibility(cb, props)
86
+ }
87
+ }
88
+
89
+ /** `ProgressBar` element: a horizontal determinate bar with a 0..1 `value`. */
90
+ class ProgressBarManager : ComponentManager() {
91
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
92
+ val pb = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal)
93
+ pb.max = 1000
94
+ return pb
95
+ }
96
+
97
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
98
+ val pb = view as ProgressBar
99
+ props.num("value")?.let { pb.progress = (it.coerceIn(0.0, 1.0) * 1000).toInt() }
100
+ PNColor.parse(props.value("color"))?.let { pb.progressTintList = ColorStateList.valueOf(it) }
101
+ PNColor.parse(props.value("track_color"))?.let {
102
+ val track = ColorStateList.valueOf(it)
103
+ pb.progressBackgroundTintList = track
104
+ pb.secondaryProgressTintList = track
105
+ }
106
+ if (props.has("indeterminate")) pb.isIndeterminate = JsonUtil.truthy(props.value("indeterminate"))
107
+ ViewStyler.apply(pb, props)
108
+ }
109
+ }
110
+
111
+ /** `ActivityIndicator` element: an indeterminate spinner. */
112
+ class ActivityIndicatorManager : ComponentManager() {
113
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = ProgressBar(context)
114
+
115
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
116
+ val pb = view as ProgressBar
117
+ if (props.has("animating")) {
118
+ pb.visibility = if (JsonUtil.truthy(props.value("animating"))) View.VISIBLE else View.GONE
119
+ }
120
+ if (props.has("hides_when_stopped") && !JsonUtil.truthy(props.value("hides_when_stopped"))) {
121
+ pb.visibility = View.VISIBLE
122
+ }
123
+ PNColor.parse(props.value("color"))?.let { pb.indeterminateTintList = ColorStateList.valueOf(it) }
124
+ props.str("size")?.let {
125
+ // The framework ProgressBar has no runtime size switch; scale "large".
126
+ val scale = if (it == "large") 1.5f else 1f
127
+ pb.scaleX = scale
128
+ pb.scaleY = scale
129
+ }
130
+ ViewStyler.apply(pb, props)
131
+ }
132
+ }
133
+
134
+ /** `Slider` element: a `SeekBar` mapped onto `min_value..max_value`. */
135
+ class SliderManager : ComponentManager() {
136
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
137
+ val sb = SeekBar(context)
138
+ sb.max = 1000
139
+ sb.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
140
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
141
+ if (!fromUser) return
142
+ fire(seekBar, "on_change", valueFor(seekBar, progress))
143
+ }
144
+
145
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
146
+ fire(seekBar, "on_sliding_start", valueFor(seekBar, seekBar.progress))
147
+ }
148
+
149
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
150
+ fire(seekBar, "on_sliding_complete", valueFor(seekBar, seekBar.progress))
151
+ }
152
+ })
153
+ return sb
154
+ }
155
+
156
+ private fun range(view: View): Pair<Double, Double> {
157
+ val merged = propsOf(view)
158
+ val mn = merged.num("min_value") ?: merged.num("minimum_value") ?: 0.0
159
+ val mx = merged.num("max_value") ?: merged.num("maximum_value") ?: 1.0
160
+ return Pair(mn, mx)
161
+ }
162
+
163
+ private fun valueFor(view: View, progress: Int): Double {
164
+ val (mn, mx) = range(view)
165
+ val span = if (mx != mn) mx - mn else 1.0
166
+ var value = mn + (progress / 1000.0) * span
167
+ propsOf(view).num("step")?.takeIf { it > 0 }?.let { step ->
168
+ value = mn + Math.round((value - mn) / step) * step
169
+ }
170
+ return value
171
+ }
172
+
173
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
174
+ val sb = view as SeekBar
175
+ val (mn, mx) = range(sb)
176
+ val span = if (mx != mn) mx - mn else 1.0
177
+ props.num("value")?.let { sb.progress = (((it - mn) / span) * 1000).toInt().coerceIn(0, 1000) }
178
+ if (props.has("disabled")) sb.isEnabled = !JsonUtil.truthy(props.value("disabled"))
179
+ PNColor.parse(props.value("minimum_track_tint_color") ?: props.value("tint_color"))?.let {
180
+ sb.progressTintList = ColorStateList.valueOf(it)
181
+ }
182
+ PNColor.parse(props.value("maximum_track_tint_color"))?.let { sb.progressBackgroundTintList = ColorStateList.valueOf(it) }
183
+ PNColor.parse(props.value("thumb_tint_color"))?.let { sb.thumbTintList = ColorStateList.valueOf(it) }
184
+ ViewStyler.applyAccessibility(sb, props)
185
+ }
186
+ }
@@ -0,0 +1,123 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.app.DatePickerDialog
4
+ import android.app.TimePickerDialog
5
+ import android.content.Context
6
+ import android.view.View
7
+ import android.widget.Button
8
+ import com.pythonnative.runtime.bridge.PNLog
9
+ import com.pythonnative.runtime.bridge.str
10
+ import com.pythonnative.runtime.bridge.value
11
+ import org.json.JSONObject
12
+ import java.text.SimpleDateFormat
13
+ import java.util.Calendar
14
+ import java.util.Locale
15
+
16
+ /**
17
+ * `DatePicker` element: a trigger `Button` opening the native
18
+ * `DatePickerDialog` (`mode` `"date"`), `TimePickerDialog` (`"time"`),
19
+ * or a chained date-then-time flow (`"datetime"`). Values are ISO
20
+ * strings; the confirmed value is reported through `on_change`.
21
+ */
22
+ class DatePickerManager : ComponentManager() {
23
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
24
+ val btn = Button(context)
25
+ btn.isAllCaps = false
26
+ btn.setOnClickListener { v ->
27
+ if (propsOf(v).value("disabled") == true) return@setOnClickListener
28
+ openDialog(v as Button)
29
+ }
30
+ return btn
31
+ }
32
+
33
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
34
+ val btn = view as Button
35
+ if (props.has("disabled")) btn.isEnabled = props.value("disabled") != true
36
+ if (props.has("value") || props.has("mode") || initial) refreshLabel(btn)
37
+ ViewStyler.applyAccessibility(btn, props)
38
+ }
39
+
40
+ private fun mode(btn: Button): String = propsOf(btn).str("mode") ?: "date"
41
+
42
+ private fun refreshLabel(btn: Button) {
43
+ val value = propsOf(btn).str("value")
44
+ btn.text = if (!value.isNullOrEmpty()) value else PLACEHOLDERS[mode(btn)] ?: "Select"
45
+ }
46
+
47
+ private fun openDialog(btn: Button) {
48
+ val mode = mode(btn)
49
+ val cal = parse(propsOf(btn).str("value"), mode)
50
+ when (mode) {
51
+ "time" -> openTime(btn, cal)
52
+ "datetime" -> openDate(btn, cal, thenTime = true)
53
+ else -> openDate(btn, cal, thenTime = false)
54
+ }
55
+ }
56
+
57
+ private fun openDate(btn: Button, cal: Calendar, thenTime: Boolean) {
58
+ val dialog = DatePickerDialog(
59
+ btn.context,
60
+ { _, year, month, day ->
61
+ cal.set(Calendar.YEAR, year)
62
+ cal.set(Calendar.MONTH, month)
63
+ cal.set(Calendar.DAY_OF_MONTH, day)
64
+ if (thenTime) openTime(btn, cal) else commit(btn, cal)
65
+ },
66
+ cal.get(Calendar.YEAR),
67
+ cal.get(Calendar.MONTH),
68
+ cal.get(Calendar.DAY_OF_MONTH),
69
+ )
70
+ try {
71
+ val merged = propsOf(btn)
72
+ merged.str("minimum")?.takeIf { it.isNotEmpty() }?.let { dialog.datePicker.minDate = parse(it, mode(btn)).timeInMillis }
73
+ merged.str("maximum")?.takeIf { it.isNotEmpty() }?.let { dialog.datePicker.maxDate = parse(it, mode(btn)).timeInMillis }
74
+ } catch (e: Exception) {
75
+ PNLog.swallowed("DatePickerManager.minMax", e)
76
+ }
77
+ dialog.show()
78
+ }
79
+
80
+ private fun openTime(btn: Button, cal: Calendar) {
81
+ TimePickerDialog(
82
+ btn.context,
83
+ { _, hour, minute ->
84
+ cal.set(Calendar.HOUR_OF_DAY, hour)
85
+ cal.set(Calendar.MINUTE, minute)
86
+ commit(btn, cal)
87
+ },
88
+ cal.get(Calendar.HOUR_OF_DAY),
89
+ cal.get(Calendar.MINUTE),
90
+ true,
91
+ ).show()
92
+ }
93
+
94
+ private fun format(mode: String): SimpleDateFormat = SimpleDateFormat(PATTERNS[mode] ?: PATTERNS["date"]!!, Locale.US)
95
+
96
+ private fun parse(value: String?, mode: String): Calendar {
97
+ val cal = Calendar.getInstance()
98
+ if (!value.isNullOrEmpty()) {
99
+ try {
100
+ format(mode).parse(value)?.let { cal.time = it }
101
+ } catch (e: Exception) {
102
+ PNLog.swallowed("DatePickerManager.parse", e)
103
+ }
104
+ }
105
+ return cal
106
+ }
107
+
108
+ private fun commit(btn: Button, cal: Calendar) {
109
+ val iso = try {
110
+ format(mode(btn)).format(cal.time)
111
+ } catch (e: Exception) {
112
+ return
113
+ }
114
+ propsOf(btn).put("value", iso)
115
+ btn.text = iso
116
+ fire(btn, "on_change", iso)
117
+ }
118
+
119
+ private companion object {
120
+ val PATTERNS = mapOf("date" to "yyyy-MM-dd", "time" to "HH:mm", "datetime" to "yyyy-MM-dd'T'HH:mm")
121
+ val PLACEHOLDERS = mapOf("date" to "Select date", "time" to "Select time", "datetime" to "Select date & time")
122
+ }
123
+ }
@@ -0,0 +1,141 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.BitmapFactory
6
+ import android.util.LruCache
7
+ import com.pythonnative.runtime.bridge.MainThread
8
+ import java.io.File
9
+ import java.io.FileOutputStream
10
+ import java.net.HttpURLConnection
11
+ import java.net.URL
12
+ import java.security.MessageDigest
13
+ import java.util.concurrent.Executors
14
+ import kotlin.math.max
15
+
16
+ /**
17
+ * Image fetching and decoding for the `Image` manager.
18
+ *
19
+ * Remote images are downloaded on a small background pool into a disk
20
+ * cache under `cacheDir/pn_images` (keyed by the SHA-256 of the URL),
21
+ * decoded with `inSampleSize` downsampling to the target size, and kept
22
+ * in a memory `LruCache`. Callbacks run on the main thread.
23
+ */
24
+ object ImageLoader {
25
+ private val executor = Executors.newFixedThreadPool(3) { runnable ->
26
+ Thread(runnable, "pn-image").apply { isDaemon = true }
27
+ }
28
+
29
+ private val memory: LruCache<String, Bitmap> by lazy {
30
+ val maxKb = (Runtime.getRuntime().maxMemory() / 1024L).toInt()
31
+ object : LruCache<String, Bitmap>(max(4 * 1024, maxKb / 8)) {
32
+ override fun sizeOf(key: String, value: Bitmap): Int = max(1, value.byteCount / 1024)
33
+ }
34
+ }
35
+
36
+ /** Result callback: exactly one of `bitmap` or `error` is non-null. */
37
+ fun interface Callback {
38
+ fun onResult(bitmap: Bitmap?, error: String?)
39
+ }
40
+
41
+ /** Load `url` (http/https) into a bitmap sized for `targetW` x `targetH` pixels. */
42
+ fun loadRemote(context: Context, url: String, targetW: Int, targetH: Int, callback: Callback) {
43
+ val key = "$url@$targetW"
44
+ memory.get(key)?.let {
45
+ callback.onResult(it, null)
46
+ return
47
+ }
48
+ val cacheDir = File(context.cacheDir, "pn_images")
49
+ executor.execute {
50
+ try {
51
+ val file = cachedFile(cacheDir, url)
52
+ if (!file.exists() || file.length() == 0L) download(url, file)
53
+ val bitmap = decodeDownsampled(file.absolutePath, targetW, targetH)
54
+ if (bitmap == null) {
55
+ file.delete()
56
+ deliver(callback, null, "decode failed")
57
+ } else {
58
+ memory.put(key, bitmap)
59
+ deliver(callback, bitmap, null)
60
+ }
61
+ } catch (e: Exception) {
62
+ deliver(callback, null, e.message ?: e.javaClass.simpleName)
63
+ }
64
+ }
65
+ }
66
+
67
+ /** Decode a local file on a background thread. */
68
+ fun loadFile(path: String, targetW: Int, targetH: Int, callback: Callback) {
69
+ executor.execute {
70
+ val bitmap = try {
71
+ decodeDownsampled(path, targetW, targetH)
72
+ } catch (e: Exception) {
73
+ null
74
+ }
75
+ deliver(callback, bitmap, if (bitmap == null) "decode failed" else null)
76
+ }
77
+ }
78
+
79
+ /** Decode `path` with `inSampleSize` chosen so the result still covers the target. */
80
+ fun decodeDownsampled(path: String, targetW: Int, targetH: Int): Bitmap? {
81
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
82
+ BitmapFactory.decodeFile(path, bounds)
83
+ val srcW = bounds.outWidth
84
+ val srcH = bounds.outHeight
85
+ if (srcW <= 0 || srcH <= 0) return null
86
+ var tw = if (targetW > 0) targetW else srcW
87
+ var th = if (targetH > 0) targetH else tw
88
+ if (tw <= 0) tw = srcW
89
+ if (th <= 0) th = srcH
90
+ var sample = 1
91
+ while (srcW / (sample * 2) >= tw && srcH / (sample * 2) >= th) sample *= 2
92
+ val opts = BitmapFactory.Options().apply { inSampleSize = sample }
93
+ return BitmapFactory.decodeFile(path, opts)
94
+ }
95
+
96
+ private fun cachedFile(dir: File, url: String): File {
97
+ if (!dir.exists()) dir.mkdirs()
98
+ val digest = MessageDigest.getInstance("SHA-256").digest(url.toByteArray(Charsets.UTF_8))
99
+ val hex = digest.joinToString("") { String.format("%02x", it) }
100
+ val path = url.substringBefore('?').substringBefore('#')
101
+ val ext = path.substringAfterLast('.', "").takeIf { it.length in 1..5 && it.all { c -> c.isLetterOrDigit() } }
102
+ return File(dir, if (ext != null) "$hex.$ext" else hex)
103
+ }
104
+
105
+ private fun download(url: String, target: File) {
106
+ var current = url
107
+ var redirects = 0
108
+ while (true) {
109
+ val connection = URL(current).openConnection() as HttpURLConnection
110
+ connection.connectTimeout = 15_000
111
+ connection.readTimeout = 30_000
112
+ connection.instanceFollowRedirects = true
113
+ connection.setRequestProperty("User-Agent", "PythonNative/Android")
114
+ try {
115
+ val code = connection.responseCode
116
+ if (code in 300..399 && redirects < 5) {
117
+ val location = connection.getHeaderField("Location") ?: throw IllegalStateException("redirect without Location")
118
+ current = URL(URL(current), location).toString()
119
+ redirects++
120
+ continue
121
+ }
122
+ if (code !in 200..299) throw IllegalStateException("HTTP $code")
123
+ val tmp = File(target.parentFile, target.name + ".part")
124
+ connection.inputStream.use { input ->
125
+ FileOutputStream(tmp).use { output -> input.copyTo(output) }
126
+ }
127
+ if (!tmp.renameTo(target)) {
128
+ tmp.copyTo(target, overwrite = true)
129
+ tmp.delete()
130
+ }
131
+ return
132
+ } finally {
133
+ connection.disconnect()
134
+ }
135
+ }
136
+ }
137
+
138
+ private fun deliver(callback: Callback, bitmap: Bitmap?, error: String?) {
139
+ MainThread.post { callback.onResult(bitmap, error) }
140
+ }
141
+ }
@@ -0,0 +1,162 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import com.pythonnative.runtime.PNBridge
4
+
5
+ import android.content.Context
6
+ import android.content.res.ColorStateList
7
+ import android.graphics.BitmapFactory
8
+ import android.util.Base64
9
+ import android.view.View
10
+ import android.widget.ImageView
11
+ import com.pythonnative.runtime.bridge.PNLog
12
+ import com.pythonnative.runtime.bridge.str
13
+ import com.pythonnative.runtime.bridge.value
14
+ import org.json.JSONObject
15
+
16
+ /**
17
+ * `Image` element. Sources: `http(s)` URLs (downloaded and cached by
18
+ * [ImageLoader]), absolute file paths, drawable resource names, and
19
+ * base64 `data:` URIs. Fires `on_load` / `on_error`.
20
+ */
21
+ class ImageManager : ComponentManager() {
22
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = ImageView(context)
23
+
24
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
25
+ val iv = view as ImageView
26
+ PNColor.parse(props.value("tint_color"))?.let { iv.imageTintList = ColorStateList.valueOf(it) }
27
+ if (props.has("tint_color") && props.value("tint_color") == null) iv.imageTintList = null
28
+ PNColor.parse(props.value("placeholder_color"))?.let { iv.setBackgroundColor(it) }
29
+ if (props.has("source")) {
30
+ val source = sourceString(props.value("source"))
31
+ if (source != null) loadSource(iv, source) else iv.setImageDrawable(null)
32
+ }
33
+ val mode = props.str("resize_mode") ?: props.str("scale_type")
34
+ if (mode != null) {
35
+ iv.scaleType = when (mode) {
36
+ "cover" -> ImageView.ScaleType.CENTER_CROP
37
+ "contain" -> ImageView.ScaleType.FIT_CENTER
38
+ "stretch" -> ImageView.ScaleType.FIT_XY
39
+ "center", "repeat" -> ImageView.ScaleType.CENTER
40
+ else -> iv.scaleType
41
+ }
42
+ }
43
+ ViewStyler.apply(iv, props)
44
+ }
45
+
46
+ private fun sourceString(value: Any?): String? {
47
+ return when (value) {
48
+ null -> null
49
+ is JSONObject -> value.str("uri") ?: value.str("url") ?: value.str("path")
50
+ else -> value.toString().takeIf { it.isNotEmpty() }
51
+ }
52
+ }
53
+
54
+ private fun loadSource(iv: ImageView, source: String) {
55
+ val state = stateOf(iv)
56
+ state["pending_uri"] = source
57
+ try {
58
+ when {
59
+ source.startsWith("data:") -> loadDataUri(iv, source)
60
+ source.startsWith("http://") || source.startsWith("https://") -> {
61
+ val (tw, th) = targetSize(iv)
62
+ ImageLoader.loadRemote(iv.context, source, tw, th) { bitmap, error ->
63
+ if (stateOf(iv)["pending_uri"] != source) return@loadRemote
64
+ if (bitmap != null) {
65
+ iv.setImageBitmap(bitmap)
66
+ fire(iv, "on_load")
67
+ PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
68
+ } else {
69
+ fire(iv, "on_error", error ?: "load failed")
70
+ }
71
+ }
72
+ }
73
+ source.startsWith("/") || source.startsWith("file://") -> {
74
+ val path = source.removePrefix("file://")
75
+ val (tw, th) = targetSize(iv)
76
+ ImageLoader.loadFile(path, tw, th) { bitmap, error ->
77
+ if (stateOf(iv)["pending_uri"] != source) return@loadFile
78
+ if (bitmap != null) {
79
+ iv.setImageBitmap(bitmap)
80
+ fire(iv, "on_load")
81
+ PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
82
+ } else {
83
+ fire(iv, "on_error", error ?: "decode failed")
84
+ }
85
+ }
86
+ }
87
+ else -> {
88
+ val ctx = iv.context
89
+ val name = source.substringBeforeLast('.', source)
90
+ val resId = ctx.resources.getIdentifier(name, "drawable", ctx.packageName)
91
+ .takeIf { it != 0 }
92
+ ?: ctx.resources.getIdentifier(name, "mipmap", ctx.packageName)
93
+ if (resId != 0) {
94
+ iv.setImageResource(resId)
95
+ fire(iv, "on_load")
96
+ PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
97
+ } else {
98
+ fire(iv, "on_error", "drawable '$name' not found")
99
+ }
100
+ }
101
+ }
102
+ } catch (e: Exception) {
103
+ PNLog.swallowed("ImageManager.loadSource", e)
104
+ fire(iv, "on_error", e.message ?: "load failed")
105
+ }
106
+ }
107
+
108
+ private fun loadDataUri(iv: ImageView, source: String) {
109
+ try {
110
+ val payload = source.substringAfter(',', "")
111
+ val raw = Base64.decode(payload, Base64.DEFAULT)
112
+ val bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.size)
113
+ if (bitmap != null) {
114
+ iv.setImageBitmap(bitmap)
115
+ fire(iv, "on_load")
116
+ PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
117
+ } else {
118
+ fire(iv, "on_error", "data URI decode failed")
119
+ }
120
+ } catch (e: Exception) {
121
+ fire(iv, "on_error", "data URI decode failed")
122
+ }
123
+ }
124
+
125
+ private fun targetSize(iv: ImageView): Pair<Int, Int> {
126
+ var w = iv.width
127
+ var h = iv.height
128
+ val frame = recordOf(iv)?.frame
129
+ if (w <= 0 && frame != null) {
130
+ w = px(frame[2])
131
+ h = px(frame[3])
132
+ }
133
+ if (w <= 0) {
134
+ val metrics = iv.context.resources.displayMetrics
135
+ w = metrics.widthPixels
136
+ h = metrics.heightPixels
137
+ }
138
+ if (h <= 0) h = w
139
+ return Pair(w, h)
140
+ }
141
+
142
+ override fun measure(view: View, maxWidth: Double, maxHeight: Double): FloatArray {
143
+ val iv = view as ImageView
144
+ val drawable = iv.drawable ?: return floatArrayOf(0f, 0f)
145
+ val density = view.context.resources.displayMetrics.density
146
+ // Bitmaps decode at device density, so their pixel size is already a dp-ish measure.
147
+ var w = drawable.intrinsicWidth / density
148
+ var h = drawable.intrinsicHeight / density
149
+ if (w <= 0f || h <= 0f) return floatArrayOf(0f, 0f)
150
+ val maxW = if (maxWidth.isFinite() && maxWidth < 1e6) maxWidth.toFloat() else Float.MAX_VALUE
151
+ val maxH = if (maxHeight.isFinite() && maxHeight < 1e6) maxHeight.toFloat() else Float.MAX_VALUE
152
+ if (w > maxW) {
153
+ h *= maxW / w
154
+ w = maxW
155
+ }
156
+ if (h > maxH) {
157
+ w *= maxH / h
158
+ h = maxH
159
+ }
160
+ return floatArrayOf(w, h)
161
+ }
162
+ }