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,106 @@
1
+ import UIKit
2
+
3
+ /// `TabBar`: a native `UITabBar`. Height is 49 pt plus the bottom safe
4
+ /// area so the bar reaches the home indicator (the screen root extends
5
+ /// past the bottom inset for this reason).
6
+ public final class PNTabBarManager: PNComponentManager {
7
+ public static let baseHeight: CGFloat = 49
8
+
9
+ /// The intrinsic bar height for the current bottom safe-area inset.
10
+ public static func height(for view: UIView?) -> CGFloat {
11
+ let bottom = view?.window?.safeAreaInsets.bottom ?? PNWindow.keyWindow()?.safeAreaInsets.bottom ?? 0
12
+ return baseHeight + bottom
13
+ }
14
+
15
+ public override func makeView(props: [String: Any]) -> UIView {
16
+ UITabBar(frame: CGRect(x: 0, y: 0, width: 0, height: PNTabBarManager.height(for: nil)))
17
+ }
18
+
19
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
20
+ let view = super.createView(tag: tag, props: props)
21
+ if let bar = view as? UITabBar {
22
+ let delegate = PNTabBarDelegate()
23
+ bar.delegate = delegate
24
+ PNViewState.existing(for: bar)?.retained.append(delegate)
25
+ }
26
+ return view
27
+ }
28
+
29
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
30
+ let w = maxW.isFinite && maxW < 1e6 ? maxW : 320
31
+ return CGSize(width: w, height: PNTabBarManager.height(for: view))
32
+ }
33
+
34
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
35
+ guard let bar = view as? UITabBar else { return }
36
+ let merged = mergedProps(bar)
37
+ let items = PNTabBarManager.items(merged)
38
+ if PNProps.has(props, "items") {
39
+ bar.setItems(items.enumerated().map { index, item in
40
+ let title = PNProps.string(item["title"]) ?? PNProps.string(item["name"]) ?? ""
41
+ let icon = PNTabBarManager.icon(item["icon"])
42
+ let barItem = UITabBarItem(title: title, image: icon, tag: index)
43
+ if let badge = PNProps.string(PNProps.value(item, "badge")) { barItem.badgeValue = badge }
44
+ return barItem
45
+ }, animated: false)
46
+ }
47
+ if PNProps.has(props, "active_tab") || PNProps.has(props, "active_index") || PNProps.has(props, "items") {
48
+ let activeName = PNProps.string(PNProps.value(merged, "active_tab"))
49
+ var index = PNProps.int(PNProps.value(merged, "active_index"))
50
+ if index == nil, let activeName = activeName {
51
+ index = items.firstIndex { PNProps.string($0["name"]) == activeName }
52
+ }
53
+ if let index = index, let barItems = bar.items, index >= 0, index < barItems.count {
54
+ bar.selectedItem = barItems[index]
55
+ }
56
+ }
57
+ if let color = PNColor.parse(PNProps.value(props, "active_color") ?? PNProps.value(props, "tint_color")) {
58
+ bar.tintColor = color
59
+ }
60
+ if let color = PNColor.parse(PNProps.value(props, "inactive_color")) {
61
+ bar.unselectedItemTintColor = color
62
+ }
63
+ if let color = PNColor.parse(PNProps.value(props, "background_color")) {
64
+ bar.barTintColor = color
65
+ bar.backgroundColor = color
66
+ let appearance = UITabBarAppearance()
67
+ appearance.configureWithOpaqueBackground()
68
+ appearance.backgroundColor = color
69
+ bar.standardAppearance = appearance
70
+ if #available(iOS 15.0, *) {
71
+ bar.scrollEdgeAppearance = appearance
72
+ }
73
+ }
74
+ if PNProps.has(props, "translucent") {
75
+ bar.isTranslucent = PNProps.bool(PNProps.value(props, "translucent")) ?? true
76
+ }
77
+ PNViewStyler.applyAccessibility(bar, props)
78
+ }
79
+
80
+ static func items(_ props: [String: Any]) -> [[String: Any]] {
81
+ ((PNProps.value(props, "items") as? [Any]) ?? []).compactMap { $0 as? [String: Any] }
82
+ }
83
+
84
+ /// Resolve an icon spec (SF Symbol name or `{"ios": name}`) to an image.
85
+ static func icon(_ spec: Any?) -> UIImage? {
86
+ var name: String?
87
+ if let text = spec as? String {
88
+ name = text
89
+ } else if let dict = spec as? [String: Any] {
90
+ name = PNProps.string(dict["ios"])
91
+ }
92
+ guard let name = name, !name.isEmpty else { return nil }
93
+ return UIImage(systemName: name) ?? UIImage(named: name)
94
+ }
95
+ }
96
+
97
+ /// Forwards `tabBar(_:didSelect:)` as `on_tab_select(name)` (plus `on_select(index)`).
98
+ final class PNTabBarDelegate: NSObject, UITabBarDelegate {
99
+ func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
100
+ let index = item.tag
101
+ let items = PNTabBarManager.items(PNViewState.existing(for: tabBar)?.props ?? [:])
102
+ guard index >= 0, index < items.count else { return }
103
+ PNEvents.emit(tabBar, "on_tab_select", [PNProps.string(items[index]["name"]) ?? ""])
104
+ PNEvents.emitIfWired(tabBar, "on_select", [index])
105
+ }
106
+ }
@@ -0,0 +1,297 @@
1
+ import UIKit
2
+
3
+ /// `TextInput`: a single-line `UITextField` or multiline `UITextView`.
4
+ ///
5
+ /// The class is chosen at creation from the `multiline` prop. Programmatic
6
+ /// `value` updates set a suppress flag so they don't echo into `on_change`.
7
+ public final class PNTextInputManager: PNComponentManager {
8
+ public override func makeView(props: [String: Any]) -> UIView {
9
+ if PNProps.bool(PNProps.value(props, "multiline")) == true {
10
+ let view = UITextView(frame: .zero)
11
+ view.font = UIFont.systemFont(ofSize: 17)
12
+ view.backgroundColor = .white
13
+ return view
14
+ }
15
+ let field = UITextField(frame: .zero)
16
+ field.borderStyle = .roundedRect
17
+ return field
18
+ }
19
+
20
+ public override func createView(tag: Int64, props: [String: Any]) -> UIView {
21
+ let view = super.createView(tag: tag, props: props)
22
+ let delegate = PNTextInputDelegate(view: view)
23
+ PNViewState.existing(for: view)?.retained.append(delegate)
24
+ if let field = view as? UITextField {
25
+ field.delegate = delegate
26
+ field.addTarget(delegate, action: #selector(PNTextInputDelegate.editingChanged(_:)), for: .editingChanged)
27
+ field.addTarget(delegate, action: #selector(PNTextInputDelegate.editingDidEndOnExit(_:)), for: .editingDidEndOnExit)
28
+ } else if let textView = view as? UITextView {
29
+ textView.delegate = delegate
30
+ }
31
+ return view
32
+ }
33
+
34
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
35
+ guard let state = PNViewState.existing(for: view) else { return }
36
+ let field = view as? UITextField
37
+ let textView = view as? UITextView
38
+ if PNProps.has(props, "max_length") {
39
+ state.extras["max_length"] = PNProps.int(PNProps.value(props, "max_length")) as Any? ?? NSNull()
40
+ }
41
+ if let value = PNProps.string(PNProps.value(props, "value")) {
42
+ let current = field?.text ?? textView?.text ?? ""
43
+ let acknowledged = PNProps.int(props["_pn_edit_revision"]) ?? 0
44
+ let edited = state.extras["edit_revision"] as? Int ?? 0
45
+ if current != value && acknowledged >= edited && (view as? UITextInput)?.markedTextRange == nil {
46
+ PNTextInputManager.setText(view, value)
47
+ }
48
+ }
49
+ if let field = field {
50
+ if PNProps.has(props, "placeholder") {
51
+ field.placeholder = PNProps.string(PNProps.value(props, "placeholder")) ?? ""
52
+ }
53
+ if let color = PNColor.parse(PNProps.value(props, "placeholder_color")) {
54
+ let placeholder = PNProps.string(PNProps.value(state.props, "placeholder")) ?? ""
55
+ field.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [.foregroundColor: color])
56
+ }
57
+ if PNProps.has(props, "clear_button") {
58
+ field.clearButtonMode = PNProps.bool(PNProps.value(props, "clear_button")) == true ? .whileEditing : .never
59
+ }
60
+ }
61
+ if let size = PNProps.double(PNProps.value(props, "font_size")) {
62
+ let font = UIFont.systemFont(ofSize: CGFloat(size))
63
+ field?.font = font
64
+ textView?.font = font
65
+ }
66
+ if let color = PNColor.parse(PNProps.value(props, "color")) {
67
+ field?.textColor = color
68
+ textView?.textColor = color
69
+ }
70
+ if let color = PNColor.parse(PNProps.value(props, "background_color")) {
71
+ view.backgroundColor = color
72
+ }
73
+ if PNProps.has(props, "secure") {
74
+ let secure = PNProps.bool(PNProps.value(props, "secure")) ?? false
75
+ field?.isSecureTextEntry = secure
76
+ textView?.isSecureTextEntry = secure
77
+ }
78
+ if let keyboard = PNProps.string(PNProps.value(props, "keyboard_type")) {
79
+ let type = PNTextInputManager.keyboardType(keyboard)
80
+ field?.keyboardType = type
81
+ textView?.keyboardType = type
82
+ }
83
+ if let cap = PNProps.string(PNProps.value(props, "auto_capitalize")) {
84
+ let type = PNTextInputManager.capitalization(cap)
85
+ field?.autocapitalizationType = type
86
+ textView?.autocapitalizationType = type
87
+ }
88
+ if PNProps.has(props, "auto_correct") {
89
+ let type: UITextAutocorrectionType = PNProps.bool(PNProps.value(props, "auto_correct")) == true ? .yes : .no
90
+ field?.autocorrectionType = type
91
+ textView?.autocorrectionType = type
92
+ }
93
+ if let key = PNProps.string(PNProps.value(props, "return_key_type")) {
94
+ let type = PNTextInputManager.returnKey(key)
95
+ field?.returnKeyType = type
96
+ textView?.returnKeyType = type
97
+ }
98
+ if let color = PNColor.parse(PNProps.value(props, "selection_color")) {
99
+ view.tintColor = color
100
+ }
101
+ if PNProps.has(props, "text_content_type") {
102
+ let type = PNProps.string(PNProps.value(props, "text_content_type")).flatMap(PNTextInputManager.contentType)
103
+ field?.textContentType = type
104
+ textView?.textContentType = type
105
+ }
106
+ if PNProps.has(props, "editable") {
107
+ let editable = PNProps.bool(PNProps.value(props, "editable")) ?? true
108
+ field?.isEnabled = editable
109
+ textView?.isEditable = editable
110
+ }
111
+ if PNProps.bool(PNProps.value(props, "auto_focus")) == true {
112
+ view.becomeFirstResponder()
113
+ }
114
+ PNViewStyler.applyDecoration(view, props)
115
+ }
116
+
117
+ public override func measure(view: UIView, maxW: CGFloat, maxH: CGFloat) -> CGSize {
118
+ let size = super.measure(view: view, maxW: maxW, maxH: maxH)
119
+ return CGSize(width: max(size.width, 100), height: max(size.height, 36))
120
+ }
121
+
122
+ public override func command(view: UIView, name: String, args: [String: Any]) -> Any? {
123
+ switch name {
124
+ case "focus":
125
+ view.becomeFirstResponder()
126
+ case "blur":
127
+ view.resignFirstResponder()
128
+ case "clear":
129
+ PNTextInputManager.setText(view, "")
130
+ case "get_value":
131
+ return (view as? UITextField)?.text ?? (view as? UITextView)?.text ?? ""
132
+ case "set_selection":
133
+ if let start = PNProps.int(args["start"]) {
134
+ let end = PNProps.int(args["end"]) ?? start
135
+ PNTextInputManager.setSelection(view, start: start, end: end)
136
+ }
137
+ default:
138
+ break
139
+ }
140
+ return nil
141
+ }
142
+
143
+ // MARK: - Helpers
144
+
145
+ static func setText(_ view: UIView, _ text: String) {
146
+ guard let state = PNViewState.existing(for: view) else { return }
147
+ let input = view as? UITextInput
148
+ let selection = input?.selectedTextRange
149
+ let start = selection.flatMap { range in input.map { $0.offset(from: $0.beginningOfDocument, to: range.start) } } ?? 0
150
+ let end = selection.flatMap { range in input.map { $0.offset(from: $0.beginningOfDocument, to: range.end) } } ?? start
151
+ state.extras["suppress"] = true
152
+ (view as? UITextField)?.text = text
153
+ (view as? UITextView)?.text = text
154
+ setSelection(view, start: min(start, text.utf16.count), end: min(end, text.utf16.count))
155
+ state.extras["suppress"] = false
156
+ }
157
+
158
+ static func setSelection(_ view: UIView, start: Int, end: Int) {
159
+ guard let input = view as? UITextInput else { return }
160
+ let begin = input.beginningOfDocument
161
+ guard let from = input.position(from: begin, offset: start),
162
+ let to = input.position(from: begin, offset: end)
163
+ else { return }
164
+ input.selectedTextRange = input.textRange(from: from, to: to)
165
+ }
166
+
167
+ static func keyboardType(_ name: String) -> UIKeyboardType {
168
+ switch name {
169
+ case "ascii": return .asciiCapable
170
+ case "numbers_and_punctuation": return .numbersAndPunctuation
171
+ case "url": return .URL
172
+ case "number_pad", "numeric": return .numberPad
173
+ case "phone_pad": return .phonePad
174
+ case "email_address", "email": return .emailAddress
175
+ case "decimal_pad", "decimal": return .decimalPad
176
+ case "web_search": return .webSearch
177
+ default: return .default
178
+ }
179
+ }
180
+
181
+ static func capitalization(_ name: String) -> UITextAutocapitalizationType {
182
+ switch name {
183
+ case "none": return .none
184
+ case "words": return .words
185
+ case "characters": return .allCharacters
186
+ default: return .sentences
187
+ }
188
+ }
189
+
190
+ static func returnKey(_ name: String) -> UIReturnKeyType {
191
+ switch name {
192
+ case "go": return .go
193
+ case "google": return .google
194
+ case "join": return .join
195
+ case "next": return .next
196
+ case "route": return .route
197
+ case "search": return .search
198
+ case "send": return .send
199
+ case "yahoo": return .yahoo
200
+ case "done": return .done
201
+ default: return .default
202
+ }
203
+ }
204
+
205
+ static func contentType(_ name: String) -> UITextContentType? {
206
+ switch name.trimmingCharacters(in: .whitespaces).lowercased() {
207
+ case "username": return .username
208
+ case "password": return .password
209
+ case "new_password": return .newPassword
210
+ case "one_time_code": return .oneTimeCode
211
+ case "email", "email_address": return .emailAddress
212
+ case "name": return .name
213
+ case "url": return .URL
214
+ case "telephone", "telephone_number", "phone", "phone_number": return .telephoneNumber
215
+ default: return nil
216
+ }
217
+ }
218
+ }
219
+
220
+ /// Delegate and control target for both input classes.
221
+ final class PNTextInputDelegate: NSObject, UITextFieldDelegate, UITextViewDelegate {
222
+ private weak var view: UIView?
223
+
224
+ init(view: UIView) {
225
+ self.view = view
226
+ }
227
+
228
+ private func currentText() -> String {
229
+ (view as? UITextField)?.text ?? (view as? UITextView)?.text ?? ""
230
+ }
231
+
232
+ private func emitChange() {
233
+ guard let view = view, let state = PNViewState.existing(for: view), !state.flag("suppress") else { return }
234
+ var text = currentText()
235
+ if let maxLength = state.extras["max_length"] as? Int, maxLength >= 0, text.count > maxLength {
236
+ text = String(text.prefix(maxLength))
237
+ PNTextInputManager.setText(view, text)
238
+ }
239
+ PNEvents.emit(view, "on_change", [text])
240
+ PNLayout.invalidate(state.tag)
241
+ }
242
+
243
+ private func emitSelection() {
244
+ guard let view = view, let state = PNViewState.existing(for: view), state.hasEvent("on_selection_change"),
245
+ let input = view as? UITextInput, let range = input.selectedTextRange
246
+ else { return }
247
+ let start = input.offset(from: input.beginningOfDocument, to: range.start)
248
+ let end = input.offset(from: input.beginningOfDocument, to: range.end)
249
+ PNEvents.emit(view, "on_selection_change", [["start": start, "end": end]])
250
+ }
251
+
252
+ // MARK: UITextField
253
+
254
+ @objc func editingChanged(_ sender: Any?) {
255
+ emitChange()
256
+ }
257
+
258
+ @objc func editingDidEndOnExit(_ sender: Any?) {
259
+ guard let view = view else { return }
260
+ PNEvents.emit(view, "on_submit", [currentText()])
261
+ }
262
+
263
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
264
+ textField.resignFirstResponder()
265
+ return true
266
+ }
267
+
268
+ func textFieldDidBeginEditing(_ textField: UITextField) {
269
+ if let view = view { PNEvents.emit(view, "on_focus") }
270
+ }
271
+
272
+ func textFieldDidEndEditing(_ textField: UITextField) {
273
+ if let view = view { PNEvents.emit(view, "on_blur") }
274
+ }
275
+
276
+ func textFieldDidChangeSelection(_ textField: UITextField) {
277
+ emitSelection()
278
+ }
279
+
280
+ // MARK: UITextView
281
+
282
+ func textViewDidChange(_ textView: UITextView) {
283
+ emitChange()
284
+ }
285
+
286
+ func textViewDidBeginEditing(_ textView: UITextView) {
287
+ if let view = view { PNEvents.emit(view, "on_focus") }
288
+ }
289
+
290
+ func textViewDidEndEditing(_ textView: UITextView) {
291
+ if let view = view { PNEvents.emit(view, "on_blur") }
292
+ }
293
+
294
+ func textViewDidChangeSelection(_ textView: UITextView) {
295
+ emitSelection()
296
+ }
297
+ }
@@ -0,0 +1,230 @@
1
+ import UIKit
2
+
3
+ /// `Text`: a `UILabel` with rich spans, text transforms, shadows, and the
4
+ /// full font prop set.
5
+ public final class PNTextManager: PNComponentManager {
6
+ static let textShadowKeys = ["text_shadow_color", "text_shadow_offset", "text_shadow_radius"]
7
+ static let attributedKeys = ["letter_spacing", "line_height", "text_decoration"] + textShadowKeys
8
+ static let fontKeys = ["font_size", "font_weight", "font_family", "italic", "bold", "font_style"]
9
+ static let spanRebuildKeys = ["spans", "text", "text_transform", "color"] + fontKeys + attributedKeys
10
+
11
+ public override func makeView(props: [String: Any]) -> UIView {
12
+ let label = UILabel(frame: .zero)
13
+ label.numberOfLines = 0
14
+ label.translatesAutoresizingMaskIntoConstraints = true
15
+ return label
16
+ }
17
+
18
+ public override func apply(view: UIView, props: [String: Any], initial: Bool) {
19
+ guard let label = view as? UILabel else { return }
20
+ let merged = mergedProps(label)
21
+ let hasSpans = !((PNProps.value(merged, "spans") as? [Any]) ?? []).isEmpty
22
+ let textChanged = PNProps.has(props, "text") || PNProps.has(props, "text_transform")
23
+ if textChanged, !hasSpans {
24
+ label.text = PNTextManager.transform(PNProps.string(PNProps.value(merged, "text")), mode: PNProps.string(PNProps.value(merged, "text_transform")))
25
+ }
26
+ if PNTextManager.fontKeys.contains(where: { PNProps.has(props, $0) }) {
27
+ label.font = PNTextManager.font(from: merged, base: label.font)
28
+ }
29
+ if let color = PNColor.parse(PNProps.value(props, "color")) {
30
+ label.textColor = color
31
+ }
32
+ if let color = PNColor.parse(PNProps.value(props, "background_color")) {
33
+ label.backgroundColor = color
34
+ }
35
+ if PNProps.has(props, "max_lines") || PNProps.has(props, "number_of_lines") {
36
+ let lines = PNProps.int(PNProps.value(props, "max_lines")) ?? PNProps.int(PNProps.value(props, "number_of_lines")) ?? 0
37
+ label.numberOfLines = max(0, lines)
38
+ }
39
+ if PNProps.has(props, "text_align") {
40
+ label.textAlignment = PNTextManager.alignment(PNProps.string(PNProps.value(props, "text_align")))
41
+ }
42
+ if PNProps.has(props, "selectable") {
43
+ label.isUserInteractionEnabled = PNProps.bool(PNProps.value(props, "selectable")) ?? false
44
+ }
45
+ if hasSpans {
46
+ if PNTextManager.spanRebuildKeys.contains(where: { PNProps.has(props, $0) }) {
47
+ applySpans(label, merged)
48
+ }
49
+ } else if PNProps.has(props, "spans") {
50
+ label.text = PNTextManager.transform(PNProps.string(PNProps.value(merged, "text")), mode: PNProps.string(PNProps.value(merged, "text_transform")))
51
+ } else if PNTextManager.attributedKeys.contains(where: { PNProps.has(props, $0) })
52
+ || (textChanged && PNTextManager.attributedKeys.contains { PNProps.value(merged, $0) != nil })
53
+ {
54
+ applyAttributed(label, merged)
55
+ }
56
+ PNViewStyler.applyDecoration(label, props)
57
+ }
58
+
59
+ // MARK: - Fonts
60
+
61
+ /// Resolve a `UIFont` from the element's font props.
62
+ static func font(from props: [String: Any], base: UIFont?) -> UIFont {
63
+ let currentSize = base?.pointSize ?? 17
64
+ let size = CGFloat(PNProps.double(PNProps.value(props, "font_size")) ?? Double(currentSize))
65
+ var weight: Any? = PNProps.value(props, "font_weight")
66
+ if weight == nil, PNProps.bool(PNProps.value(props, "bold")) == true { weight = "bold" }
67
+ let italic = PNProps.bool(PNProps.value(props, "italic")) == true
68
+ || PNProps.string(PNProps.value(props, "font_style")) == "italic"
69
+ return font(size: size, weight: weight, family: PNProps.string(PNProps.value(props, "font_family")), italic: italic)
70
+ }
71
+
72
+ static func font(size: CGFloat, weight: Any?, family: String?, italic: Bool) -> UIFont {
73
+ if let family = family, !family.isEmpty, let named = UIFont(name: family, size: size) {
74
+ return italic ? italicized(named, size: size) : named
75
+ }
76
+ let font = UIFont.systemFont(ofSize: size, weight: fontWeight(weight))
77
+ return italic ? italicized(font, size: size) : font
78
+ }
79
+
80
+ static func italicized(_ font: UIFont, size: CGFloat) -> UIFont {
81
+ var traits = font.fontDescriptor.symbolicTraits
82
+ traits.insert(.traitItalic)
83
+ guard let descriptor = font.fontDescriptor.withSymbolicTraits(traits) else { return font }
84
+ return UIFont(descriptor: descriptor, size: size)
85
+ }
86
+
87
+ static func fontWeight(_ value: Any?) -> UIFont.Weight {
88
+ if let name = value as? String {
89
+ switch name.lowercased() {
90
+ case "ultralight": return .ultraLight
91
+ case "thin": return .thin
92
+ case "light": return .light
93
+ case "medium": return .medium
94
+ case "semibold": return .semibold
95
+ case "bold": return .bold
96
+ case "heavy": return .heavy
97
+ case "black": return .black
98
+ default:
99
+ if let numeric = Double(name) { return fontWeight(numeric) }
100
+ return .regular
101
+ }
102
+ }
103
+ guard let numeric = PNProps.double(value) else { return .regular }
104
+ let n = max(100, min(900, numeric))
105
+ switch n {
106
+ case ...100: return .ultraLight
107
+ case ...200: return .thin
108
+ case ...300: return .light
109
+ case ...400: return .regular
110
+ case ...500: return .medium
111
+ case ...600: return .semibold
112
+ case ...700: return .bold
113
+ case ...800: return .heavy
114
+ default: return .black
115
+ }
116
+ }
117
+
118
+ static func alignment(_ value: String?) -> NSTextAlignment {
119
+ switch value {
120
+ case "center": return .center
121
+ case "right": return .right
122
+ case "justify": return .justified
123
+ case "natural": return .natural
124
+ default: return .left
125
+ }
126
+ }
127
+
128
+ // MARK: - Transforms
129
+
130
+ /// Apply a `text_transform` mode (`uppercase`, `lowercase`, `capitalize`).
131
+ public static func transform(_ text: String?, mode: String?) -> String {
132
+ let s = text ?? ""
133
+ switch mode {
134
+ case "uppercase": return s.uppercased()
135
+ case "lowercase": return s.lowercased()
136
+ case "capitalize":
137
+ var out = ""
138
+ var atWordStart = true
139
+ for ch in s {
140
+ if ch.isWhitespace {
141
+ atWordStart = true
142
+ out.append(ch)
143
+ } else if atWordStart {
144
+ out.append(contentsOf: String(ch).uppercased())
145
+ atWordStart = false
146
+ } else {
147
+ out.append(ch)
148
+ }
149
+ }
150
+ return out
151
+ default: return s
152
+ }
153
+ }
154
+
155
+ // MARK: - Attributed rendering
156
+
157
+ static func baseAttributes(_ props: [String: Any], font: UIFont?) -> [NSAttributedString.Key: Any] {
158
+ var attrs: [NSAttributedString.Key: Any] = [:]
159
+ if let font = font { attrs[.font] = font }
160
+ if let kern = PNProps.double(PNProps.value(props, "letter_spacing")) { attrs[.kern] = kern }
161
+ if let lineHeight = PNProps.double(PNProps.value(props, "line_height")) {
162
+ let style = NSMutableParagraphStyle()
163
+ style.minimumLineHeight = CGFloat(lineHeight)
164
+ style.maximumLineHeight = CGFloat(lineHeight)
165
+ attrs[.paragraphStyle] = style
166
+ }
167
+ switch PNProps.string(PNProps.value(props, "text_decoration")) {
168
+ case "underline": attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
169
+ case "line_through": attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
170
+ default: break
171
+ }
172
+ if let shadow = textShadow(props) { attrs[.shadow] = shadow }
173
+ return attrs
174
+ }
175
+
176
+ static func textShadow(_ props: [String: Any]) -> NSShadow? {
177
+ guard textShadowKeys.contains(where: { PNProps.value(props, $0) != nil }) else { return nil }
178
+ let shadow = NSShadow()
179
+ shadow.shadowColor = PNColor.parse(PNProps.value(props, "text_shadow_color")) ?? UIColor.black
180
+ let (dx, dy) = PNViewStyler.shadowOffset(PNProps.value(props, "text_shadow_offset"))
181
+ shadow.shadowOffset = CGSize(width: dx, height: dy)
182
+ shadow.shadowBlurRadius = CGFloat(PNProps.double(PNProps.value(props, "text_shadow_radius")) ?? 0)
183
+ return shadow
184
+ }
185
+
186
+ func applyAttributed(_ label: UILabel, _ props: [String: Any]) {
187
+ guard let text = label.text, !text.isEmpty else { return }
188
+ label.attributedText = NSAttributedString(string: text, attributes: PNTextManager.baseAttributes(props, font: label.font))
189
+ }
190
+
191
+ func applySpans(_ label: UILabel, _ merged: [String: Any]) {
192
+ let mode = PNProps.string(PNProps.value(merged, "text_transform"))
193
+ let spans = ((PNProps.value(merged, "spans") as? [Any]) ?? []).compactMap { $0 as? [String: Any] }
194
+ let texts = spans.map { PNTextManager.transform(PNProps.string($0["text"]), mode: mode) }
195
+ let full = NSMutableAttributedString(string: texts.joined(), attributes: PNTextManager.baseAttributes(merged, font: label.font))
196
+ let baseSize = label.font?.pointSize ?? 17
197
+ var location = 0
198
+ for (span, text) in zip(spans, texts) {
199
+ let length = (text as NSString).length
200
+ let range = NSRange(location: location, length: length)
201
+ location += length
202
+ if length == 0 { continue }
203
+ if PNTextManager.fontKeys.contains(where: { PNProps.value(span, $0) != nil }) {
204
+ let size = CGFloat(PNProps.double(PNProps.value(span, "font_size")) ?? Double(baseSize))
205
+ var weight: Any? = PNProps.value(span, "font_weight")
206
+ if weight == nil, PNProps.bool(PNProps.value(span, "bold")) == true { weight = "bold" }
207
+ let font = PNTextManager.font(
208
+ size: size, weight: weight, family: PNProps.string(PNProps.value(span, "font_family")),
209
+ italic: PNProps.bool(PNProps.value(span, "italic")) == true
210
+ )
211
+ full.addAttribute(.font, value: font, range: range)
212
+ }
213
+ if let color = PNColor.parse(PNProps.value(span, "color")) {
214
+ full.addAttribute(.foregroundColor, value: color, range: range)
215
+ }
216
+ if let color = PNColor.parse(PNProps.value(span, "background_color")) {
217
+ full.addAttribute(.backgroundColor, value: color, range: range)
218
+ }
219
+ if let kern = PNProps.double(PNProps.value(span, "letter_spacing")) {
220
+ full.addAttribute(.kern, value: kern, range: range)
221
+ }
222
+ switch PNProps.string(PNProps.value(span, "text_decoration")) {
223
+ case "underline": full.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
224
+ case "line_through": full.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: range)
225
+ default: break
226
+ }
227
+ }
228
+ label.attributedText = full
229
+ }
230
+ }