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,250 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import com.pythonnative.runtime.PNBridge
4
+
5
+ import android.content.Context
6
+ import android.os.Build
7
+ import android.text.Editable
8
+ import android.text.InputFilter
9
+ import android.text.InputType
10
+ import android.text.TextWatcher
11
+ import android.view.MotionEvent
12
+ import android.view.View
13
+ import android.view.inputmethod.EditorInfo
14
+ import android.view.inputmethod.InputMethodManager
15
+ import android.widget.EditText
16
+ import com.pythonnative.runtime.bridge.JsonUtil
17
+ import com.pythonnative.runtime.bridge.PNLog
18
+ import com.pythonnative.runtime.bridge.num
19
+ import com.pythonnative.runtime.bridge.str
20
+ import com.pythonnative.runtime.bridge.value
21
+ import com.pythonnative.runtime.views.PNEditText
22
+ import org.json.JSONObject
23
+ import kotlin.math.max
24
+ import kotlin.math.min
25
+
26
+ /**
27
+ * `TextInput` element: an `EditText` reporting `on_change`, `on_submit`,
28
+ * `on_focus`, `on_blur`, and `on_selection_change`, with keyboard type,
29
+ * secure entry, return key, autofill, and clear-button props.
30
+ */
31
+ class TextInputManager : ComponentManager() {
32
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
33
+ val et = PNEditText(context)
34
+ // Default to single-line so Enter triggers the IME action instead of a newline.
35
+ if (!JsonUtil.truthy(props.value("multiline"))) et.setSingleLine(true)
36
+ bindListeners(et)
37
+ return et
38
+ }
39
+
40
+ private fun bindListeners(et: PNEditText) {
41
+ et.addTextChangedListener(object : TextWatcher {
42
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
43
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
44
+ override fun afterTextChanged(s: Editable?) {
45
+ if (stateOf(et)["suppress"] == true) return
46
+ fire(et, "on_change", s?.toString() ?: "")
47
+ PNBridge.registry.tagOf(et)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
48
+ }
49
+ })
50
+ et.setOnFocusChangeListener { v, hasFocus -> fire(v, if (hasFocus) "on_focus" else "on_blur") }
51
+ et.onSelectionChangedListener = { start, end ->
52
+ if (hasEvent(et, "on_selection_change")) {
53
+ fire(et, "on_selection_change", mapOf("start" to start, "end" to end))
54
+ }
55
+ }
56
+ installEditorAction(et)
57
+ }
58
+
59
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
60
+ val et = view as EditText
61
+ val state = stateOf(et)
62
+ val merged = propsOf(et)
63
+ if (props.has("value")) {
64
+ val incoming = props.str("value") ?: ""
65
+ val acknowledged = props.optLong("_pn_edit_revision", 0)
66
+ val edited = (state["edit_revision"] as? Number)?.toLong() ?: 0L
67
+ if (et.text.toString() != incoming && acknowledged >= edited && android.view.inputmethod.BaseInputConnection.getComposingSpanStart(et.text) < 0) {
68
+ val selStart = et.selectionStart
69
+ val selEnd = et.selectionEnd
70
+ state["suppress"] = true
71
+ try {
72
+ et.setText(incoming)
73
+ val maxPos = incoming.length
74
+ val start = max(0, min(selStart, maxPos))
75
+ val end = max(0, min(selEnd, maxPos))
76
+ if (start == end) et.setSelection(start) else et.setSelection(start, end)
77
+ } catch (e: Exception) {
78
+ PNLog.swallowed("TextInputManager.value", e)
79
+ } finally {
80
+ state["suppress"] = false
81
+ }
82
+ }
83
+ }
84
+ if (props.has("placeholder")) et.hint = props.str("placeholder") ?: ""
85
+ PNColor.parse(props.value("placeholder_color"))?.let { et.setHintTextColor(it) }
86
+ props.num("font_size")?.let { et.textSize = it.toFloat() }
87
+ PNColor.parse(props.value("color"))?.let { et.setTextColor(it) }
88
+ if (listOf("font_family", "font_weight", "italic", "bold").any { props.has(it) }) {
89
+ TextStyle.applyTypeface(et, merged)
90
+ }
91
+ if (listOf("multiline", "secure", "secure_text_entry", "keyboard_type", "auto_capitalize", "auto_correct").any { props.has(it) }) {
92
+ applyInputType(et, merged)
93
+ }
94
+ if (props.has("max_length")) {
95
+ val limit = props.num("max_length")
96
+ et.filters = if (limit != null) arrayOf<InputFilter>(InputFilter.LengthFilter(limit.toInt())) else arrayOf()
97
+ }
98
+ if (JsonUtil.truthy(props.value("auto_focus"))) et.requestFocus()
99
+ if (props.has("editable")) {
100
+ // Only present when False (read-only); removal restores editing.
101
+ val editable = props.value("editable") != false
102
+ et.isFocusable = editable
103
+ et.isFocusableInTouchMode = editable
104
+ et.isCursorVisible = editable
105
+ et.isLongClickable = editable
106
+ }
107
+ PNColor.parse(props.value("selection_color"))?.let { et.highlightColor = it }
108
+ props.str("text_content_type")?.let { applyAutofill(et, it) }
109
+ if (props.has("clear_button")) applyClearButton(et, JsonUtil.truthy(props.value("clear_button")))
110
+ props.str("return_key_type")?.let { et.imeOptions = imeAction(it) }
111
+ if (props.has("text_align")) {
112
+ et.gravity = when (props.str("text_align")) {
113
+ "center" -> android.view.Gravity.CENTER_HORIZONTAL or (et.gravity and android.view.Gravity.VERTICAL_GRAVITY_MASK)
114
+ "right", "end" -> android.view.Gravity.END or (et.gravity and android.view.Gravity.VERTICAL_GRAVITY_MASK)
115
+ else -> android.view.Gravity.START or (et.gravity and android.view.Gravity.VERTICAL_GRAVITY_MASK)
116
+ }
117
+ }
118
+ ViewStyler.apply(et, props)
119
+ }
120
+
121
+ private fun applyInputType(et: EditText, merged: JSONObject) {
122
+ var base = InputType.TYPE_CLASS_TEXT
123
+ val secure = JsonUtil.truthy(merged.value("secure")) || JsonUtil.truthy(merged.value("secure_text_entry"))
124
+ if (secure) {
125
+ base = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
126
+ } else {
127
+ when (merged.str("keyboard_type")) {
128
+ "email_address", "email" -> base = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
129
+ "number_pad", "numeric" -> base = InputType.TYPE_CLASS_NUMBER
130
+ "decimal_pad", "decimal" -> base = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
131
+ "phone_pad", "phone" -> base = InputType.TYPE_CLASS_PHONE
132
+ "url" -> base = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
133
+ }
134
+ when (merged.str("auto_capitalize")) {
135
+ "sentences" -> base = base or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
136
+ "words" -> base = base or InputType.TYPE_TEXT_FLAG_CAP_WORDS
137
+ "characters" -> base = base or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
138
+ }
139
+ if (merged.value("auto_correct") == false) base = base or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
140
+ }
141
+ if (JsonUtil.truthy(merged.value("multiline"))) {
142
+ base = base or InputType.TYPE_TEXT_FLAG_MULTI_LINE
143
+ et.setSingleLine(false)
144
+ } else {
145
+ et.setSingleLine(true)
146
+ }
147
+ et.inputType = base
148
+ }
149
+
150
+ private fun imeAction(type: String): Int = when (type) {
151
+ "default" -> EditorInfo.IME_ACTION_UNSPECIFIED
152
+ "go" -> EditorInfo.IME_ACTION_GO
153
+ "next" -> EditorInfo.IME_ACTION_NEXT
154
+ "search" -> EditorInfo.IME_ACTION_SEARCH
155
+ "send" -> EditorInfo.IME_ACTION_SEND
156
+ "previous" -> EditorInfo.IME_ACTION_PREVIOUS
157
+ else -> EditorInfo.IME_ACTION_DONE // done, google, join, route, yahoo
158
+ }
159
+
160
+ /**
161
+ * Single-line inputs always dismiss the keyboard on the action key and
162
+ * fire `on_submit` first. Multi-line inputs only consume the action
163
+ * when an `on_submit` handler exists; otherwise Enter inserts a newline.
164
+ */
165
+ private fun installEditorAction(et: EditText) {
166
+ et.setOnEditorActionListener { v, _, _ ->
167
+ val multiline = JsonUtil.truthy(propsOf(v).value("multiline"))
168
+ val hasSubmit = hasEvent(v, "on_submit")
169
+ if (multiline && !hasSubmit) return@setOnEditorActionListener false
170
+ if (hasSubmit) fire(v, "on_submit", v.text.toString())
171
+ if (!multiline && propsOf(v).value("blur_on_submit") != false) blur(v)
172
+ true
173
+ }
174
+ }
175
+
176
+ private fun applyAutofill(et: EditText, contentType: String) {
177
+ if (Build.VERSION.SDK_INT < 26) return
178
+ val hint = when (contentType) {
179
+ "username" -> View.AUTOFILL_HINT_USERNAME
180
+ "password" -> View.AUTOFILL_HINT_PASSWORD
181
+ "new_password" -> "newPassword"
182
+ "email", "email_address" -> View.AUTOFILL_HINT_EMAIL_ADDRESS
183
+ "name" -> View.AUTOFILL_HINT_NAME
184
+ "given_name" -> "personGivenName"
185
+ "family_name" -> "personFamilyName"
186
+ "telephone", "phone", "phone_number" -> View.AUTOFILL_HINT_PHONE
187
+ "postal_code" -> View.AUTOFILL_HINT_POSTAL_CODE
188
+ "street_address" -> View.AUTOFILL_HINT_POSTAL_ADDRESS
189
+ "credit_card_number" -> View.AUTOFILL_HINT_CREDIT_CARD_NUMBER
190
+ "one_time_code" -> "smsOTPCode"
191
+ else -> null
192
+ } ?: return
193
+ et.setAutofillHints(hint)
194
+ }
195
+
196
+ private fun applyClearButton(et: EditText, enabled: Boolean) {
197
+ val state = stateOf(et)
198
+ if (!enabled) {
199
+ et.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
200
+ return
201
+ }
202
+ et.setCompoundDrawablesWithIntrinsicBounds(0, 0, android.R.drawable.ic_menu_close_clear_cancel, 0)
203
+ if (state["clear_bound"] == true) return
204
+ state["clear_bound"] = true
205
+ et.setOnTouchListener { v, event ->
206
+ if (event.action == MotionEvent.ACTION_UP) {
207
+ val right = (v as EditText).compoundDrawables.getOrNull(2)
208
+ if (right != null) {
209
+ val threshold = v.width - v.paddingRight - right.bounds.width()
210
+ if (event.x >= threshold) {
211
+ v.setText("")
212
+ v.performClick()
213
+ return@setOnTouchListener true
214
+ }
215
+ }
216
+ }
217
+ false
218
+ }
219
+ }
220
+
221
+ override fun command(view: View, name: String, args: JSONObject): Any? {
222
+ val et = view as? EditText ?: return null
223
+ when (name) {
224
+ "focus" -> {
225
+ et.requestFocus()
226
+ imm(et)?.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT)
227
+ }
228
+ "blur" -> blur(et)
229
+ "clear" -> et.setText("")
230
+ "select_all" -> et.selectAll()
231
+ "set_selection" -> {
232
+ val start = JsonUtil.toInt(args.opt("start"))
233
+ val end = JsonUtil.toInt(args.opt("end"), start)
234
+ val len = et.text.length
235
+ et.setSelection(start.coerceIn(0, len), end.coerceIn(0, len))
236
+ }
237
+ "is_focused" -> return et.isFocused
238
+ "get_value" -> return et.text.toString()
239
+ }
240
+ return null
241
+ }
242
+
243
+ private fun blur(view: View) {
244
+ view.clearFocus()
245
+ imm(view)?.hideSoftInputFromWindow(view.windowToken, 0)
246
+ }
247
+
248
+ private fun imm(view: View): InputMethodManager? =
249
+ view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
250
+ }
@@ -0,0 +1,233 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.graphics.Paint
5
+ import android.graphics.Typeface
6
+ import android.text.SpannableStringBuilder
7
+ import android.text.Spanned
8
+ import android.text.TextUtils
9
+ import android.text.style.AbsoluteSizeSpan
10
+ import android.text.style.BackgroundColorSpan
11
+ import android.text.style.ForegroundColorSpan
12
+ import android.text.style.StrikethroughSpan
13
+ import android.text.style.StyleSpan
14
+ import android.text.style.TypefaceSpan
15
+ import android.text.style.UnderlineSpan
16
+ import android.view.Gravity
17
+ import android.view.View
18
+ import android.widget.TextView
19
+ import com.pythonnative.runtime.bridge.JsonUtil
20
+ import com.pythonnative.runtime.bridge.PNLog
21
+ import com.pythonnative.runtime.bridge.num
22
+ import com.pythonnative.runtime.bridge.str
23
+ import com.pythonnative.runtime.bridge.value
24
+ import org.json.JSONArray
25
+ import org.json.JSONObject
26
+ import kotlin.math.max
27
+
28
+ /** Text helpers shared by `Text`, `Button`, and `TextInput`. */
29
+ object TextStyle {
30
+ private val boldWords = setOf("bold", "semibold", "black", "heavy", "extrabold", "extra_bold", "semi_bold")
31
+
32
+ /** Whether `weight` (a name or a numeric weight) implies bold. */
33
+ fun isBold(weight: Any?): Boolean {
34
+ return when (weight) {
35
+ is String -> weight.lowercase() in boldWords || (weight.toIntOrNull()?.let { it >= 600 } ?: false)
36
+ is Number -> weight.toDouble() >= 600
37
+ else -> false
38
+ }
39
+ }
40
+
41
+ /** `Typeface` style constant for `weight` / `italic`. */
42
+ fun typefaceStyle(weight: Any?, italic: Boolean): Int {
43
+ val bold = isBold(weight)
44
+ return when {
45
+ bold && italic -> Typeface.BOLD_ITALIC
46
+ bold -> Typeface.BOLD
47
+ italic -> Typeface.ITALIC
48
+ else -> Typeface.NORMAL
49
+ }
50
+ }
51
+
52
+ /** Apply `text_transform` to `text`; `capitalize` upper-cases each word's first letter. */
53
+ fun transform(text: String?, mode: String?): String {
54
+ val s = text ?: ""
55
+ return when (mode) {
56
+ "uppercase" -> s.uppercase()
57
+ "lowercase" -> s.lowercase()
58
+ "capitalize" -> {
59
+ val sb = StringBuilder(s.length)
60
+ var atWordStart = true
61
+ for (ch in s) {
62
+ if (ch.isWhitespace()) {
63
+ atWordStart = true
64
+ sb.append(ch)
65
+ } else {
66
+ sb.append(if (atWordStart) ch.uppercaseChar() else ch)
67
+ atWordStart = false
68
+ }
69
+ }
70
+ sb.toString()
71
+ }
72
+ else -> s
73
+ }
74
+ }
75
+
76
+ /** Coerce a `shadow_offset` value (`{width, height}` or `[dx, dy]`) to `(dx, dy)`. */
77
+ fun shadowOffset(value: Any?): Pair<Double, Double> {
78
+ return when (value) {
79
+ is JSONObject -> Pair(value.num("width") ?: 0.0, value.num("height") ?: 0.0)
80
+ is JSONArray -> if (value.length() >= 2) Pair(JsonUtil.toDouble(value.opt(0)), JsonUtil.toDouble(value.opt(1))) else Pair(0.0, 0.0)
81
+ else -> Pair(0.0, 0.0)
82
+ }
83
+ }
84
+
85
+ /** Build a spannable from a rich-text span list, applying `transform` per span. */
86
+ fun buildSpannable(spans: JSONArray, transform: String?): SpannableStringBuilder {
87
+ val builder = SpannableStringBuilder()
88
+ for (i in 0 until spans.length()) {
89
+ val span = spans.optJSONObject(i) ?: continue
90
+ val text = transform(span.str("text"), transform)
91
+ if (text.isEmpty()) continue
92
+ val start = builder.length
93
+ builder.append(text)
94
+ val end = builder.length
95
+ fun set(obj: Any) = builder.setSpan(obj, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
96
+ try {
97
+ PNColor.parse(span.value("color"))?.let { set(ForegroundColorSpan(it)) }
98
+ PNColor.parse(span.value("background_color"))?.let { set(BackgroundColorSpan(it)) }
99
+ span.num("font_size")?.let { set(AbsoluteSizeSpan(it.toInt(), true)) }
100
+ var bold = JsonUtil.truthy(span.value("bold"))
101
+ val weight = span.value("font_weight")
102
+ if (!bold && weight != null) bold = isBold(weight)
103
+ val italic = JsonUtil.truthy(span.value("italic"))
104
+ if (bold || italic) set(StyleSpan(typefaceStyle(if (bold) "bold" else null, italic)))
105
+ span.str("font_family")?.takeIf { it.isNotEmpty() }?.let { set(TypefaceSpan(it)) }
106
+ when (span.str("text_decoration")) {
107
+ "underline" -> set(UnderlineSpan())
108
+ "line_through" -> set(StrikethroughSpan())
109
+ }
110
+ } catch (e: Exception) {
111
+ PNLog.swallowed("TextStyle.buildSpannable", e)
112
+ }
113
+ }
114
+ return builder
115
+ }
116
+
117
+ /** Apply font props (`font_family`, `font_weight`, `bold`, `italic`) from merged props. */
118
+ fun applyTypeface(tv: TextView, merged: JSONObject) {
119
+ val family = merged.str("font_family")
120
+ val weight = merged.value("font_weight") ?: if (JsonUtil.truthy(merged.value("bold"))) "bold" else null
121
+ val italic = JsonUtil.truthy(merged.value("italic"))
122
+ val style = typefaceStyle(weight, italic)
123
+ if (!family.isNullOrEmpty()) {
124
+ tv.setTypeface(Typeface.create(family, style))
125
+ } else {
126
+ tv.setTypeface(tv.typeface, style)
127
+ }
128
+ }
129
+ }
130
+
131
+ /** `Text` element: a `TextView` with rich spans, transforms, shadows, and line limits. */
132
+ class TextManager : ComponentManager() {
133
+ private val shadowKeys = listOf("text_shadow_color", "text_shadow_offset", "text_shadow_radius")
134
+
135
+ override fun createView(context: Context, tag: Long, props: JSONObject): View = TextView(context)
136
+
137
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
138
+ val tv = view as TextView
139
+ val merged = propsOf(tv)
140
+ if (props.has("spans") || props.has("text") || props.has("text_transform")) {
141
+ val transform = merged.str("text_transform")
142
+ val spans = merged.value("spans") as? JSONArray
143
+ if (spans != null && spans.length() > 0) {
144
+ try {
145
+ tv.text = TextStyle.buildSpannable(spans, transform)
146
+ } catch (e: Exception) {
147
+ tv.text = TextStyle.transform(merged.str("text"), transform)
148
+ }
149
+ } else {
150
+ tv.text = TextStyle.transform(merged.str("text"), transform)
151
+ }
152
+ }
153
+ props.num("font_size")?.let { tv.textSize = it.toFloat() }
154
+ PNColor.parse(props.value("color"))?.let { tv.setTextColor(it) }
155
+ if (listOf("font_family", "font_weight", "italic", "bold").any { props.has(it) }) {
156
+ try {
157
+ TextStyle.applyTypeface(tv, merged)
158
+ } catch (e: Exception) {
159
+ PNLog.swallowed("TextManager.typeface", e)
160
+ }
161
+ }
162
+ if (props.has("max_lines") || props.has("number_of_lines")) {
163
+ val lines = merged.num("number_of_lines") ?: merged.num("max_lines")
164
+ if (lines != null && lines > 0) {
165
+ tv.maxLines = lines.toInt()
166
+ tv.ellipsize = ellipsizeMode(merged.str("ellipsize_mode") ?: merged.str("ellipsize"))
167
+ } else {
168
+ tv.maxLines = Int.MAX_VALUE
169
+ tv.ellipsize = null
170
+ }
171
+ }
172
+ if (props.has("ellipsize_mode") || props.has("ellipsize")) {
173
+ if (tv.maxLines != Int.MAX_VALUE) tv.ellipsize = ellipsizeMode(merged.str("ellipsize_mode") ?: merged.str("ellipsize"))
174
+ }
175
+ if (props.has("selectable")) tv.setTextIsSelectable(JsonUtil.truthy(props.value("selectable")))
176
+ if (props.has("text_align")) {
177
+ tv.gravity = when (props.str("text_align")) {
178
+ "center" -> Gravity.CENTER
179
+ "right", "end" -> Gravity.END
180
+ "justify" -> Gravity.START
181
+ else -> Gravity.START
182
+ }
183
+ }
184
+ props.num("letter_spacing")?.let {
185
+ // Android takes letter spacing in ems (a ratio of the font size).
186
+ val size = merged.num("font_size") ?: 16.0
187
+ tv.letterSpacing = (it / max(size, 1.0)).toFloat()
188
+ }
189
+ props.num("line_height")?.let {
190
+ val size = merged.num("font_size") ?: 16.0
191
+ tv.setLineSpacing(0f, (it / max(size, 1.0)).toFloat())
192
+ }
193
+ if (props.has("text_decoration")) {
194
+ var flags = tv.paintFlags and Paint.UNDERLINE_TEXT_FLAG.inv() and Paint.STRIKE_THRU_TEXT_FLAG.inv()
195
+ when (props.str("text_decoration")) {
196
+ "underline" -> flags = flags or Paint.UNDERLINE_TEXT_FLAG
197
+ "line_through" -> flags = flags or Paint.STRIKE_THRU_TEXT_FLAG
198
+ }
199
+ tv.paintFlags = flags
200
+ }
201
+ if (shadowKeys.any { props.has(it) }) applyTextShadow(tv, merged)
202
+ ViewStyler.apply(tv, props)
203
+ }
204
+
205
+ private fun ellipsizeMode(mode: String?): TextUtils.TruncateAt = when (mode) {
206
+ "head" -> TextUtils.TruncateAt.START
207
+ "middle" -> TextUtils.TruncateAt.MIDDLE
208
+ "clip" -> TextUtils.TruncateAt.END
209
+ "marquee" -> TextUtils.TruncateAt.MARQUEE
210
+ else -> TextUtils.TruncateAt.END
211
+ }
212
+
213
+ private fun applyTextShadow(tv: TextView, merged: JSONObject) {
214
+ if (shadowKeys.none { merged.value(it) != null }) {
215
+ tv.setShadowLayer(0f, 0f, 0f, 0)
216
+ return
217
+ }
218
+ val argb = PNColor.parseOr(merged.value("text_shadow_color") ?: "#000000", 0xFF000000.toInt())
219
+ val (dx, dy) = TextStyle.shadowOffset(merged.value("text_shadow_offset"))
220
+ var radiusPx = merged.num("text_shadow_radius")?.let { pxF(it) } ?: 0f
221
+ // A zero radius with an offset hides the shadow on some renderers; use a hairline blur.
222
+ if (radiusPx <= 0f && (dx != 0.0 || dy != 0.0)) radiusPx = 0.01f
223
+ tv.setShadowLayer(radiusPx, pxF(dx), pxF(dy), argb)
224
+ }
225
+
226
+ override fun setAnimatedProperty(view: View, prop: String, value: Any?) {
227
+ if (prop == "color" && view is TextView) {
228
+ PNColor.parse(value)?.let { view.setTextColor(it) }
229
+ return
230
+ }
231
+ super.setAnimatedProperty(view, prop, value)
232
+ }
233
+ }