@nativescript/core 9.1.0-alpha.8 → 9.1.0-dev.0

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 (403) hide show
  1. package/animation-frame/animation-native.windows.d.ts +1 -0
  2. package/animation-frame/animation-native.windows.js +4 -0
  3. package/animation-frame/animation-native.windows.js.map +1 -0
  4. package/application/application-common.d.ts +2 -1
  5. package/application/application-common.js +4 -0
  6. package/application/application-common.js.map +1 -1
  7. package/application/application.android.d.ts +0 -16
  8. package/application/application.android.js +1 -6
  9. package/application/application.android.js.map +1 -1
  10. package/application/application.d.ts +9 -0
  11. package/application/application.ios.d.ts +1 -2
  12. package/application/application.ios.js +3 -50
  13. package/application/application.ios.js.map +1 -1
  14. package/application/application.windows.d.ts +27 -0
  15. package/application/application.windows.js +331 -0
  16. package/application/application.windows.js.map +1 -0
  17. package/application/helpers.windows.d.ts +8 -0
  18. package/application/helpers.windows.js +12 -0
  19. package/application/helpers.windows.js.map +1 -0
  20. package/application/index.windows.d.ts +2 -0
  21. package/application/index.windows.js +3 -0
  22. package/application/index.windows.js.map +1 -0
  23. package/application/window-helper.windows.d.ts +29 -0
  24. package/application/window-helper.windows.js +159 -0
  25. package/application/window-helper.windows.js.map +1 -0
  26. package/application-settings/index.windows.d.ts +11 -0
  27. package/application-settings/index.windows.js +93 -0
  28. package/application-settings/index.windows.js.map +1 -0
  29. package/color/index.d.ts +5 -0
  30. package/color/index.windows.d.ts +8 -0
  31. package/color/index.windows.js +19 -0
  32. package/color/index.windows.js.map +1 -0
  33. package/connectivity/index.windows.d.ts +11 -0
  34. package/connectivity/index.windows.js +50 -0
  35. package/connectivity/index.windows.js.map +1 -0
  36. package/debugger/webinspector-network.windows.d.ts +79 -0
  37. package/debugger/webinspector-network.windows.js +307 -0
  38. package/debugger/webinspector-network.windows.js.map +1 -0
  39. package/file-system/file-system-access.windows.d.ts +76 -0
  40. package/file-system/file-system-access.windows.js +476 -0
  41. package/file-system/file-system-access.windows.js.map +1 -0
  42. package/fps-meter/fps-native.windows.d.ts +8 -0
  43. package/fps-meter/fps-native.windows.js +40 -0
  44. package/fps-meter/fps-native.windows.js.map +1 -0
  45. package/global-types.d.ts +1 -0
  46. package/globals/index.js +1 -4
  47. package/globals/index.js.map +1 -1
  48. package/http/http-request/index.android.js.map +1 -1
  49. package/http/http-request/index.ios.js.map +1 -1
  50. package/http/http-request/index.windows.d.ts +2 -0
  51. package/http/http-request/index.windows.js +41 -0
  52. package/http/http-request/index.windows.js.map +1 -0
  53. package/http/http-request-internal/index.android.d.ts +1 -7
  54. package/http/http-request-internal/index.android.js.map +1 -1
  55. package/http/http-request-internal/index.ios.d.ts +1 -7
  56. package/http/http-request-internal/index.ios.js.map +1 -1
  57. package/http/http-request-internal/index.windows.d.ts +4 -0
  58. package/http/http-request-internal/index.windows.js +134 -0
  59. package/http/http-request-internal/index.windows.js.map +1 -0
  60. package/image-asset/index.windows.d.ts +9 -0
  61. package/image-asset/index.windows.js +34 -0
  62. package/image-asset/index.windows.js.map +1 -0
  63. package/image-source/index.android.d.ts +1 -0
  64. package/image-source/index.android.js.map +1 -1
  65. package/image-source/index.d.ts +5 -0
  66. package/image-source/index.ios.d.ts +1 -0
  67. package/image-source/index.ios.js.map +1 -1
  68. package/image-source/index.windows.d.ts +52 -0
  69. package/image-source/index.windows.js +498 -0
  70. package/image-source/index.windows.js.map +1 -0
  71. package/package.json +6 -2
  72. package/platform/common.d.ts +2 -0
  73. package/platform/common.js +2 -0
  74. package/platform/common.js.map +1 -1
  75. package/platform/device/index.windows.d.ts +18 -0
  76. package/platform/device/index.windows.js +94 -0
  77. package/platform/device/index.windows.js.map +1 -0
  78. package/platform/screen/index.windows.d.ts +13 -0
  79. package/platform/screen/index.windows.js +25 -0
  80. package/platform/screen/index.windows.js.map +1 -0
  81. package/platforms/windows/arm64/NativeScript.Widgets.dll +0 -0
  82. package/platforms/windows/arm64/NativeScript.Widgets.winmd +0 -0
  83. package/platforms/windows/x64/NativeScript.Widgets.dll +0 -0
  84. package/platforms/windows/x64/NativeScript.Widgets.winmd +0 -0
  85. package/plugin.props +12 -0
  86. package/plugin.targets +71 -0
  87. package/references.d.ts +4 -0
  88. package/text/index.windows.d.ts +9 -0
  89. package/text/index.windows.js +12 -0
  90. package/text/index.windows.js.map +1 -0
  91. package/timer/index.windows.d.ts +4 -0
  92. package/timer/index.windows.js +14 -0
  93. package/timer/index.windows.js.map +1 -0
  94. package/ui/action-bar/index.ios.js +1 -0
  95. package/ui/action-bar/index.ios.js.map +1 -1
  96. package/ui/action-bar/index.windows.d.ts +12 -0
  97. package/ui/action-bar/index.windows.js +36 -0
  98. package/ui/action-bar/index.windows.js.map +1 -0
  99. package/ui/activity-indicator/index.windows.d.ts +15 -0
  100. package/ui/activity-indicator/index.windows.js +65 -0
  101. package/ui/activity-indicator/index.windows.js.map +1 -0
  102. package/ui/animation/index.windows.d.ts +19 -0
  103. package/ui/animation/index.windows.js +603 -0
  104. package/ui/animation/index.windows.js.map +1 -0
  105. package/ui/builder/index.d.ts +1 -0
  106. package/ui/builder/index.js +2 -1
  107. package/ui/builder/index.js.map +1 -1
  108. package/ui/button/index.android.d.ts +9 -2
  109. package/ui/button/index.android.js +24 -7
  110. package/ui/button/index.android.js.map +1 -1
  111. package/ui/button/index.ios.d.ts +9 -2
  112. package/ui/button/index.ios.js +72 -17
  113. package/ui/button/index.ios.js.map +1 -1
  114. package/ui/button/index.windows.d.ts +13 -0
  115. package/ui/button/index.windows.js +73 -0
  116. package/ui/button/index.windows.js.map +1 -0
  117. package/ui/core/bindable/index.js +1 -2
  118. package/ui/core/bindable/index.js.map +1 -1
  119. package/ui/core/control-state-change/index.windows.d.ts +4 -0
  120. package/ui/core/control-state-change/index.windows.js +6 -0
  121. package/ui/core/control-state-change/index.windows.js.map +1 -0
  122. package/ui/core/view/index.android.d.ts +0 -1
  123. package/ui/core/view/index.android.js +8 -17
  124. package/ui/core/view/index.android.js.map +1 -1
  125. package/ui/core/view/index.ios.d.ts +0 -1
  126. package/ui/core/view/index.ios.js +4 -17
  127. package/ui/core/view/index.ios.js.map +1 -1
  128. package/ui/core/view/index.windows.d.ts +120 -0
  129. package/ui/core/view/index.windows.js +2178 -0
  130. package/ui/core/view/index.windows.js.map +1 -0
  131. package/ui/core/view/view-common.d.ts +2 -0
  132. package/ui/core/view/view-common.js +6 -1
  133. package/ui/core/view/view-common.js.map +1 -1
  134. package/ui/core/view/view-helper/index.d.ts +0 -1
  135. package/ui/core/view/view-helper/index.ios.d.ts +0 -24
  136. package/ui/core/view/view-helper/index.ios.js +5 -54
  137. package/ui/core/view/view-helper/index.ios.js.map +1 -1
  138. package/ui/core/view/view-helper/index.windows.d.ts +3 -0
  139. package/ui/core/view/view-helper/index.windows.js +4 -0
  140. package/ui/core/view/view-helper/index.windows.js.map +1 -0
  141. package/ui/core/view-base/index.d.ts +10 -24
  142. package/ui/core/view-base/index.js +32 -50
  143. package/ui/core/view-base/index.js.map +1 -1
  144. package/ui/date-picker/index.windows.d.ts +22 -0
  145. package/ui/date-picker/index.windows.js +97 -0
  146. package/ui/date-picker/index.windows.js.map +1 -0
  147. package/ui/dialogs/index.windows.d.ts +17 -0
  148. package/ui/dialogs/index.windows.js +471 -0
  149. package/ui/dialogs/index.windows.js.map +1 -0
  150. package/ui/editable-text-base/index.windows.d.ts +27 -0
  151. package/ui/editable-text-base/index.windows.js +165 -0
  152. package/ui/editable-text-base/index.windows.js.map +1 -0
  153. package/ui/embedding/index.windows.d.ts +1 -0
  154. package/ui/embedding/index.windows.js +4 -0
  155. package/ui/embedding/index.windows.js.map +1 -0
  156. package/ui/frame/fragment.transitions.android.d.ts +13 -13
  157. package/ui/frame/fragment.transitions.android.js.map +1 -1
  158. package/ui/frame/frame-common.d.ts +1 -2
  159. package/ui/frame/frame-common.js +0 -10
  160. package/ui/frame/frame-common.js.map +1 -1
  161. package/ui/frame/frame-helper-for-android.d.ts +4 -4
  162. package/ui/frame/frame-helper-for-android.js +8 -17
  163. package/ui/frame/frame-helper-for-android.js.map +1 -1
  164. package/ui/frame/index.android.d.ts +11 -6
  165. package/ui/frame/index.android.js +59 -18
  166. package/ui/frame/index.android.js.map +1 -1
  167. package/ui/frame/index.d.ts +21 -12
  168. package/ui/frame/index.ios.d.ts +2 -2
  169. package/ui/frame/index.ios.js.map +1 -1
  170. package/ui/frame/index.windows.d.ts +52 -0
  171. package/ui/frame/index.windows.js +778 -0
  172. package/ui/frame/index.windows.js.map +1 -0
  173. package/ui/gestures/index.windows.d.ts +35 -0
  174. package/ui/gestures/index.windows.js +483 -0
  175. package/ui/gestures/index.windows.js.map +1 -0
  176. package/ui/html-view/index.windows.d.ts +18 -0
  177. package/ui/html-view/index.windows.js +191 -0
  178. package/ui/html-view/index.windows.js.map +1 -0
  179. package/ui/image/index.windows.d.ts +17 -0
  180. package/ui/image/index.windows.js +132 -0
  181. package/ui/image/index.windows.js.map +1 -0
  182. package/ui/image/symbol-effects.windows.d.ts +5 -0
  183. package/ui/image/symbol-effects.windows.js +8 -0
  184. package/ui/image/symbol-effects.windows.js.map +1 -0
  185. package/ui/image-cache/index.windows.d.ts +11 -0
  186. package/ui/image-cache/index.windows.js +48 -0
  187. package/ui/image-cache/index.windows.js.map +1 -0
  188. package/ui/label/index.ios.d.ts +5 -2
  189. package/ui/label/index.ios.js +44 -12
  190. package/ui/label/index.ios.js.map +1 -1
  191. package/ui/label/index.windows.d.ts +15 -0
  192. package/ui/label/index.windows.js +66 -0
  193. package/ui/label/index.windows.js.map +1 -0
  194. package/ui/layouts/absolute-layout/index.ios.js +1 -5
  195. package/ui/layouts/absolute-layout/index.ios.js.map +1 -1
  196. package/ui/layouts/absolute-layout/index.windows.d.ts +8 -0
  197. package/ui/layouts/absolute-layout/index.windows.js +47 -0
  198. package/ui/layouts/absolute-layout/index.windows.js.map +1 -0
  199. package/ui/layouts/dock-layout/index.ios.js +1 -5
  200. package/ui/layouts/dock-layout/index.ios.js.map +1 -1
  201. package/ui/layouts/dock-layout/index.windows.d.ts +19 -0
  202. package/ui/layouts/dock-layout/index.windows.js +202 -0
  203. package/ui/layouts/dock-layout/index.windows.js.map +1 -0
  204. package/ui/layouts/flexbox-layout/flexbox-layout-common.d.ts +17 -2
  205. package/ui/layouts/flexbox-layout/flexbox-layout-common.js +73 -2
  206. package/ui/layouts/flexbox-layout/flexbox-layout-common.js.map +1 -1
  207. package/ui/layouts/flexbox-layout/index.ios.js +1 -5
  208. package/ui/layouts/flexbox-layout/index.ios.js.map +1 -1
  209. package/ui/layouts/flexbox-layout/index.windows.d.ts +27 -0
  210. package/ui/layouts/flexbox-layout/index.windows.js +188 -0
  211. package/ui/layouts/flexbox-layout/index.windows.js.map +1 -0
  212. package/ui/layouts/grid-layout/index.ios.js +1 -5
  213. package/ui/layouts/grid-layout/index.ios.js.map +1 -1
  214. package/ui/layouts/grid-layout/index.windows.d.ts +20 -0
  215. package/ui/layouts/grid-layout/index.windows.js +130 -0
  216. package/ui/layouts/grid-layout/index.windows.js.map +1 -0
  217. package/ui/layouts/layout-base.android.d.ts +10 -3
  218. package/ui/layouts/layout-base.android.js +24 -7
  219. package/ui/layouts/layout-base.android.js.map +1 -1
  220. package/ui/layouts/layout-base.ios.js.map +1 -1
  221. package/ui/layouts/layout-base.windows.d.ts +4 -0
  222. package/ui/layouts/layout-base.windows.js +5 -0
  223. package/ui/layouts/layout-base.windows.js.map +1 -0
  224. package/ui/layouts/liquid-glass/index.windows.d.ts +4 -0
  225. package/ui/layouts/liquid-glass/index.windows.js +5 -0
  226. package/ui/layouts/liquid-glass/index.windows.js.map +1 -0
  227. package/ui/layouts/liquid-glass-container/index.windows.d.ts +4 -0
  228. package/ui/layouts/liquid-glass-container/index.windows.js +5 -0
  229. package/ui/layouts/liquid-glass-container/index.windows.js.map +1 -0
  230. package/ui/layouts/root-layout/index.windows.d.ts +13 -0
  231. package/ui/layouts/root-layout/index.windows.js +160 -0
  232. package/ui/layouts/root-layout/index.windows.js.map +1 -0
  233. package/ui/layouts/stack-layout/index.ios.js +1 -5
  234. package/ui/layouts/stack-layout/index.ios.js.map +1 -1
  235. package/ui/layouts/stack-layout/index.windows.d.ts +13 -0
  236. package/ui/layouts/stack-layout/index.windows.js +63 -0
  237. package/ui/layouts/stack-layout/index.windows.js.map +1 -0
  238. package/ui/layouts/wrap-layout/index.ios.js +1 -5
  239. package/ui/layouts/wrap-layout/index.ios.js.map +1 -1
  240. package/ui/layouts/wrap-layout/index.windows.d.ts +20 -0
  241. package/ui/layouts/wrap-layout/index.windows.js +182 -0
  242. package/ui/layouts/wrap-layout/index.windows.js.map +1 -0
  243. package/ui/list-picker/index.windows.d.ts +19 -0
  244. package/ui/list-picker/index.windows.js +75 -0
  245. package/ui/list-picker/index.windows.js.map +1 -0
  246. package/ui/list-view/index.d.ts +1 -13
  247. package/ui/list-view/index.ios.d.ts +1 -3
  248. package/ui/list-view/index.ios.js +10 -48
  249. package/ui/list-view/index.ios.js.map +1 -1
  250. package/ui/list-view/index.windows.d.ts +60 -0
  251. package/ui/list-view/index.windows.js +842 -0
  252. package/ui/list-view/index.windows.js.map +1 -0
  253. package/ui/list-view/list-view-common.d.ts +0 -14
  254. package/ui/list-view/list-view-common.js +0 -4
  255. package/ui/list-view/list-view-common.js.map +1 -1
  256. package/ui/page/index.d.ts +1 -1
  257. package/ui/page/index.ios.d.ts +0 -1
  258. package/ui/page/index.ios.js +1 -15
  259. package/ui/page/index.ios.js.map +1 -1
  260. package/ui/page/index.windows.d.ts +12 -0
  261. package/ui/page/index.windows.js +42 -0
  262. package/ui/page/index.windows.js.map +1 -0
  263. package/ui/page/page-common.d.ts +2 -5
  264. package/ui/page/page-common.js.map +1 -1
  265. package/ui/progress/index.windows.d.ts +20 -0
  266. package/ui/progress/index.windows.js +65 -0
  267. package/ui/progress/index.windows.js.map +1 -0
  268. package/ui/repeater/index.d.ts +1 -2
  269. package/ui/repeater/index.js.map +1 -1
  270. package/ui/scroll-view/index.d.ts +0 -13
  271. package/ui/scroll-view/index.ios.js +7 -21
  272. package/ui/scroll-view/index.ios.js.map +1 -1
  273. package/ui/scroll-view/index.windows.d.ts +29 -0
  274. package/ui/scroll-view/index.windows.js +192 -0
  275. package/ui/scroll-view/index.windows.js.map +1 -0
  276. package/ui/scroll-view/scroll-view-common.d.ts +0 -6
  277. package/ui/scroll-view/scroll-view-common.js +0 -8
  278. package/ui/scroll-view/scroll-view-common.js.map +1 -1
  279. package/ui/search-bar/index.windows.d.ts +19 -0
  280. package/ui/search-bar/index.windows.js +91 -0
  281. package/ui/search-bar/index.windows.js.map +1 -0
  282. package/ui/segmented-bar/index.windows.d.ts +23 -0
  283. package/ui/segmented-bar/index.windows.js +117 -0
  284. package/ui/segmented-bar/index.windows.js.map +1 -0
  285. package/ui/segmented-bar/segmented-bar-common.d.ts +1 -5
  286. package/ui/segmented-bar/segmented-bar-common.js.map +1 -1
  287. package/ui/slider/index.windows.d.ts +25 -0
  288. package/ui/slider/index.windows.js +134 -0
  289. package/ui/slider/index.windows.js.map +1 -0
  290. package/ui/split-view/index.windows.d.ts +49 -0
  291. package/ui/split-view/index.windows.js +549 -0
  292. package/ui/split-view/index.windows.js.map +1 -0
  293. package/ui/styling/background.windows.d.ts +2 -0
  294. package/ui/styling/background.windows.js +2 -0
  295. package/ui/styling/background.windows.js.map +1 -0
  296. package/ui/styling/font.d.ts +4 -0
  297. package/ui/styling/font.windows.d.ts +25 -0
  298. package/ui/styling/font.windows.js +213 -0
  299. package/ui/styling/font.windows.js.map +1 -0
  300. package/ui/styling/style/index.d.ts +0 -1
  301. package/ui/styling/style/index.js.map +1 -1
  302. package/ui/styling/style-properties.d.ts +0 -1
  303. package/ui/styling/style-properties.js +32 -41
  304. package/ui/styling/style-properties.js.map +1 -1
  305. package/ui/styling/style-scope.js +9 -1
  306. package/ui/styling/style-scope.js.map +1 -1
  307. package/ui/switch/index.windows.d.ts +20 -0
  308. package/ui/switch/index.windows.js +97 -0
  309. package/ui/switch/index.windows.js.map +1 -0
  310. package/ui/tab-view/index.ios.d.ts +1 -10
  311. package/ui/tab-view/index.ios.js +4 -21
  312. package/ui/tab-view/index.ios.js.map +1 -1
  313. package/ui/tab-view/index.windows.d.ts +29 -0
  314. package/ui/tab-view/index.windows.js +268 -0
  315. package/ui/tab-view/index.windows.js.map +1 -0
  316. package/ui/tab-view/tab-view-common.d.ts +1 -5
  317. package/ui/tab-view/tab-view-common.js.map +1 -1
  318. package/ui/text-base/index.android.d.ts +9 -2
  319. package/ui/text-base/index.android.js +24 -7
  320. package/ui/text-base/index.android.js.map +1 -1
  321. package/ui/text-base/index.windows.d.ts +47 -0
  322. package/ui/text-base/index.windows.js +639 -0
  323. package/ui/text-base/index.windows.js.map +1 -0
  324. package/ui/text-field/index.ios.d.ts +9 -2
  325. package/ui/text-field/index.ios.js +23 -2
  326. package/ui/text-field/index.ios.js.map +1 -1
  327. package/ui/text-field/index.windows.d.ts +23 -0
  328. package/ui/text-field/index.windows.js +186 -0
  329. package/ui/text-field/index.windows.js.map +1 -0
  330. package/ui/text-view/index.ios.d.ts +9 -2
  331. package/ui/text-view/index.ios.js +72 -20
  332. package/ui/text-view/index.ios.js.map +1 -1
  333. package/ui/text-view/index.windows.d.ts +12 -0
  334. package/ui/text-view/index.windows.js +47 -0
  335. package/ui/text-view/index.windows.js.map +1 -0
  336. package/ui/time-picker/index.windows.d.ts +18 -0
  337. package/ui/time-picker/index.windows.js +77 -0
  338. package/ui/time-picker/index.windows.js.map +1 -0
  339. package/ui/transition/fade-transition.windows.d.ts +3 -0
  340. package/ui/transition/fade-transition.windows.js +4 -0
  341. package/ui/transition/fade-transition.windows.js.map +1 -0
  342. package/ui/transition/index.d.ts +1 -2
  343. package/ui/transition/index.windows.d.ts +20 -0
  344. package/ui/transition/index.windows.js +30 -0
  345. package/ui/transition/index.windows.js.map +1 -0
  346. package/ui/transition/modal-transition.ios.js +5 -33
  347. package/ui/transition/modal-transition.ios.js.map +1 -1
  348. package/ui/transition/modal-transition.windows.d.ts +3 -0
  349. package/ui/transition/modal-transition.windows.js +4 -0
  350. package/ui/transition/modal-transition.windows.js.map +1 -0
  351. package/ui/transition/page-transition.ios.d.ts +0 -5
  352. package/ui/transition/page-transition.ios.js +33 -296
  353. package/ui/transition/page-transition.ios.js.map +1 -1
  354. package/ui/transition/page-transition.windows.d.ts +3 -0
  355. package/ui/transition/page-transition.windows.js +4 -0
  356. package/ui/transition/page-transition.windows.js.map +1 -0
  357. package/ui/transition/shared-transition-helper.android.d.ts +0 -3
  358. package/ui/transition/shared-transition-helper.android.js +0 -9
  359. package/ui/transition/shared-transition-helper.android.js.map +1 -1
  360. package/ui/transition/shared-transition-helper.d.ts +0 -7
  361. package/ui/transition/shared-transition-helper.ios.d.ts +0 -36
  362. package/ui/transition/shared-transition-helper.ios.js +89 -765
  363. package/ui/transition/shared-transition-helper.ios.js.map +1 -1
  364. package/ui/transition/shared-transition-helper.windows.d.ts +30 -0
  365. package/ui/transition/shared-transition-helper.windows.js +155 -0
  366. package/ui/transition/shared-transition-helper.windows.js.map +1 -0
  367. package/ui/transition/shared-transition.d.ts +0 -66
  368. package/ui/transition/shared-transition.js +2 -57
  369. package/ui/transition/shared-transition.js.map +1 -1
  370. package/ui/transition/slide-transition.windows.d.ts +3 -0
  371. package/ui/transition/slide-transition.windows.js +4 -0
  372. package/ui/transition/slide-transition.windows.js.map +1 -0
  373. package/ui/utils.windows.d.ts +9 -0
  374. package/ui/utils.windows.js +21 -0
  375. package/ui/utils.windows.js.map +1 -0
  376. package/ui/web-view/index.windows.d.ts +23 -0
  377. package/ui/web-view/index.windows.js +115 -0
  378. package/ui/web-view/index.windows.js.map +1 -0
  379. package/utils/constants.windows.d.ts +2 -0
  380. package/utils/constants.windows.js +5 -0
  381. package/utils/constants.windows.js.map +1 -0
  382. package/utils/debug-source.js +3 -0
  383. package/utils/debug-source.js.map +1 -1
  384. package/utils/index.windows.d.ts +20 -0
  385. package/utils/index.windows.js +94 -0
  386. package/utils/index.windows.js.map +1 -0
  387. package/utils/layout-helper/index.windows.d.ts +25 -0
  388. package/utils/layout-helper/index.windows.js +74 -0
  389. package/utils/layout-helper/index.windows.js.map +1 -0
  390. package/utils/mainthread-helper.windows.d.ts +3 -0
  391. package/utils/mainthread-helper.windows.js +18 -0
  392. package/utils/mainthread-helper.windows.js.map +1 -0
  393. package/utils/native-helper.android.d.ts +2 -66
  394. package/utils/native-helper.android.js.map +1 -1
  395. package/utils/native-helper.ios.d.ts +2 -105
  396. package/utils/native-helper.ios.js +4 -15
  397. package/utils/native-helper.ios.js.map +1 -1
  398. package/utils/native-helper.windows.d.ts +8 -0
  399. package/utils/native-helper.windows.js +101 -0
  400. package/utils/native-helper.windows.js.map +1 -0
  401. package/utils/platform-check.d.ts +1 -1
  402. package/utils/platform-check.js.map +1 -1
  403. package/utils/native-helper.types.d.ts +0 -2
@@ -0,0 +1,2178 @@
1
+ export * from './view-common';
2
+ export * from './view-helper';
3
+ export * from '../properties';
4
+ import { ViewCommon, originXProperty, originYProperty } from './view-common';
5
+ import { visibilityProperty, opacityProperty, backgroundInternalProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, rotateProperty, rotateXProperty, rotateYProperty, perspectiveProperty, horizontalAlignmentProperty, verticalAlignmentProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from '../../styling/style-properties';
6
+ import { widthProperty, heightProperty, minWidthProperty, minHeightProperty, marginLeftProperty, marginTopProperty, marginRightProperty, marginBottomProperty } from '../../styling/style-properties';
7
+ import { layout } from '../../../utils';
8
+ import { unsetValue } from '../properties/property-shared';
9
+ import { Color } from '../../../color';
10
+ import { hiddenProperty } from '../view-base';
11
+ import { getCurrentWindowBounds, getCurrentWindowContent } from '../../../application/window-helper.windows';
12
+ import { ImageSource } from '../../../image-source';
13
+ import { ClipPathFunction } from '../../styling/clip-path-function';
14
+ // Windows.UI.Color is a plain {A,R,G,B} struct — bridge reads fields directly, no WinRT call needed.
15
+ const _defaultBackground = { A: 0, R: 0, G: 0, B: 0 };
16
+ export function getDefaultBackground() {
17
+ return _defaultBackground;
18
+ }
19
+ // `_nativeBackgroundState` is initialized on the prototype after the class definition
20
+ function toXamlLength(value) {
21
+ if (!value || value === 'auto')
22
+ return NaN;
23
+ if (typeof value === 'number')
24
+ return value;
25
+ if (typeof value === 'object' && 'value' in value) {
26
+ if (value.unit === '%')
27
+ return NaN; // percent not directly settable
28
+ return value.value ?? NaN;
29
+ }
30
+ return NaN;
31
+ }
32
+ // Color struct cache: Windows.UI.Color is {A,R,G,B} bytes — construct directly, no ColorHelper.FromArgb().
33
+ // Cache avoids allocating a new object for every border/shadow color update with the same color.
34
+ const _winColorCache = new Map();
35
+ function _argbToWinColor(argb) {
36
+ const cached = _winColorCache.get(argb);
37
+ if (cached !== undefined)
38
+ return cached;
39
+ const c = { A: (argb >>> 24) & 0xFF, R: (argb >> 16) & 0xFF, G: (argb >> 8) & 0xFF, B: argb & 0xFF };
40
+ if (_winColorCache.size >= 32)
41
+ _winColorCache.delete(_winColorCache.keys().next().value);
42
+ _winColorCache.set(argb, c);
43
+ return c;
44
+ }
45
+ // Parse a CSS clip-path dimension value: "50%"→fraction of totalDip, "10px"→px/density, "10"→raw DIP.
46
+ function _parseClipDim(val, totalDip, density) {
47
+ const s = (val || '').trim();
48
+ if (s.endsWith('%'))
49
+ return (parseFloat(s) / 100) * totalDip;
50
+ if (s.endsWith('px'))
51
+ return parseFloat(s) / density;
52
+ return parseFloat(s) || 0;
53
+ }
54
+ // True for CSS background-repeat values that tile along at least one axis (repeat / repeat-x /
55
+ // repeat-y, incl. the two-value 'repeat repeat'). WinUI's ImageBrush can't tile, so these go through
56
+ // TileHelper. 'space'/'round' aren't implemented and fall through to the plain (no-repeat) path.
57
+ function _isTilingRepeat(repeat) {
58
+ const r = String(repeat ?? '').toLowerCase().trim();
59
+ return r === 'repeat' || r === 'repeat-x' || r === 'repeat-y' || r === 'repeat repeat';
60
+ }
61
+ function _resolveToWinColor(color) {
62
+ if (!color)
63
+ return null;
64
+ if (typeof color.windows !== 'undefined') {
65
+ try {
66
+ return color.windows;
67
+ }
68
+ catch (_e) { }
69
+ }
70
+ if (typeof color.A === 'number' && typeof color.R === 'number') {
71
+ return color;
72
+ }
73
+ if (typeof color === 'number') {
74
+ return _argbToWinColor(color >>> 0);
75
+ }
76
+ if (typeof color === 'string') {
77
+ try {
78
+ return new Color(color).windows;
79
+ }
80
+ catch (_e) { }
81
+ }
82
+ return null;
83
+ }
84
+ export function _ensureNativeTransforms(native) {
85
+ try {
86
+ if (native.__ns_transforms)
87
+ return native.__ns_transforms;
88
+ const tg = new Microsoft.UI.Xaml.Media.TransformGroup();
89
+ const scale = new Microsoft.UI.Xaml.Media.ScaleTransform();
90
+ const rotate = new Microsoft.UI.Xaml.Media.RotateTransform();
91
+ const translate = new Microsoft.UI.Xaml.Media.TranslateTransform();
92
+ tg.Children.Append(scale);
93
+ tg.Children.Append(rotate);
94
+ tg.Children.Append(translate);
95
+ native.RenderTransform = tg;
96
+ const result = { scale, rotate, translate };
97
+ native.__ns_transforms = result;
98
+ return result;
99
+ }
100
+ catch (_e) {
101
+ return null;
102
+ }
103
+ }
104
+ const hidden = Symbol('[[hidden]]');
105
+ function setVisibility(nativeView, value) {
106
+ switch (value) {
107
+ case "collapse":
108
+ case "collapsed":
109
+ nativeView[hidden] = true;
110
+ nativeView.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
111
+ try {
112
+ nativeView.IsHitTestVisible = true;
113
+ }
114
+ catch (_e) { } // moot while collapsed; correct once shown
115
+ break;
116
+ case "hidden":
117
+ // 'hidden' = invisible but STILL TAKES LAYOUT SPACE — must stay Visibility=Visible (not
118
+ // Collapsed). An invisible element that still captures pointer input blocks taps on whatever
119
+ // it overlaps, so also disable hit-testing.
120
+ nativeView[hidden] = true;
121
+ nativeView.Opacity = 0;
122
+ nativeView.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
123
+ try {
124
+ nativeView.IsHitTestVisible = false;
125
+ }
126
+ catch (_e) { }
127
+ break;
128
+ case "visible":
129
+ if (!nativeView[hidden])
130
+ return; // symbol unset = native default (Visible/Opacity=1/HitTest=true) — skip 4 WinRT calls
131
+ nativeView[hidden] = false;
132
+ nativeView.Opacity = 1;
133
+ nativeView.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
134
+ try {
135
+ nativeView.IsHitTestVisible = true;
136
+ }
137
+ catch (_e) { } // restore (in case it was 'hidden' before)
138
+ break;
139
+ default:
140
+ }
141
+ }
142
+ function getVisibility(nativeView) {
143
+ if (nativeView[hidden]) {
144
+ if (nativeView.Opacity === 0) {
145
+ return "hidden";
146
+ }
147
+ else {
148
+ if (nativeView.Visibility === Microsoft.UI.Xaml.Visibility.Collapsed) {
149
+ return "collapse";
150
+ }
151
+ }
152
+ }
153
+ return "visible";
154
+ }
155
+ // Box-shadow uses native Microsoft.UI.Composition DropShadow (GPU, per element). Shadow hosts are
156
+ // inserted as siblings behind the target in its parent panel. NativeScript.Widgets.ShadowHelper
157
+ // handles the CPU blur fallback path on a thread-pool thread (never touches JS/UI thread).
158
+ class CompositionBorderHandler {
159
+ constructor(element, rootVisual) {
160
+ // Border SpriteVisuals are created lazily in _ensureBorderSprites() — only when UpdateBorder()
161
+ // is first called with a non-zero width. Shadow-only handlers never allocate these.
162
+ this._top = null;
163
+ this._bottom = null;
164
+ this._left = null;
165
+ this._right = null;
166
+ this._topBrush = null;
167
+ this._bottomBrush = null;
168
+ this._leftBrush = null;
169
+ this._rightBrush = null;
170
+ this._clipGeometry = null;
171
+ // Sig of last applied border geometry (w×h + all widths/colors/radius); guards the WinRT property
172
+ // sets in UpdateBorder, which runs on every SizeChanged.
173
+ this._lastBorderUpdateSig = null;
174
+ // One host per CSS shadow, stacked behind element (first-listed shadow nearest/highest z).
175
+ this._shadowImages = [];
176
+ // Compositor objects kept alive alongside each host for fast-path size-only updates.
177
+ // Parallel array to _shadowImages (same length, same index correspondence).
178
+ this._shadowEntries = [];
179
+ // Children collection captured at insert time — re-reading el.Parent at removal returns undefined
180
+ // on this host, causing old shadows to accumulate.
181
+ this._shadowKids = null;
182
+ // Signature of last applied shadow render. UpdateBoxShadow runs on SizeChanged; re-inserting
183
+ // hosts re-invalidates layout → LayoutCycleException. Only mutate the tree when sig changes.
184
+ this._lastShadowSig = null;
185
+ // Config-only sig (no size). When only w×h changes the host Border and all Compositor objects
186
+ // are updated in-place — no XAML tree mutation, no new WinRT allocations.
187
+ this._lastShadowConfigSig = null;
188
+ // Per-side colored border drawn as a bitmap overlay ON TOP of the element (native XAML BorderBrush
189
+ // is one color, so 4-color borders need a separate bitmap). Inserted right after the element (higher z).
190
+ this._borderOverlayActive = false;
191
+ this._borderKids = null;
192
+ this._lastBorderSig = null;
193
+ this._element = element;
194
+ this._rootVisual = rootVisual;
195
+ const c = rootVisual.Compositor;
196
+ this._container = c.CreateContainerVisual();
197
+ // Expression animations on `Size` throw E_INVALIDARG in this V8/UWP composition projection;
198
+ // RelativeSizeAdjustment(1,1) tracks the parent size natively without an animation.
199
+ this._container.RelativeSizeAdjustment = new Windows.Foundation.Numerics.Vector2(1, 1);
200
+ // Border SpriteVisuals NOT created here — see _ensureBorderSprites().
201
+ Microsoft.UI.Xaml.Hosting.ElementCompositionPreview.SetElementChildVisual(element, this._container);
202
+ }
203
+ // Lazily allocate the 4 border SpriteVisuals. Only called by UpdateBorder when a real border
204
+ // width is present. Returns false if allocation fails (handler unusable for borders).
205
+ _ensureBorderSprites() {
206
+ if (this._top)
207
+ return true;
208
+ try {
209
+ const c = this._rootVisual.Compositor;
210
+ this._topBrush = c.CreateColorBrush();
211
+ this._bottomBrush = c.CreateColorBrush();
212
+ this._leftBrush = c.CreateColorBrush();
213
+ this._rightBrush = c.CreateColorBrush();
214
+ this._top = c.CreateSpriteVisual();
215
+ this._top.Brush = this._topBrush;
216
+ this._bottom = c.CreateSpriteVisual();
217
+ this._bottom.Brush = this._bottomBrush;
218
+ this._left = c.CreateSpriteVisual();
219
+ this._left.Brush = this._leftBrush;
220
+ this._right = c.CreateSpriteVisual();
221
+ this._right.Brush = this._rightBrush;
222
+ this._container.Children.InsertAtTop(this._top);
223
+ this._container.Children.InsertAtTop(this._bottom);
224
+ this._container.Children.InsertAtTop(this._left);
225
+ this._container.Children.InsertAtTop(this._right);
226
+ return true;
227
+ }
228
+ catch (_e) {
229
+ return false;
230
+ }
231
+ }
232
+ static Create(element) {
233
+ try {
234
+ const rootVisual = Microsoft.UI.Xaml.Hosting.ElementCompositionPreview.GetElementVisual(element);
235
+ return new CompositionBorderHandler(element, rootVisual);
236
+ }
237
+ catch (_e) {
238
+ return null;
239
+ }
240
+ }
241
+ _animate(target, prop, expr) {
242
+ // Expression animations throw E_INVALIDARG in this V8/UWP composition projection; guard so a
243
+ // failure can't abort background setup (which would blank the page).
244
+ try {
245
+ const anim = this._rootVisual.Compositor.CreateExpressionAnimation(expr);
246
+ anim.SetReferenceParameter('host', this._rootVisual);
247
+ target.StartAnimation(prop, anim);
248
+ }
249
+ catch (_e) { }
250
+ }
251
+ // Four explicitly-sized SpriteVisuals (one per side) for per-side widths/colors; rounded geometric
252
+ // clip for corner radius. Explicit sizing avoids E_INVALIDARG from expression animations. Works on
253
+ // plain panels (no native BorderThickness/BorderBrush) and renders above the element edge.
254
+ UpdateBorder(w, h, tW, rW, bW, lW, tC, rC, bC, lC, radius) {
255
+ if (w <= 0 || h <= 0) {
256
+ return; // not laid out yet — _redrawNativeBackground re-runs on SizeChanged
257
+ }
258
+ // Sig guards the WinRT property sets below; UpdateBorder runs on every SizeChanged, so skip
259
+ // when nothing changed.
260
+ const sig = `${Math.round(w)}x${Math.round(h)}|${tW},${rW},${bW},${lW},${tC},${rC},${bC},${lC},${radius}`;
261
+ if (sig === this._lastBorderUpdateSig)
262
+ return;
263
+ this._lastBorderUpdateSig = sig;
264
+ if (!this._ensureBorderSprites())
265
+ return;
266
+ try {
267
+ const N = Windows.Foundation.Numerics;
268
+ const tWc = Math.max(0, tW), rWc = Math.max(0, rW), bWc = Math.max(0, bW), lWc = Math.max(0, lW);
269
+ const midH = Math.max(0, h - tWc - bWc);
270
+ this._top.Offset = new N.Vector3(0, 0, 0);
271
+ this._top.Size = new N.Vector2(w, tWc);
272
+ this._topBrush.Color = _argbToWinColor(tC);
273
+ this._bottom.Offset = new N.Vector3(0, Math.max(0, h - bWc), 0);
274
+ this._bottom.Size = new N.Vector2(w, bWc);
275
+ this._bottomBrush.Color = _argbToWinColor(bC);
276
+ this._left.Offset = new N.Vector3(0, tWc, 0);
277
+ this._left.Size = new N.Vector2(lWc, midH);
278
+ this._leftBrush.Color = _argbToWinColor(lC);
279
+ this._right.Offset = new N.Vector3(Math.max(0, w - rWc), tWc, 0);
280
+ this._right.Size = new N.Vector2(rWc, midH);
281
+ this._rightBrush.Color = _argbToWinColor(rC);
282
+ const c = this._rootVisual.Compositor;
283
+ if (radius > 0) {
284
+ if (!this._clipGeometry) {
285
+ this._clipGeometry = c.CreateRoundedRectangleGeometry();
286
+ this._container.Clip = c.CreateGeometricClip(this._clipGeometry);
287
+ }
288
+ this._clipGeometry.Size = new N.Vector2(w, h);
289
+ this._clipGeometry.CornerRadius = new N.Vector2(radius, radius);
290
+ }
291
+ else if (this._clipGeometry) {
292
+ try {
293
+ this._container.Clip = null;
294
+ }
295
+ catch (_e) { }
296
+ this._clipGeometry = null;
297
+ }
298
+ }
299
+ catch (_e) { }
300
+ }
301
+ ClearBorder() {
302
+ // No sprites ever created (shadow-only handler) or already cleared — nothing to do.
303
+ if (!this._top || this._lastBorderUpdateSig === 'none')
304
+ return;
305
+ this._lastBorderUpdateSig = 'none';
306
+ try {
307
+ const z = new Windows.Foundation.Numerics.Vector2(0, 0);
308
+ this._top.Size = z;
309
+ this._bottom.Size = z;
310
+ this._left.Size = z;
311
+ this._right.Size = z;
312
+ if (this._clipGeometry) {
313
+ try {
314
+ this._container.Clip = null;
315
+ }
316
+ catch (_e) { }
317
+ this._clipGeometry = null;
318
+ }
319
+ }
320
+ catch (_e) { }
321
+ }
322
+ UpdateBoxShadow(shadows, cornerRadius = 0) {
323
+ // One element-sized DropShadow host per CSS shadow, inserted as a sibling behind the target.
324
+ // Overlap-capable parents only — StackPanel would stack the host as a new row (skipped below).
325
+ const el = this._element;
326
+ if (!el) {
327
+ return;
328
+ }
329
+ try {
330
+ const list = shadows == null ? [] : (Array.isArray(shadows) ? shadows : [shadows]);
331
+ // No shadows → clear (only mutate tree if we currently have images).
332
+ if (!list.length) {
333
+ if (this._lastShadowSig === 'none') {
334
+ return;
335
+ }
336
+ this._lastShadowSig = 'none';
337
+ this._lastShadowConfigSig = null;
338
+ this._removeShadowImages();
339
+ return;
340
+ }
341
+ const w = el.ActualWidth || 0;
342
+ const h = el.ActualHeight || 0;
343
+ if (w <= 0 || h <= 0) {
344
+ return; // not laid out yet — re-applied on the next SizeChanged redraw (sig not cached)
345
+ }
346
+ // Signature guards against LayoutCycleException: inserting/removing hosts re-invalidates
347
+ // layout. Only mutate the XAML tree when the shadow config changes; when only w×h changes
348
+ // (size-only path) update Compositor objects in-place. Key on pure-JS `.argb`, not `.windows`
349
+ // (which calls ColorHelper.FromArgb() + 4 struct reads per shadow color on every call).
350
+ const configSig = `${cornerRadius}|` + list.map((s) => {
351
+ const a = s.color?.argb ?? 0;
352
+ return `${a}:${s.offsetX || 0},${s.offsetY || 0},${s.blurRadius || 0},${s.spreadRadius || 0}`;
353
+ }).join(';');
354
+ const sig = `${Math.round(w)}x${Math.round(h)}|${configSig}`;
355
+ if (sig === this._lastShadowSig && this._shadowImages.length > 0) {
356
+ return;
357
+ }
358
+ // Fast path: only w×h changed, same shadow config, same count → update sizes in-place.
359
+ const configUnchanged = configSig === this._lastShadowConfigSig
360
+ && this._shadowImages.length === list.length
361
+ && this._shadowEntries.length === list.length
362
+ && this._shadowImages.length > 0;
363
+ this._lastShadowSig = sig;
364
+ // _lastShadowConfigSig is set AFTER _removeShadowImages() in the full-rebuild path so that
365
+ // a mid-rebuild exception can't leave a stale configSig that fools the fast path.
366
+ if (configUnchanged) {
367
+ try {
368
+ const N = Windows.Foundation.Numerics;
369
+ const cssOrder = list.slice().reverse();
370
+ for (let i = 0; i < this._shadowEntries.length; i++) {
371
+ const entry = this._shadowEntries[i];
372
+ const s = cssOrder[i];
373
+ const spread = Math.max(0, layout.toDeviceIndependentPixels(s.spreadRadius || 0));
374
+ const cw = Math.max(0, w + 2 * spread);
375
+ const chh = Math.max(0, h + 2 * spread);
376
+ this._shadowImages[i].Width = w;
377
+ this._shadowImages[i].Height = h;
378
+ entry.layer.Size = new N.Vector2(cw, chh);
379
+ entry.shapeVisual.Size = new N.Vector2(cw, chh);
380
+ entry.geo.Size = new N.Vector2(cw, chh);
381
+ }
382
+ return; // no tree mutation needed
383
+ }
384
+ catch (_e) { /* fall through to full rebuild on error */ }
385
+ }
386
+ this._removeShadowImages();
387
+ // Now safe to record the new configSig — hosts have been removed, rebuild is starting.
388
+ this._lastShadowConfigSig = configSig;
389
+ const parent = el.Parent;
390
+ const kids = parent?.Children;
391
+ if (!kids || typeof kids.Size !== 'number') {
392
+ return;
393
+ }
394
+ if (typeof parent.Orientation !== 'undefined') {
395
+ return; // StackPanel — sibling overlap not possible
396
+ }
397
+ const G = Microsoft.UI.Xaml.Controls.Grid;
398
+ // style-properties stores shadows in reverse CSS order; restore so built[0] is first-listed
399
+ // (nearest the element after insertion).
400
+ const cssOrder = list.slice().reverse();
401
+ // CRITICAL: ActualWidth/Height and cornerRadius are in DIPs, but blurRadius/spreadRadius/
402
+ // offsetX/offsetY are in device pixels (style system converts via Length.toDevicePixels).
403
+ // Convert back to DIPs — mixing units over-blurs and swamps the offset (flat halo artifact).
404
+ const built = [];
405
+ for (const s of cssOrder) {
406
+ const entry = this._buildShadowHost(s, w, h, cornerRadius);
407
+ if (!entry) {
408
+ continue;
409
+ }
410
+ const host = entry.host;
411
+ // Match grid cell + alignment of the element. Composition shadow overflows host bounds
412
+ // (XAML doesn't clip child visuals), so element-sized host is sufficient.
413
+ try {
414
+ host.HorizontalAlignment = el.HorizontalAlignment;
415
+ host.VerticalAlignment = el.VerticalAlignment;
416
+ G.SetRow(host, G.GetRow(el));
417
+ G.SetColumn(host, G.GetColumn(el));
418
+ G.SetRowSpan(host, G.GetRowSpan(el));
419
+ G.SetColumnSpan(host, G.GetColumnSpan(el));
420
+ }
421
+ catch (_e) { }
422
+ built.push(entry);
423
+ }
424
+ if (!built.length) {
425
+ return;
426
+ }
427
+ // Insert behind the element (lower index = lower z). Always inserting at the element's index
428
+ // pushes earlier-inserted images up, so the first-listed shadow ends up nearest (per CSS).
429
+ let idx = -1;
430
+ for (let i = 0; i < kids.Size; i++) {
431
+ if (kids.GetAt(i) === el) {
432
+ idx = i;
433
+ break;
434
+ }
435
+ }
436
+ for (const entry of built) {
437
+ if (idx < 0) {
438
+ kids.Append(entry.host);
439
+ }
440
+ else {
441
+ kids.InsertAt(idx, entry.host);
442
+ }
443
+ }
444
+ this._shadowImages = built.map(e => e.host);
445
+ this._shadowEntries = built.map(e => ({ layer: e.layer, shapeVisual: e.shapeVisual, geo: e.geo }));
446
+ this._shadowKids = kids; // remember the exact collection for removal (el.Parent is unreliable later)
447
+ }
448
+ catch (_e) { }
449
+ }
450
+ // Element-sized host carrying one DropShadow per CSS box-shadow entry. The opaque shadow caster is
451
+ // covered by the (opaque) element, so only the blurred shadow shows. Spread inflates the caster and
452
+ // its corner radius. Uses LayerVisual + ShapeVisual (a GeometricClip would clip the shadow away).
453
+ _buildShadowHost(s, w, h, cornerRadius) {
454
+ try {
455
+ const winColor = s.color?.windows;
456
+ if (!winColor) {
457
+ return null;
458
+ }
459
+ const N = Windows.Foundation.Numerics;
460
+ const CH = Windows.UI.ColorHelper;
461
+ const blur = Math.max(0, layout.toDeviceIndependentPixels(s.blurRadius || 0));
462
+ const spread = Math.max(0, layout.toDeviceIndependentPixels(s.spreadRadius || 0));
463
+ const offX = layout.toDeviceIndependentPixels(s.offsetX || 0);
464
+ const offY = layout.toDeviceIndependentPixels(s.offsetY || 0);
465
+ const cr = Math.max(0, cornerRadius);
466
+ const cw = Math.max(0, w + 2 * spread);
467
+ const chh = Math.max(0, h + 2 * spread);
468
+ // DropShadow.Color carries the alpha for CSS opacity; caster fill is opaque for full-strength shadow.
469
+ const shadowColor = CH.FromArgb(winColor.A ?? 255, winColor.R ?? 0, winColor.G ?? 0, winColor.B ?? 0);
470
+ const casterColor = CH.FromArgb(255, winColor.R ?? 0, winColor.G ?? 0, winColor.B ?? 0);
471
+ const host = new Microsoft.UI.Xaml.Controls.Border();
472
+ host.Width = w;
473
+ host.Height = h;
474
+ try {
475
+ host.IsHitTestVisible = false;
476
+ }
477
+ catch (_e) { }
478
+ const ECP = Microsoft.UI.Xaml.Hosting.ElementCompositionPreview;
479
+ const c = ECP.GetElementVisual(host).Compositor;
480
+ const drop = c.CreateDropShadow();
481
+ drop.BlurRadius = blur;
482
+ drop.Color = shadowColor;
483
+ drop.Offset = new N.Vector3(offX, offY, 0);
484
+ // LayerVisual content isn't painted — only its DropShadow is — giving a pure soft shadow
485
+ // with no visible opaque caster edge.
486
+ const cornerR = cr > 0 ? cr + spread : 0;
487
+ const layer = c.CreateLayerVisual();
488
+ layer.Size = new N.Vector2(cw, chh);
489
+ layer.Offset = new N.Vector3(-spread, -spread, 0);
490
+ const shapeVisual = c.CreateShapeVisual();
491
+ shapeVisual.Size = new N.Vector2(cw, chh);
492
+ const geo = c.CreateRoundedRectangleGeometry();
493
+ geo.Size = new N.Vector2(cw, chh);
494
+ geo.CornerRadius = new N.Vector2(cornerR, cornerR);
495
+ const shape = c.CreateSpriteShape(geo);
496
+ const fb = c.CreateColorBrush();
497
+ fb.Color = casterColor;
498
+ shape.FillBrush = fb;
499
+ shapeVisual.Shapes.Append(shape);
500
+ layer.Children.InsertAtTop(shapeVisual);
501
+ layer.Shadow = drop;
502
+ ECP.SetElementChildVisual(host, layer);
503
+ return { host, layer, shapeVisual, geo };
504
+ }
505
+ catch (_e) {
506
+ return null;
507
+ }
508
+ }
509
+ _removeShadowImages() {
510
+ const count = this._shadowImages.length;
511
+ this._shadowImages = [];
512
+ this._shadowEntries = [];
513
+ if (count <= 0) {
514
+ return;
515
+ }
516
+ // Remove by position, not identity: collection has no Remove()/IndexOf; GetAt returns a fresh
517
+ // JS wrapper per call so `=== img` is false. Only `GetAt(i) === el` is stable. Shadow images
518
+ // are the `count` siblings immediately before the element — find el, RemoveAt the slots before it.
519
+ const kids = this._shadowKids;
520
+ const el = this._element;
521
+ if (!kids || typeof kids.Size !== 'number' || !el) {
522
+ return;
523
+ }
524
+ try {
525
+ let elIdx = -1;
526
+ for (let i = 0; i < kids.Size; i++) {
527
+ if (kids.GetAt(i) === el) {
528
+ elIdx = i;
529
+ break;
530
+ }
531
+ }
532
+ if (elIdx < 0) {
533
+ return;
534
+ }
535
+ for (let k = 1; k <= count; k++) {
536
+ const idx = elIdx - k;
537
+ if (idx >= 0) {
538
+ try {
539
+ kids.RemoveAt(idx);
540
+ }
541
+ catch (_e) { }
542
+ }
543
+ }
544
+ }
545
+ catch (_e) { }
546
+ }
547
+ // Per-side ("colorful") border drawn as a bitmap overlay on top of the element. Pass null to clear.
548
+ UpdateColorfulBorder(b) {
549
+ const el = this._element;
550
+ if (!el) {
551
+ return;
552
+ }
553
+ try {
554
+ if (!b || (b.tW <= 0 && b.rW <= 0 && b.bW <= 0 && b.lW <= 0)) {
555
+ if (this._lastBorderSig === 'none') {
556
+ return;
557
+ }
558
+ this._lastBorderSig = 'none';
559
+ this._removeBorderOverlay();
560
+ return;
561
+ }
562
+ // Not laid out yet — do NOT touch the existing overlay; a transient w=0 pass must not
563
+ // tear down a valid overlay (would flicker/vanish).
564
+ if (b.w <= 0 || b.h <= 0) {
565
+ return;
566
+ }
567
+ const sig = `${Math.round(b.w)}x${Math.round(b.h)}|${b.radius}|${b.tW},${b.rW},${b.bW},${b.lW}|${b.tC},${b.rC},${b.bC},${b.lC}`;
568
+ if (sig === this._lastBorderSig && this._borderOverlayActive) {
569
+ return;
570
+ }
571
+ this._lastBorderSig = sig;
572
+ this._removeBorderOverlay();
573
+ const parent = el.Parent;
574
+ const kids = parent?.Children;
575
+ if (!kids || typeof kids.Size !== 'number') {
576
+ return;
577
+ }
578
+ if (typeof parent.Orientation !== 'undefined') {
579
+ return; // StackPanel — overlay positioning not supported
580
+ }
581
+ const result = NativeScript.Widgets.ShadowHelper.CreateBorder(b.w, b.h, b.tW, b.rW, b.bW, b.lW, b.tC, b.rC, b.bC, b.lC, b.radius);
582
+ const img = result?.Image;
583
+ if (!img) {
584
+ return;
585
+ }
586
+ try {
587
+ img.HorizontalAlignment = el.HorizontalAlignment;
588
+ img.VerticalAlignment = el.VerticalAlignment;
589
+ const G = Microsoft.UI.Xaml.Controls.Grid;
590
+ G.SetRow(img, G.GetRow(el));
591
+ G.SetColumn(img, G.GetColumn(el));
592
+ G.SetRowSpan(img, G.GetRowSpan(el));
593
+ G.SetColumnSpan(img, G.GetColumnSpan(el));
594
+ }
595
+ catch (_e) { }
596
+ // Insert immediately AFTER the element (just above it in z-order).
597
+ let elIdx = -1;
598
+ for (let i = 0; i < kids.Size; i++) {
599
+ if (kids.GetAt(i) === el) {
600
+ elIdx = i;
601
+ break;
602
+ }
603
+ }
604
+ if (elIdx < 0 || elIdx + 1 >= kids.Size) {
605
+ kids.Append(img);
606
+ }
607
+ else {
608
+ kids.InsertAt(elIdx + 1, img);
609
+ }
610
+ this._borderKids = kids;
611
+ this._borderOverlayActive = true;
612
+ }
613
+ catch (_e) { }
614
+ }
615
+ _removeBorderOverlay() {
616
+ if (!this._borderOverlayActive) {
617
+ return;
618
+ }
619
+ this._borderOverlayActive = false;
620
+ const kids = this._borderKids;
621
+ const el = this._element;
622
+ if (!kids || typeof kids.Size !== 'number' || !el) {
623
+ return;
624
+ }
625
+ try {
626
+ // Overlay sits immediately after the element (or last if appended). Position-based removal
627
+ // since wrapper identity for our Image is unreliable (fresh wrapper per GetAt).
628
+ let elIdx = -1;
629
+ for (let i = 0; i < kids.Size; i++) {
630
+ if (kids.GetAt(i) === el) {
631
+ elIdx = i;
632
+ break;
633
+ }
634
+ }
635
+ if (elIdx >= 0 && elIdx + 1 < kids.Size) {
636
+ kids.RemoveAt(elIdx + 1);
637
+ }
638
+ else if (kids.Size > 0) {
639
+ kids.RemoveAt(kids.Size - 1); // was appended as last
640
+ }
641
+ }
642
+ catch (_e) { }
643
+ }
644
+ Free() {
645
+ this._removeShadowImages();
646
+ this._removeBorderOverlay();
647
+ if (this._element) {
648
+ try {
649
+ Microsoft.UI.Xaml.Hosting.ElementCompositionPreview.SetElementChildVisual(this._element, null);
650
+ }
651
+ catch (_e) { }
652
+ }
653
+ this._element = null;
654
+ this._rootVisual = null;
655
+ this._container = null;
656
+ this._top = null;
657
+ this._bottom = null;
658
+ this._left = null;
659
+ this._right = null;
660
+ this._topBrush = null;
661
+ this._bottomBrush = null;
662
+ this._leftBrush = null;
663
+ this._rightBrush = null;
664
+ }
665
+ }
666
+ export class View extends ViewCommon {
667
+ constructor() {
668
+ super(...arguments);
669
+ this._nativeBackgroundState = 'invalid';
670
+ this._percentWidth = null; // null = not set
671
+ this._percentHeight = null;
672
+ // Held SizeChanged delegate (prevents GC — see _ensureSizeWatch).
673
+ this._sizeChangedDelegate = null;
674
+ this._sizeWatchWired = false;
675
+ this._sizeRedrawPending = false;
676
+ // Background-application caches — prevent redundant WinRT calls across repeated _redrawNativeBackground calls.
677
+ // IMPORTANT: all sigs are value-based (not object-reference-based) because Background uses a clone pattern:
678
+ // every withBorderWidth/withBorderRadius/withColor call creates a new object, so reference equality
679
+ // is always false and would never skip anything.
680
+ this._lastStaticSig = null; // skip color/gradient rebuild when color+image unchanged
681
+ this._lastRadiusSig = null; // skip CornerRadius set when unchanged
682
+ this._lastBorderSig = null; // skip BorderThickness/UpdateBorder when unchanged
683
+ this._lastSizeDepSig = null; // skip ALL of Phase 2 when no size-dependent input changed (color-only rebind)
684
+ this._isNativeButton = null; // cached once in initNativeView — avoids per-call 'in' check
685
+ this._lastMarginSig = null; // coalesce the 4 individual margin setNative calls into 1 WinRT set
686
+ this._lastPaddingSig = null; // coalesce the 4 individual padding setNative calls into 1 WinRT set
687
+ this._lastClipSig = null; // skip clip-path rebuild when shape+dims unchanged
688
+ this._lastTileSig = null; // skip background-repeat tile rebuild when image+repeat+size unchanged
689
+ this._tileBytesCache = new Map(); // url → encoded source bytes (read once)
690
+ this._colorAnimBrush = null; // the current SolidColorBrush used as Background; lets animation reuse it without QI
691
+ // Last layout size we reacted to. LayoutUpdated fires on every layout pass (including scrolling);
692
+ // tracking size avoids expensive _onSizeChanged (tree mutation via box-shadow/border Images) when unchanged.
693
+ this._lastLayoutW = NaN;
694
+ this._lastLayoutH = NaN;
695
+ }
696
+ measure(widthMeasureSpec, heightMeasureSpec) {
697
+ super.measure(widthMeasureSpec, heightMeasureSpec);
698
+ this.onMeasure(widthMeasureSpec, heightMeasureSpec);
699
+ }
700
+ layout(left, top, right, bottom, setFrame = true) {
701
+ super.layout(left, top, right, bottom);
702
+ if (setFrame) {
703
+ this.layoutNativeView(left, top, right, bottom);
704
+ }
705
+ this.onLayout(left, top, right, bottom);
706
+ }
707
+ onMeasure(widthMeasureSpec, heightMeasureSpec) {
708
+ const view = this.nativeViewProtected;
709
+ const width = layout.getMeasureSpecSize(widthMeasureSpec);
710
+ const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
711
+ const height = layout.getMeasureSpecSize(heightMeasureSpec);
712
+ const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
713
+ let nativeWidth = 0;
714
+ let nativeHeight = 0;
715
+ if (view) {
716
+ const nativeSize = layout.measureNativeView(view, width, widthMode, height, heightMode);
717
+ nativeWidth = nativeSize.width;
718
+ nativeHeight = nativeSize.height;
719
+ }
720
+ const measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
721
+ const measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
722
+ const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
723
+ const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
724
+ this.setMeasuredDimension(widthAndState, heightAndState);
725
+ }
726
+ onLayout(_left, _top, _right, _bottom) {
727
+ // Native panels (StackPanel, Grid, etc.) position children; Canvas layouts set positions in their own handlers.
728
+ }
729
+ layoutNativeView(left, top, right, bottom) {
730
+ const native = this.nativeViewProtected;
731
+ if (!native) {
732
+ return;
733
+ }
734
+ const w = layout.toDeviceIndependentPixels(right - left);
735
+ const h = layout.toDeviceIndependentPixels(bottom - top);
736
+ const l = layout.toDeviceIndependentPixels(left);
737
+ const t = layout.toDeviceIndependentPixels(top);
738
+ try {
739
+ if (native.Parent) {
740
+ Microsoft.UI.Xaml.Controls.Canvas.SetLeft(native, l);
741
+ Microsoft.UI.Xaml.Controls.Canvas.SetTop(native, t);
742
+ }
743
+ }
744
+ catch (_e) { }
745
+ try {
746
+ if (w > 0) {
747
+ native.Width = w;
748
+ }
749
+ if (h > 0) {
750
+ native.Height = h;
751
+ }
752
+ }
753
+ catch (_e) { }
754
+ }
755
+ _redrawNativeBackground(value) {
756
+ const native = this.nativeViewProtected;
757
+ if (!native)
758
+ return;
759
+ const background = value;
760
+ if (!(background && typeof background === 'object')) {
761
+ return;
762
+ }
763
+ // Wire size watcher for size-dependent backgrounds (border/shadow/clip/radius/image). Not wired
764
+ // on every view so plain views skip SizeChanged and fast scroll stays cheap.
765
+ const depends = !!((background.borderTopWidth || background.borderRightWidth || background.borderBottomWidth || background.borderLeftWidth) ||
766
+ (background.clipPath) ||
767
+ (background.image && background.image !== 'none') ||
768
+ (typeof background.hasBorderRadius === 'function' && background.hasBorderRadius()) ||
769
+ (typeof background.hasBoxShadows === 'function' && background.hasBoxShadows()));
770
+ if (depends) {
771
+ this._ensureSizeWatch();
772
+ }
773
+ // ── Phase 1: Static background (color / gradient / image) ────────────────────────────────
774
+ // Sig encodes just color+image (the only static parts); border/radius/shadow are size-dependent
775
+ // and handled in Phase 2 with their own per-value sigs. Key on pure-JS `color.argb`, not
776
+ // `color.windowsArgb` (which calls ColorHelper.FromArgb() + 4 WinRT struct reads on every call).
777
+ const _colorArgb = background.color?.argb ?? 0;
778
+ let _imageKey = '';
779
+ if (background.image) {
780
+ if (typeof background.image === 'string') {
781
+ _imageKey = background.image;
782
+ }
783
+ else {
784
+ // Gradient: key on its actual content (angle + stops) so switching between gradients
785
+ // invalidates the cache. A constant 'lg' here would skip the brush rebuild.
786
+ const lg = background.image;
787
+ const stops = (lg.colorStops || []).map((s) => `${s.color?.argb ?? ''}@${s.offset?.value ?? ''}`).join(',');
788
+ _imageKey = `lg:${lg.angle ?? ''}:${stops}`;
789
+ }
790
+ }
791
+ // Include position/repeat/size so changes to those (without color/image change) still trigger a redraw.
792
+ const _staticSig = `${_colorArgb}|${_imageKey}|${background.repeat || ''}|${background.position || ''}|${background.size || ''}`;
793
+ // Save prior sig BEFORE updating — used below to detect first call (fresh view).
794
+ const _prevStaticSig = this._lastStaticSig;
795
+ if (_staticSig !== this._lastStaticSig) {
796
+ this._lastStaticSig = _staticSig;
797
+ // Only invalidate Phase-2 (radius/border) sigs on the FIRST draw of a fresh element, when the
798
+ // composition handler / native border may not exist yet. On a later color-only change Phase 2's
799
+ // own per-value sigs already skip unchanged radius/border, so resetting here would be wasted work.
800
+ if (_prevStaticSig === null) {
801
+ this._lastRadiusSig = null;
802
+ this._lastBorderSig = null;
803
+ }
804
+ if (background.image && typeof background.image === 'object' && 'colorStops' in background.image) {
805
+ try {
806
+ const lg = background.image;
807
+ const cs = lg.colorStops || [];
808
+ // Create brush first to access its built-in GradientStops collection
809
+ // (GradientStopCollection is not directly constructable in WinRT JS projection).
810
+ const brush = new Microsoft.UI.Xaml.Media.LinearGradientBrush();
811
+ const stopsCol = brush.GradientStops;
812
+ for (let i = 0; i < cs.length; i++) {
813
+ const entry = cs[i];
814
+ const stop = new Microsoft.UI.Xaml.Media.GradientStop();
815
+ const resolvedColor = _resolveToWinColor(entry.color);
816
+ stop.Color = resolvedColor ?? Windows.UI.Colors.Transparent;
817
+ if (entry.offset && typeof entry.offset.value === 'number') {
818
+ stop.Offset = entry.offset.value;
819
+ }
820
+ else {
821
+ stop.Offset = cs.length > 1 ? i / (cs.length - 1) : 0;
822
+ }
823
+ stopsCol.Append(stop);
824
+ }
825
+ // angle: 0 = top-to-bottom, 90deg = left-to-right (NS convention matches CSS)
826
+ const angleRad = typeof lg.angle === 'number' ? lg.angle : 0;
827
+ const dx = Math.sin(angleRad);
828
+ const dy = -Math.cos(angleRad);
829
+ brush.StartPoint = Microsoft.UI.Xaml.PointHelper.FromCoordinates(0.5 - dx / 2, 0.5 - dy / 2);
830
+ brush.EndPoint = Microsoft.UI.Xaml.PointHelper.FromCoordinates(0.5 + dx / 2, 0.5 + dy / 2);
831
+ brush.MappingMode = Microsoft.UI.Xaml.Media.BrushMappingMode.RelativeToBoundingBox;
832
+ native.Background = brush;
833
+ this._colorAnimBrush = null; // background is now a gradient — no reusable solid brush
834
+ }
835
+ catch (_e) { /* fallback to solid color below */ }
836
+ }
837
+ else if (background.image && typeof background.image === 'string' && background.image !== 'none') {
838
+ // Raster background-image: url(...). A repeating background needs the element's pixel
839
+ // size to build the tiled bitmap, so it's deferred to the size-dependent phase below;
840
+ // here we only handle the non-repeating case via a plain ImageBrush.
841
+ this._colorAnimBrush = null; // background is now an image — no reusable solid brush
842
+ if (!_isTilingRepeat(background.repeat)) {
843
+ this._applyBackgroundImage(native, background.image, background);
844
+ }
845
+ }
846
+ else if (background && background.color) {
847
+ const winColor = _resolveToWinColor(background.color);
848
+ if (winColor) {
849
+ try {
850
+ if (this._colorAnimBrush) {
851
+ // Reuse the existing solid brush: mutate its Color instead of allocating a new
852
+ // SolidColorBrush + reassigning native.Background (which re-triggers the whole
853
+ // background pipeline + a fresh repaint). Button Resources point at this same
854
+ // brush instance, so they update automatically — no re-Insert needed.
855
+ this._colorAnimBrush.Color = winColor;
856
+ }
857
+ else {
858
+ const brush = new Microsoft.UI.Xaml.Media.SolidColorBrush(winColor);
859
+ native.Background = brush;
860
+ // Cache the brush so later colour changes (and animations) reuse it.
861
+ this._colorAnimBrush = brush;
862
+ // For Button controls the default XAML template ignores Background via its VSM
863
+ // Normal-state animation. Override the ButtonBackground theme resource on the
864
+ // instance so the template binding picks up our colour. (Only for actual Buttons —
865
+ // Resources.Insert on a non-Button creates a useless ResourceDictionary.)
866
+ if (this._isNativeButton) {
867
+ try {
868
+ native.Resources.Insert('ButtonBackground', brush);
869
+ }
870
+ catch (_re) { }
871
+ try {
872
+ native.Resources.Insert('ButtonBackgroundPointerOver', brush);
873
+ }
874
+ catch (_re) { }
875
+ try {
876
+ native.Resources.Insert('ButtonBackgroundPressed', brush);
877
+ }
878
+ catch (_re) { }
879
+ }
880
+ }
881
+ }
882
+ catch (bgErr) {
883
+ console.error('[Windows] Background SolidColorBrush failed:', bgErr);
884
+ }
885
+ }
886
+ else {
887
+ // Transparent color (winColor = null) — XAML default Background is null for non-buttons.
888
+ this._colorAnimBrush = null;
889
+ if (this._isNativeButton || _prevStaticSig !== null) {
890
+ native.Background = null;
891
+ }
892
+ }
893
+ }
894
+ else {
895
+ // No color, no image — same result as transparent.
896
+ this._colorAnimBrush = null;
897
+ // Skip null-set on fresh non-button views (XAML default is already null).
898
+ if (this._isNativeButton || _prevStaticSig !== null) {
899
+ native.Background = null;
900
+ }
901
+ }
902
+ }
903
+ // ── Phase 2: Size-dependent work (composition, border, radius, shadow) ───────────────────
904
+ this._applySizeDependentNativeBackground(native, background);
905
+ }
906
+ // Size-dependent half of background rendering — called from both _redrawNativeBackground
907
+ // (full redraw) and _onSizeChanged (size-change-only redraw, skips Phase 1 above).
908
+ _applySizeDependentNativeBackground(native, background) {
909
+ let radius = 0;
910
+ if (typeof background.getUniformBorderRadius === 'function') {
911
+ radius = background.getUniformBorderRadius();
912
+ }
913
+ // Read border-color argb early (pure JS, no WinRT) — used for both needsComposition and
914
+ // the border sig / native XAML path below.
915
+ // Default an unset border-color to opaque black on any side that has a width — matches CSS
916
+ // (initial border-color resolves to currentColor → black here) and iOS/Android, which both
917
+ // fall back to black. Without this, a width-only border renders invisible on Windows.
918
+ const tCArgb = background.borderTopColor?.argb ?? (background.borderTopWidth ? 0xff000000 : 0);
919
+ const rCArgb = background.borderRightColor?.argb ?? (background.borderRightWidth ? 0xff000000 : 0);
920
+ const bCArgb = background.borderBottomColor?.argb ?? (background.borderBottomWidth ? 0xff000000 : 0);
921
+ const lCArgb = background.borderLeftColor?.argb ?? (background.borderLeftWidth ? 0xff000000 : 0);
922
+ const _anyBorderWidth = !!(background.borderTopWidth || background.borderRightWidth || background.borderBottomWidth || background.borderLeftWidth);
923
+ // Per-side different colors → XAML BorderBrush (single color) can't handle it.
924
+ const _hasNonUniformBorderColor = _anyBorderWidth && (tCArgb !== rCArgb || tCArgb !== bCArgb || tCArgb !== lCArgb);
925
+ const _hasBoxShadow = typeof background.hasBoxShadows === 'function' && background.hasBoxShadows();
926
+ // Whether XAML has native BorderThickness + BorderBrush on this element.
927
+ // Controls (Button, TextBox, etc.) and Border do; layout panels (Grid, StackPanel) do NOT.
928
+ // `'BorderThickness' in native` is a free prototype check — no WinRT getter invoked.
929
+ const _nativeHasBorderSupport = 'BorderThickness' in native;
930
+ // Only create the Compositor handler for features XAML can't handle natively:
931
+ // • box-shadows (no XAML equivalent at the NativeScript level)
932
+ // • per-side different border colors (XAML BorderBrush is a single uniform color)
933
+ // • layout panels: Grid/StackPanel/etc. have no BorderThickness, so they need Compositor
934
+ // for any border regardless of uniformity
935
+ // Controls with uniform-color borders are handled natively via BorderThickness + BorderBrush
936
+ // + CornerRadius — no WinRT Compositor overhead (~20 WinRT calls per element) needed.
937
+ const needsComposition = !!(_hasBoxShadow || _hasNonUniformBorderColor || (_anyBorderWidth && !_nativeHasBorderSupport));
938
+ if (needsComposition && !this._viewCompositionHandler) {
939
+ this._viewCompositionHandler = CompositionBorderHandler.Create(native);
940
+ }
941
+ // Size-dependent sig guard. Native-XAML borders (CornerRadius + BorderThickness) re-lay-out
942
+ // automatically on resize, so the element's pixel size is NOT an input for them; only composition
943
+ // (clip/shadow geometry) consumes the live size. So fold size into the sig ONLY when
944
+ // needsComposition (or a clip-path) is in play; otherwise a color-only change (Phase 1 already
945
+ // swapped the brush) leaves every input identical and the whole phase collapses to a compare + return.
946
+ let _szSig = '';
947
+ // clip-path geometry is also size-driven (see the clip block below), so the live size is an
948
+ // input whenever EITHER composition or a clip-path is in play — not just needsComposition.
949
+ if (needsComposition || background.clipPath) {
950
+ try {
951
+ _szSig = `${Math.round(native.ActualWidth || 0)}x${Math.round(native.ActualHeight || 0)}`;
952
+ }
953
+ catch (_e) {
954
+ /* ActualWidth/Height not realized yet — treated as 0x0, recomputed once it lays out */
955
+ }
956
+ }
957
+ const _sizeDepSig = `${radius}|${background.borderTopLeftRadius || 0},${background.borderTopRightRadius || 0},${background.borderBottomRightRadius || 0},${background.borderBottomLeftRadius || 0}|${tCArgb},${rCArgb},${bCArgb},${lCArgb}|${background.borderTopWidth || 0},${background.borderRightWidth || 0},${background.borderBottomWidth || 0},${background.borderLeftWidth || 0}|${_hasBoxShadow ? 1 : 0}|${needsComposition ? 1 : 0}|${background.clipPath || ''}|${_szSig}`;
958
+ if (_sizeDepSig === this._lastSizeDepSig) {
959
+ return;
960
+ }
961
+ this._lastSizeDepSig = _sizeDepSig;
962
+ // Borders/radius via native XAML CornerRadius. Use `'CornerRadius' in native` (JS prototype
963
+ // chain check, no getter invocation) instead of `typeof native.CornerRadius !== 'undefined'`
964
+ // (which would invoke the WinRT getter). Sig-guard skips the WinRT set when nothing changed.
965
+ const tlr = background.borderTopLeftRadius || 0;
966
+ const trr = background.borderTopRightRadius || 0;
967
+ const brr = background.borderBottomRightRadius || 0;
968
+ const blr = background.borderBottomLeftRadius || 0;
969
+ const radiusSig = `${radius}|${tlr}|${trr}|${brr}|${blr}`;
970
+ if (radiusSig !== this._lastRadiusSig) {
971
+ this._lastRadiusSig = radiusSig;
972
+ try {
973
+ if ('CornerRadius' in native) {
974
+ if (radius > 0) {
975
+ const radiusDp = layout.toDeviceIndependentPixels(radius);
976
+ native.CornerRadius = Microsoft.UI.Xaml.CornerRadiusHelper.FromUniformRadius(radiusDp);
977
+ }
978
+ else {
979
+ native.CornerRadius = Microsoft.UI.Xaml.CornerRadiusHelper.FromRadii(layout.toDeviceIndependentPixels(tlr), layout.toDeviceIndependentPixels(trr), layout.toDeviceIndependentPixels(brr), layout.toDeviceIndependentPixels(blr));
980
+ }
981
+ }
982
+ }
983
+ catch (_e) { }
984
+ }
985
+ // Border rendering: XAML-native path (BorderThickness + BorderBrush) or composition path.
986
+ // Only access native.ActualWidth/Height when a composition handler actually exists —
987
+ // those are WinRT getter calls and are wasted on elements with no handler.
988
+ // (tCArgb / rCArgb / bCArgb / lCArgb computed above before needsComposition check.)
989
+ try {
990
+ const lW = layout.toDeviceIndependentPixels(background.borderLeftWidth || 0);
991
+ const tW = layout.toDeviceIndependentPixels(background.borderTopWidth || 0);
992
+ const rW = layout.toDeviceIndependentPixels(background.borderRightWidth || 0);
993
+ const bW = layout.toDeviceIndependentPixels(background.borderBottomWidth || 0);
994
+ const anyWidth = lW > 0 || tW > 0 || rW > 0 || bW > 0;
995
+ const radiusDp = layout.toDeviceIndependentPixels(radius || 0);
996
+ // Border config sig excludes size — size is only needed inside UpdateBorder (lazy).
997
+ // Including size would force ActualWidth/Height reads even for elements with no handler.
998
+ const borderConfigSig = `${tW},${rW},${bW},${lW},${tCArgb},${rCArgb},${bCArgb},${lCArgb},${radiusDp}`;
999
+ if (borderConfigSig !== this._lastBorderSig) {
1000
+ this._lastBorderSig = borderConfigSig;
1001
+ if (!this._viewCompositionHandler) {
1002
+ // XAML-native path: uniform-color border via BorderThickness + BorderBrush.
1003
+ // CornerRadius already set above. No WinRT Compositor objects needed.
1004
+ // `'BorderThickness' in native` is a free prototype check (no WinRT getter).
1005
+ try {
1006
+ if ('BorderThickness' in native) {
1007
+ if (anyWidth) {
1008
+ native.BorderThickness = { Left: lW, Top: tW, Right: rW, Bottom: bW };
1009
+ // All 4 colors are equal (enforced by needsComposition → use top as canonical).
1010
+ const borderBrush = tCArgb
1011
+ ? new Microsoft.UI.Xaml.Media.SolidColorBrush(_argbToWinColor(tCArgb))
1012
+ : null;
1013
+ native.BorderBrush = borderBrush;
1014
+ // Button VSM overrides BorderBrush via {ThemeResource ButtonBorderBrush}.
1015
+ // Insert into the instance ResourceDictionary so the binding resolves our value.
1016
+ if (this._isNativeButton && borderBrush) {
1017
+ try {
1018
+ native.Resources.Insert('ButtonBorderBrush', borderBrush);
1019
+ }
1020
+ catch (_re) { }
1021
+ try {
1022
+ native.Resources.Insert('ButtonBorderBrushPointerOver', borderBrush);
1023
+ }
1024
+ catch (_re) { }
1025
+ try {
1026
+ native.Resources.Insert('ButtonBorderBrushPressed', borderBrush);
1027
+ }
1028
+ catch (_re) { }
1029
+ }
1030
+ }
1031
+ else {
1032
+ native.BorderThickness = { Left: 0, Top: 0, Right: 0, Bottom: 0 };
1033
+ native.BorderBrush = null;
1034
+ }
1035
+ }
1036
+ }
1037
+ catch (_e) { }
1038
+ }
1039
+ else {
1040
+ // Composition path: clear native XAML border (composition draws its own).
1041
+ try {
1042
+ if ('BorderThickness' in native) {
1043
+ native.BorderThickness = { Left: 0, Top: 0, Right: 0, Bottom: 0 };
1044
+ }
1045
+ }
1046
+ catch (_e) { }
1047
+ this._viewCompositionHandler?.UpdateColorfulBorder(null);
1048
+ }
1049
+ }
1050
+ // ActualWidth/Height reads gated inside the anyWidth branch — shadow-only handlers never
1051
+ // pay these 2 WinRT getter calls. _argbToWinColor uses the module-level cache.
1052
+ if (this._viewCompositionHandler) {
1053
+ if (anyWidth) {
1054
+ const aw = native.ActualWidth || 0;
1055
+ const ah = native.ActualHeight || 0;
1056
+ this._viewCompositionHandler.UpdateBorder(aw, ah, tW, rW, bW, lW, tCArgb, rCArgb, bCArgb, lCArgb, radiusDp);
1057
+ }
1058
+ else {
1059
+ this._viewCompositionHandler.ClearBorder();
1060
+ }
1061
+ }
1062
+ }
1063
+ catch (_e) { }
1064
+ this._nativeBackgroundState = 'drawn';
1065
+ if (this._viewCompositionHandler) {
1066
+ const hasShadow = background && typeof background.hasBoxShadows === 'function' && background.hasBoxShadows();
1067
+ if (hasShadow) {
1068
+ const boxShadows = typeof background.getBoxShadows === 'function' ? background.getBoxShadows() : background.boxShadows;
1069
+ this._viewCompositionHandler.UpdateBoxShadow(boxShadows?.length ? boxShadows : null, layout.toDeviceIndependentPixels(radius || 0));
1070
+ }
1071
+ else {
1072
+ this._viewCompositionHandler.UpdateBoxShadow(null);
1073
+ }
1074
+ }
1075
+ // Clip-path via Composition Visual.Clip — rect, inset, circle, ellipse, polygon.
1076
+ const clipPath = background.clipPath;
1077
+ if (clipPath) {
1078
+ try {
1079
+ const aw = native.ActualWidth || 0;
1080
+ const ah = native.ActualHeight || 0;
1081
+ const sig = String(clipPath) + `|${aw}|${ah}`;
1082
+ if (sig !== this._lastClipSig) {
1083
+ this._lastClipSig = sig;
1084
+ this._applyClipGeometry(native, clipPath instanceof ClipPathFunction ? clipPath : null, aw, ah);
1085
+ }
1086
+ }
1087
+ catch (_e) { }
1088
+ }
1089
+ else if (this._lastClipSig !== null) {
1090
+ this._lastClipSig = null;
1091
+ try {
1092
+ NativeScript.Widgets.ClipHelper.ClearClip(native);
1093
+ }
1094
+ catch (_e) { }
1095
+ }
1096
+ // CSS background-repeat tiling. WinUI's ImageBrush can't tile, so the C++ TileHelper renders a
1097
+ // bitmap tiled to the element's pixel size and we use that as the brush. Size-dependent, so it
1098
+ // regenerates on resize; sig-guarded to skip when image/repeat/size are unchanged.
1099
+ const bgImage = background.image;
1100
+ if (typeof bgImage === 'string' && bgImage && bgImage !== 'none' && _isTilingRepeat(background.repeat)) {
1101
+ const aw = native.ActualWidth || 0;
1102
+ const ah = native.ActualHeight || 0;
1103
+ if (aw > 0 && ah > 0) {
1104
+ const density = layout.getDisplayDensity?.() || 1;
1105
+ const wPx = Math.max(1, Math.round(aw * density));
1106
+ const hPx = Math.max(1, Math.round(ah * density));
1107
+ const repeat = String(background.repeat).toLowerCase();
1108
+ const tileSig = `${bgImage}|${repeat}|${wPx}x${hPx}`;
1109
+ if (tileSig !== this._lastTileSig) {
1110
+ this._lastTileSig = tileSig;
1111
+ this._applyTiledBackgroundImage(native, bgImage, repeat, wPx, hPx);
1112
+ }
1113
+ }
1114
+ }
1115
+ else if (this._lastTileSig !== null) {
1116
+ this._lastTileSig = null;
1117
+ }
1118
+ }
1119
+ // Resolves a CSS background-image url to a path that ImageHelper.ReadFileAsync can read
1120
+ // (ms-appx:/// for ~/, res:// and bare; passthrough for ms-appx/file/http). Returns '' if unusable.
1121
+ _resolveBackgroundImagePath(src) {
1122
+ let url = (src || '').trim();
1123
+ const m = url.match(/^url\(\s*['"]?([^'")]+)['"]?\s*\)$/i);
1124
+ if (m)
1125
+ url = m[1];
1126
+ if (!url)
1127
+ return '';
1128
+ if (url.startsWith('~/'))
1129
+ return 'ms-appx:///app/' + url.slice(2);
1130
+ if (url.startsWith('res://'))
1131
+ return 'ms-appx:///Assets/' + url.slice('res://'.length);
1132
+ if (/^ms-appx/i.test(url) || /^https?:/i.test(url) || /^file:/i.test(url))
1133
+ return url;
1134
+ return url; // bare filesystem path
1135
+ }
1136
+ // Builds the tiled bitmap via the C++ TileHelper and applies it as the native Background brush.
1137
+ _applyTiledBackgroundImage(native, src, repeat, wPx, hPx) {
1138
+ const repeatX = repeat === 'repeat' || repeat === 'repeat-x';
1139
+ const repeatY = repeat === 'repeat' || repeat === 'repeat-y';
1140
+ const path = this._resolveBackgroundImagePath(src);
1141
+ if (!path)
1142
+ return;
1143
+ const applyTiled = (sourceBytes) => {
1144
+ NSWinRT.toPromise(NativeScript.Widgets.TileHelper.CreateTiledImageAsync(sourceBytes, wPx, hPx, repeatX, repeatY))
1145
+ .then((tiled) => NSWinRT.toPromise(NativeScript.Widgets.ImageHelper.LoadFromBufferAsync(tiled)))
1146
+ .then((result) => {
1147
+ const bmp = result?.Bitmap;
1148
+ if (!bmp || !native)
1149
+ return;
1150
+ // Guard against a later size/image change that superseded this async result.
1151
+ const repeatNow = String(this.style.backgroundInternal?.repeat ?? '').toLowerCase();
1152
+ if (!_isTilingRepeat(repeatNow))
1153
+ return;
1154
+ const Media = Microsoft.UI.Xaml.Media;
1155
+ const brush = new Media.ImageBrush();
1156
+ brush.ImageSource = bmp;
1157
+ // The tiled bitmap is exactly the element's pixel size, so Fill maps it 1:1.
1158
+ try {
1159
+ brush.Stretch = Media.Stretch.Fill;
1160
+ }
1161
+ catch (_e) { }
1162
+ native.Background = brush;
1163
+ try {
1164
+ native.Resources.Insert('ButtonBackground', brush);
1165
+ }
1166
+ catch (_e) { }
1167
+ try {
1168
+ native.Resources.Insert('ButtonBackgroundPointerOver', brush);
1169
+ }
1170
+ catch (_e) { }
1171
+ try {
1172
+ native.Resources.Insert('ButtonBackgroundPressed', brush);
1173
+ }
1174
+ catch (_e) { }
1175
+ })
1176
+ .catch(() => { });
1177
+ };
1178
+ const cached = this._tileBytesCache.get(path);
1179
+ if (cached) {
1180
+ applyTiled(cached);
1181
+ return;
1182
+ }
1183
+ // http(s) sources can't be read by ReadFileAsync; fetch via image-source bytes path instead.
1184
+ NSWinRT.toPromise(NativeScript.Widgets.ImageHelper.ReadFileAsync(path))
1185
+ .then((bytes) => {
1186
+ if (!bytes)
1187
+ return;
1188
+ this._tileBytesCache.set(path, bytes);
1189
+ applyTiled(bytes);
1190
+ })
1191
+ .catch(() => { });
1192
+ }
1193
+ initNativeView() {
1194
+ super.initNativeView();
1195
+ // Size-dependent work (percent sizing, border/shadow/clip redraw) is wired ON DEMAND via
1196
+ // _ensureSizeWatch(). SizeChanged fires for every recycled cell + child during fast scroll;
1197
+ // wiring it unconditionally crossed the JS bridge per cell and blocked scrolling.
1198
+ //
1199
+ // Cache whether this is a Button-family control (gates the button-template VSM resource inserts).
1200
+ try {
1201
+ const nv = this.nativeViewProtected;
1202
+ // `in` is unreliable on COM proxy objects — use direct property access instead.
1203
+ // ClickMode is declared on ButtonBase (Button/AppBarButton/etc.) and returns 0 (Release)
1204
+ // by default; non-button controls return `undefined` for absent properties.
1205
+ this._isNativeButton = !!(nv && nv.ClickMode !== undefined);
1206
+ }
1207
+ catch (_e) {
1208
+ this._isNativeButton = false;
1209
+ }
1210
+ }
1211
+ // Applies CSS clip-path by calling the C++ NativeScript.Widgets.ClipHelper, which performs
1212
+ // strongly-typed WinRT calls that bypass V8-bridge interface-matching limitations.
1213
+ // rect/inset/circle/ellipse use a composition rounded-rect (radius=size/2 is a true ellipse);
1214
+ // polygon goes through a Direct2D path geometry wrapped in IGeometrySource2DInterop.
1215
+ _applyClipGeometry(native, clipPath, w, h) {
1216
+ try {
1217
+ if (!clipPath) {
1218
+ NativeScript.Widgets.ClipHelper.ClearClip(native);
1219
+ return;
1220
+ }
1221
+ const density = layout.getDisplayDensity() || 1;
1222
+ const dim = (v, t) => _parseClipDim(v, t, density);
1223
+ const { shape, rule } = clipPath;
1224
+ if (shape === 'rect') {
1225
+ const parts = rule.trim().split(/\s+/);
1226
+ const top = dim(parts[0] || '0', h);
1227
+ const right = dim(parts[1] || String(w), w);
1228
+ const bottom = dim(parts[2] || String(h), h);
1229
+ const left = dim(parts[3] || '0', w);
1230
+ NativeScript.Widgets.ClipHelper.ApplyRoundedRectClip(native, left, top, Math.max(0, right - left), Math.max(0, bottom - top), 0, 0);
1231
+ }
1232
+ else if (shape === 'inset') {
1233
+ const parts = rule.trim().split(/\s+/);
1234
+ const t = dim(parts[0] || '0', h);
1235
+ const r = dim(parts.length >= 2 ? parts[1] : parts[0], w);
1236
+ const b = dim(parts.length >= 3 ? parts[2] : parts[0], h);
1237
+ const l = dim(parts.length >= 4 ? parts[3] : (parts.length >= 2 ? parts[1] : parts[0]), w);
1238
+ NativeScript.Widgets.ClipHelper.ApplyRoundedRectClip(native, l, t, Math.max(0, w - l - r), Math.max(0, h - t - b), 0, 0);
1239
+ }
1240
+ else if (shape === 'circle') {
1241
+ const m = rule.trim().match(/^([^\s]+)\s+at\s+([^\s]+)\s+([^\s]+)$/i);
1242
+ // CSS circle() percentage radius resolves against min(width,height)/2 (matches iOS/Android).
1243
+ const radiusRef = Math.min(w, h) / 2;
1244
+ let radius, cx, cy;
1245
+ if (m) {
1246
+ radius = dim(m[1], radiusRef);
1247
+ cx = dim(m[2], w);
1248
+ cy = dim(m[3], h);
1249
+ }
1250
+ else {
1251
+ radius = dim(rule.trim(), radiusRef);
1252
+ cx = w / 2;
1253
+ cy = h / 2;
1254
+ }
1255
+ NativeScript.Widgets.ClipHelper.ApplyRoundedRectClip(native, cx - radius, cy - radius, radius * 2, radius * 2, radius, radius);
1256
+ }
1257
+ else if (shape === 'ellipse') {
1258
+ const m = rule.trim().match(/^([^\s]+)\s+([^\s]+)\s+at\s+([^\s]+)\s+([^\s]+)$/i);
1259
+ if (m) {
1260
+ const rx = dim(m[1], w);
1261
+ const ry = dim(m[2], h);
1262
+ const cx = dim(m[3], w);
1263
+ const cy = dim(m[4], h);
1264
+ NativeScript.Widgets.ClipHelper.ApplyRoundedRectClip(native, cx - rx, cy - ry, rx * 2, ry * 2, rx, ry);
1265
+ }
1266
+ }
1267
+ else if (shape === 'polygon') {
1268
+ // Direct2D path via C++ helper — no Win2D NuGet needed.
1269
+ NativeScript.Widgets.ClipHelper.ApplyPolygonClip(native, w, h, rule, density);
1270
+ }
1271
+ }
1272
+ catch (_e) { }
1273
+ }
1274
+ // Renders CSS background-image as a native ImageBrush. Resolves NS path forms (res://, ~/, data:,
1275
+ // http(s) async, ms-appx). background-size → Stretch, background-position → alignment. CSS tiling
1276
+ // (repeat) isn't expressible with ImageBrush; only single no-repeat is supported.
1277
+ _applyBackgroundImage(native, src, background) {
1278
+ const Media = Microsoft.UI.Xaml.Media;
1279
+ const apply = (bmp) => {
1280
+ if (!bmp || !native) {
1281
+ // Image missing / failed to decode → fall back to the element's background-color so it
1282
+ // still shows (CSS + iOS/Android parity). Phase 1 skipped the color branch because an
1283
+ // image was specified, so without this the element would render with no background.
1284
+ try {
1285
+ const winColor = background && background.color ? _resolveToWinColor(background.color) : null;
1286
+ native.Background = winColor ? new Media.SolidColorBrush(winColor) : null;
1287
+ }
1288
+ catch (_e) { }
1289
+ return;
1290
+ }
1291
+ try {
1292
+ const brush = new Media.ImageBrush();
1293
+ brush.ImageSource = bmp;
1294
+ // Map background-size → Stretch (default no-repeat shows the image at natural size).
1295
+ const size = (background.size || '').toString().toLowerCase();
1296
+ try {
1297
+ if (size === 'cover') {
1298
+ brush.Stretch = Media.Stretch.UniformToFill;
1299
+ }
1300
+ else if (size === 'contain') {
1301
+ brush.Stretch = Media.Stretch.Uniform;
1302
+ }
1303
+ else if (size === '100% 100%' || size === 'fill') {
1304
+ brush.Stretch = Media.Stretch.Fill;
1305
+ }
1306
+ else {
1307
+ brush.Stretch = Media.Stretch.None;
1308
+ }
1309
+ }
1310
+ catch (_e) { }
1311
+ // Map background-position keywords → alignment (CSS default is top-left).
1312
+ const pos = (background.position || '').toString().toLowerCase();
1313
+ try {
1314
+ brush.AlignmentX = pos.includes('right') ? Media.AlignmentX.Right : pos.includes('center') ? Media.AlignmentX.Center : Media.AlignmentX.Left;
1315
+ brush.AlignmentY = pos.includes('bottom') ? Media.AlignmentY.Bottom : pos.includes('center') ? Media.AlignmentY.Center : Media.AlignmentY.Top;
1316
+ }
1317
+ catch (_e) { }
1318
+ native.Background = brush;
1319
+ // Buttons ignore Background in their default template (VSM); override the template brush too.
1320
+ try {
1321
+ native.Resources.Insert('ButtonBackground', brush);
1322
+ }
1323
+ catch (_e) { }
1324
+ try {
1325
+ native.Resources.Insert('ButtonBackgroundPointerOver', brush);
1326
+ }
1327
+ catch (_e) { }
1328
+ try {
1329
+ native.Resources.Insert('ButtonBackgroundPressed', brush);
1330
+ }
1331
+ catch (_e) { }
1332
+ }
1333
+ catch (_e) { }
1334
+ };
1335
+ try {
1336
+ let url = (src || '').trim();
1337
+ const m = url.match(/^url\(\s*['"]?([^'")]+)['"]?\s*\)$/i);
1338
+ if (m) {
1339
+ url = m[1];
1340
+ }
1341
+ if (url.startsWith('~/')) {
1342
+ url = 'ms-appx:///app/' + url.slice(2);
1343
+ }
1344
+ if (/^data:/i.test(url)) {
1345
+ const is = ImageSource.fromBase64Sync(url.replace(/^data:[^,]*,/, ''));
1346
+ apply(is && is.windows);
1347
+ }
1348
+ else if (/^https?:/i.test(url)) {
1349
+ ImageSource.fromUrl(url).then((is) => apply(is && is.windows)).catch(() => { });
1350
+ }
1351
+ else if (url.startsWith('res://')) {
1352
+ const is = ImageSource.fromResourceSync(url);
1353
+ apply(is && is.windows);
1354
+ }
1355
+ else {
1356
+ const is = ImageSource.fromFileSync(url);
1357
+ apply(is && is.windows);
1358
+ }
1359
+ }
1360
+ catch (_e) { }
1361
+ }
1362
+ // Lazily wire SizeChanged via asDelegate (the only event that reliably subscribes in this host;
1363
+ // raw-fn LayoutUpdated does not). Idempotent. Only called for views that need it.
1364
+ _ensureSizeWatch() {
1365
+ if (this._sizeWatchWired)
1366
+ return;
1367
+ const nv = this.nativeViewProtected;
1368
+ if (!nv)
1369
+ return;
1370
+ this._sizeWatchWired = true;
1371
+ const ref = new WeakRef(this);
1372
+ const onSize = () => {
1373
+ const owner = ref.deref();
1374
+ if (!owner)
1375
+ return;
1376
+ const n = owner.nativeViewProtected;
1377
+ if (!n)
1378
+ return;
1379
+ // NOTE: _applyPercentSizing() is intentionally NOT called here synchronously.
1380
+ // Setting Width/Height from within a SizeChanged handler mutates the layout tree
1381
+ // mid-pass and triggers XAML's LayoutCycleException (0x80000003 fail-fast).
1382
+ // The deferred _onSizeChanged() path calls it safely after the layout pass ends.
1383
+ let w = NaN, h = NaN;
1384
+ try {
1385
+ w = n.ActualWidth;
1386
+ h = n.ActualHeight;
1387
+ }
1388
+ catch (_e) {
1389
+ return;
1390
+ }
1391
+ if (Math.abs(w - owner._lastLayoutW) < 0.5 && Math.abs(h - owner._lastLayoutH) < 0.5) {
1392
+ return; // size unchanged (e.g. scrolling) — nothing to redraw
1393
+ }
1394
+ owner._lastLayoutW = w;
1395
+ owner._lastLayoutH = h;
1396
+ // Coalesce rapid size changes into one deferred redraw; defer out of the layout callback
1397
+ // (tree mutation inside it risks a 0xC000027B fail-fast).
1398
+ if (owner._sizeRedrawPending)
1399
+ return;
1400
+ owner._sizeRedrawPending = true;
1401
+ setTimeout(() => { const o = ref.deref(); if (o) {
1402
+ o._sizeRedrawPending = false;
1403
+ try {
1404
+ o._onSizeChanged();
1405
+ }
1406
+ catch (_e) { }
1407
+ } }, 0);
1408
+ };
1409
+ try {
1410
+ this._sizeChangedDelegate = NSWinRT.asDelegate('Microsoft.UI.Xaml.SizeChangedEventHandler', onSize);
1411
+ nv.SizeChanged = this._sizeChangedDelegate;
1412
+ }
1413
+ catch (_e) { }
1414
+ // Apply immediately + once on load (covers percent before SizeChanged fires).
1415
+ try {
1416
+ this._applyPercentSizing();
1417
+ }
1418
+ catch (_e) { }
1419
+ }
1420
+ _easeOutCubic(t) {
1421
+ return 1 - Math.pow(1 - t, 3);
1422
+ }
1423
+ _animateNativeOpacity(nativeElem, from, to, durationMs, done) {
1424
+ try {
1425
+ nativeElem.Opacity = from;
1426
+ }
1427
+ catch (_e) { }
1428
+ const start = Date.now();
1429
+ const step = () => {
1430
+ const now = Date.now();
1431
+ const elapsed = now - start;
1432
+ const t = Math.min(1, elapsed / Math.max(1, durationMs));
1433
+ const val = from + (to - from) * this._easeOutCubic(t);
1434
+ try {
1435
+ nativeElem.Opacity = val;
1436
+ }
1437
+ catch (_e) { }
1438
+ if (t < 1) {
1439
+ if (typeof requestAnimationFrame === 'function') {
1440
+ requestAnimationFrame(step);
1441
+ }
1442
+ else {
1443
+ setTimeout(step, 16);
1444
+ }
1445
+ }
1446
+ else {
1447
+ done && done();
1448
+ }
1449
+ };
1450
+ step();
1451
+ }
1452
+ disposeNativeView() {
1453
+ const nativeView = this.nativeViewProtected;
1454
+ if (nativeView) {
1455
+ nativeView.SizeChanged = null;
1456
+ }
1457
+ // Reset so a recycled view re-wires on the next size-dependent property set.
1458
+ this._sizeWatchWired = false;
1459
+ this._sizeChangedDelegate = null;
1460
+ // Reset background caches so recycled view re-applies on next use.
1461
+ this._lastStaticSig = null;
1462
+ this._lastRadiusSig = null;
1463
+ this._lastBorderSig = null;
1464
+ this._isNativeButton = null;
1465
+ this._lastMarginSig = null;
1466
+ this._lastPaddingSig = null;
1467
+ this._colorAnimBrush = null;
1468
+ super.disposeNativeView();
1469
+ }
1470
+ _onSizeChanged() {
1471
+ const nativeView = this.nativeViewProtected;
1472
+ if (!nativeView) {
1473
+ return;
1474
+ }
1475
+ try {
1476
+ this._applyPercentSizing();
1477
+ }
1478
+ catch (_e) { }
1479
+ const background = this.style.backgroundInternal;
1480
+ // Any border width makes the background size-dependent (composition border needs measured size).
1481
+ const anyBorderWidth = !!(background && (background.borderTopWidth || background.borderRightWidth || background.borderBottomWidth || background.borderLeftWidth));
1482
+ const backgroundDependsOnSize = (background && background.image && background.image !== 'none') || (background && background.clipPath) || anyBorderWidth || (background && !background.hasUniformBorder()) || (background && background.hasBorderRadius && background.hasBorderRadius()) || (background && background.hasBoxShadows && background.hasBoxShadows());
1483
+ if (this._nativeBackgroundState === 'invalid') {
1484
+ // Full redraw needed (background property changed while view had no size yet).
1485
+ this._redrawNativeBackground(background);
1486
+ }
1487
+ else if (this._nativeBackgroundState === 'drawn' && backgroundDependsOnSize) {
1488
+ // Size changed — only re-run the size-dependent phase. The static background (brush
1489
+ // creation, native.Background=, Resources.Insert) is unchanged, so skip it.
1490
+ const native = this.nativeViewProtected;
1491
+ if (native && background && typeof background === 'object') {
1492
+ this._applySizeDependentNativeBackground(native, background);
1493
+ }
1494
+ }
1495
+ try {
1496
+ if (nativeView.Clip && nativeView.Clip instanceof Microsoft.UI.Xaml.Media.RectangleGeometry) {
1497
+ const rectGeom = nativeView.Clip;
1498
+ const w = nativeView.ActualWidth || nativeView.Width || 0;
1499
+ const h = nativeView.ActualHeight || nativeView.Height || 0;
1500
+ const location = Microsoft.UI.Xaml.PointHelper.FromCoordinates(0, 0);
1501
+ const size = Microsoft.UI.Xaml.SizeHelper.FromDimensions(w, h);
1502
+ rectGeom.Rect = Microsoft.UI.Xaml.RectHelper.FromLocationAndSize(location, size);
1503
+ }
1504
+ }
1505
+ catch (_e) { }
1506
+ }
1507
+ _applyPercentSizing() {
1508
+ const nativeView = this.nativeViewProtected;
1509
+ if (!nativeView) {
1510
+ return;
1511
+ }
1512
+ try {
1513
+ const parentNative = this.parent?.nativeViewProtected;
1514
+ const parentWidth = parentNative?.ActualWidth || 0;
1515
+ const parentHeight = parentNative?.ActualHeight || 0;
1516
+ // Only apply when parent has a real layout size. Using Window.Bounds as fallback when
1517
+ // parent is 0 causes nested height="100%" views to overflow the container and cover
1518
+ // sibling rows; LayoutUpdated fires again once the parent is properly sized.
1519
+ if (this._percentWidth != null && parentWidth > 0) {
1520
+ const w = parentWidth * (this._percentWidth);
1521
+ nativeView.Width = isFinite(w) ? w : NaN;
1522
+ }
1523
+ if (this._percentHeight != null && parentHeight > 0) {
1524
+ const h = parentHeight * (this._percentHeight);
1525
+ nativeView.Height = isFinite(h) ? h : NaN;
1526
+ }
1527
+ }
1528
+ catch (_e) { }
1529
+ }
1530
+ [backgroundInternalProperty.getDefault]() {
1531
+ const native = this.nativeViewProtected;
1532
+ if (!native)
1533
+ return null;
1534
+ try {
1535
+ return native.Background ?? null;
1536
+ }
1537
+ catch (_e) {
1538
+ return null;
1539
+ }
1540
+ }
1541
+ [backgroundInternalProperty.setNative](value) {
1542
+ this._nativeBackgroundState = 'invalid';
1543
+ this._redrawNativeBackground(value);
1544
+ }
1545
+ //@ts-ignore
1546
+ [widthProperty.setNative](value) {
1547
+ if (!this.nativeViewProtected) {
1548
+ return;
1549
+ }
1550
+ if (value && typeof value === 'object' && value.unit === '%') {
1551
+ const pct = value.value;
1552
+ this._percentWidth = pct;
1553
+ this._ensureSizeWatch(); // re-apply percent when the parent resizes
1554
+ this.nativeViewProtected.Width = NaN;
1555
+ if (pct >= 1) {
1556
+ // 100% → let the container size it natively (NativeScript.Widgets.StackLayout gives
1557
+ // a Stretch child the remaining bounded extent; Grid Stretch fills the cell).
1558
+ this.nativeViewProtected.HorizontalAlignment = 3; // Stretch
1559
+ }
1560
+ else {
1561
+ try {
1562
+ this._applyPercentSizing();
1563
+ }
1564
+ catch (_e) { }
1565
+ }
1566
+ // Feed FlexBasisPercent so the C++ FlexboxLayout panel's MeasureOverride sets
1567
+ // item.MainSize = containerWidth * pct, which triggers a line break on flexWrap:wrap.
1568
+ // pct is stored as a 0..1 fraction; the C++ property expects 0..100.
1569
+ try {
1570
+ NativeScript.Widgets.FlexboxLayout.SetFlexBasisPercent(this.nativeViewProtected, pct * 100);
1571
+ }
1572
+ catch (_e) { }
1573
+ return;
1574
+ }
1575
+ this._percentWidth = null;
1576
+ // Clear any previously set flex-basis so the C++ widget falls back to DesiredSize.
1577
+ try {
1578
+ NativeScript.Widgets.FlexboxLayout.SetFlexBasisPercent(this.nativeViewProtected, -1);
1579
+ }
1580
+ catch (_e) { }
1581
+ this.nativeViewProtected.Width = toXamlLength(value);
1582
+ }
1583
+ //@ts-ignore
1584
+ [heightProperty.setNative](value) {
1585
+ if (!this.nativeViewProtected) {
1586
+ return;
1587
+ }
1588
+ if (value && typeof value === 'object' && value.unit === '%') {
1589
+ const pct = value.value;
1590
+ this._percentHeight = pct;
1591
+ this._ensureSizeWatch(); // re-apply percent when the parent resizes
1592
+ this.nativeViewProtected.Height = NaN;
1593
+ if (pct >= 1) {
1594
+ // 100% → let the container size it natively (NativeScript.Widgets.StackLayout sizes a
1595
+ // Stretch child to remaining bounded extent; a Grid Stretch fills the cell).
1596
+ this.nativeViewProtected.VerticalAlignment = 3; // Stretch
1597
+ // VerticalAlignment is ARRANGE-only; changing it doesn't re-run MeasureOverride.
1598
+ // Force a re-measure so the panel re-evaluates this child as a fill child.
1599
+ try {
1600
+ this.nativeViewProtected.InvalidateMeasure();
1601
+ }
1602
+ catch (_e) { }
1603
+ }
1604
+ else {
1605
+ try {
1606
+ this._applyPercentSizing();
1607
+ }
1608
+ catch (_e) { }
1609
+ }
1610
+ return;
1611
+ }
1612
+ this._percentHeight = null;
1613
+ this.nativeViewProtected.Height = toXamlLength(value);
1614
+ }
1615
+ //@ts-ignore
1616
+ [minWidthProperty.setNative](value) {
1617
+ if (this.nativeViewProtected) {
1618
+ const v = toXamlLength(value);
1619
+ this.nativeViewProtected.MinWidth = isNaN(v) ? 0 : v;
1620
+ }
1621
+ }
1622
+ //@ts-ignore
1623
+ [minHeightProperty.setNative](value) {
1624
+ if (this.nativeViewProtected) {
1625
+ const v = toXamlLength(value);
1626
+ this.nativeViewProtected.MinHeight = isNaN(v) ? 0 : v;
1627
+ }
1628
+ }
1629
+ [opacityProperty.setNative](value) {
1630
+ if (this.nativeViewProtected) {
1631
+ this.nativeViewProtected.Opacity = value;
1632
+ }
1633
+ }
1634
+ [hiddenProperty.getDefault]() {
1635
+ return getVisibility(this.nativeViewProtected) !== 'visible';
1636
+ }
1637
+ [hiddenProperty.setNative](value) {
1638
+ setVisibility(this.nativeViewProtected, value ? 'hidden' : 'visible');
1639
+ }
1640
+ [visibilityProperty.setNative](value) {
1641
+ if (this.nativeViewProtected) {
1642
+ setVisibility(this.nativeViewProtected, value);
1643
+ }
1644
+ }
1645
+ //@ts-ignore
1646
+ [marginLeftProperty.setNative](_value) {
1647
+ this._applyMargin();
1648
+ }
1649
+ //@ts-ignore
1650
+ [marginTopProperty.setNative](_value) {
1651
+ this._applyMargin();
1652
+ }
1653
+ //@ts-ignore
1654
+ [marginRightProperty.setNative](_value) {
1655
+ this._applyMargin();
1656
+ }
1657
+ //@ts-ignore
1658
+ [marginBottomProperty.setNative](_value) {
1659
+ this._applyMargin();
1660
+ }
1661
+ _applyMargin() {
1662
+ if (!this.nativeViewProtected)
1663
+ return;
1664
+ try {
1665
+ const l = toXamlLength(this.style.marginLeft) || 0;
1666
+ const t = toXamlLength(this.style.marginTop) || 0;
1667
+ const r = toXamlLength(this.style.marginRight) || 0;
1668
+ const b = toXamlLength(this.style.marginBottom) || 0;
1669
+ // All 4 margin properties share this helper — CSS `margin:` decomposes into 4 separate
1670
+ // setNative calls in the same synchronous applyAllNativeSetters loop. The sig check
1671
+ // coalesces them: the first call does the WinRT set; the remaining 3 are no-ops.
1672
+ const sig = `${l},${t},${r},${b}`;
1673
+ if (sig === this._lastMarginSig)
1674
+ return;
1675
+ const prev = this._lastMarginSig;
1676
+ this._lastMarginSig = sig;
1677
+ // Skip the WinRT call on the very first zero-margin assignment: XAML default Margin is
1678
+ // already {0,0,0,0}, so setting it is a no-op. Only safe when prev===null (native never
1679
+ // touched); if margin was previously non-zero and is now reset to 0, we still call WinRT.
1680
+ if (l === 0 && t === 0 && r === 0 && b === 0 && prev === null)
1681
+ return;
1682
+ // Thickness is a plain value struct {Left,Top,Right,Bottom: f64} — pass as a plain JS
1683
+ // object via the bridge's append_struct_object_bytes path (same as Windows.UI.Color).
1684
+ // Saves the ThicknessHelper.FromLengths WinRT static call on every margin change.
1685
+ this.nativeViewProtected.Margin = { Left: l, Top: t, Right: r, Bottom: b };
1686
+ }
1687
+ catch (_e) { }
1688
+ }
1689
+ //@ts-ignore
1690
+ [paddingTopProperty.setNative](_value) { this._applyPadding(); }
1691
+ //@ts-ignore
1692
+ [paddingRightProperty.setNative](_value) { this._applyPadding(); }
1693
+ //@ts-ignore
1694
+ [paddingBottomProperty.setNative](_value) { this._applyPadding(); }
1695
+ //@ts-ignore
1696
+ [paddingLeftProperty.setNative](_value) { this._applyPadding(); }
1697
+ _applyPadding() {
1698
+ const native = this.nativeViewProtected;
1699
+ if (!native)
1700
+ return;
1701
+ try {
1702
+ const l = toXamlLength(this.style.paddingLeft) || 0;
1703
+ const t = toXamlLength(this.style.paddingTop) || 0;
1704
+ const r = toXamlLength(this.style.paddingRight) || 0;
1705
+ const b = toXamlLength(this.style.paddingBottom) || 0;
1706
+ const sig = `${l},${t},${r},${b}`;
1707
+ if (sig === this._lastPaddingSig)
1708
+ return;
1709
+ const prev = this._lastPaddingSig;
1710
+ this._lastPaddingSig = sig;
1711
+ // XAML default Padding is {0,0,0,0} — skip the WinRT call on the first zero-padding set.
1712
+ if (l === 0 && t === 0 && r === 0 && b === 0 && prev === null)
1713
+ return;
1714
+ native.Padding = { Left: l, Top: t, Right: r, Bottom: b };
1715
+ }
1716
+ catch (_e) { }
1717
+ }
1718
+ //@ts-ignore
1719
+ [horizontalAlignmentProperty.setNative](value) {
1720
+ const native = this.nativeViewProtected;
1721
+ if (!native)
1722
+ return;
1723
+ // WinUI HorizontalAlignment: Left=0, Center=1, Right=2, Stretch=3
1724
+ switch (value) {
1725
+ case 'left':
1726
+ native.HorizontalAlignment = 0;
1727
+ break;
1728
+ case 'center':
1729
+ native.HorizontalAlignment = 1;
1730
+ break;
1731
+ case 'right':
1732
+ native.HorizontalAlignment = 2;
1733
+ break;
1734
+ case 'stretch':
1735
+ native.HorizontalAlignment = 3;
1736
+ break;
1737
+ }
1738
+ }
1739
+ //@ts-ignore
1740
+ [verticalAlignmentProperty.setNative](value) {
1741
+ const native = this.nativeViewProtected;
1742
+ if (!native)
1743
+ return;
1744
+ // WinUI VerticalAlignment: Top=0, Center=1, Bottom=2, Stretch=3
1745
+ switch (value) {
1746
+ case 'top':
1747
+ native.VerticalAlignment = 0;
1748
+ break;
1749
+ case 'middle':
1750
+ native.VerticalAlignment = 1;
1751
+ break;
1752
+ case 'center':
1753
+ native.VerticalAlignment = 1;
1754
+ break;
1755
+ case 'bottom':
1756
+ native.VerticalAlignment = 2;
1757
+ break;
1758
+ case 'stretch':
1759
+ native.VerticalAlignment = 3;
1760
+ break;
1761
+ }
1762
+ }
1763
+ [originXProperty.getDefault]() {
1764
+ const native = this.nativeViewProtected;
1765
+ if (!native)
1766
+ return 0.5;
1767
+ try {
1768
+ const origin = native.RenderTransformOrigin;
1769
+ return origin ? origin.X ?? 0.5 : 0.5;
1770
+ }
1771
+ catch (_e) {
1772
+ return 0.5;
1773
+ }
1774
+ }
1775
+ [originXProperty.setNative](value) {
1776
+ try {
1777
+ const native = this.nativeViewProtected;
1778
+ const y = this.originY ?? 0.5;
1779
+ if (native && typeof Windows !== 'undefined' && Windows.UI && Microsoft.UI.Xaml && Microsoft.UI.Xaml.PointHelper) {
1780
+ native.RenderTransformOrigin = Microsoft.UI.Xaml.PointHelper.FromCoordinates(value, y);
1781
+ }
1782
+ }
1783
+ catch (_e) { }
1784
+ }
1785
+ [originYProperty.getDefault]() {
1786
+ const native = this.nativeViewProtected;
1787
+ if (!native)
1788
+ return 0.5;
1789
+ try {
1790
+ const origin = native.RenderTransformOrigin;
1791
+ return origin ? origin.Y ?? 0.5 : 0.5;
1792
+ }
1793
+ catch (_e) {
1794
+ return 0.5;
1795
+ }
1796
+ }
1797
+ [originYProperty.setNative](value) {
1798
+ try {
1799
+ const native = this.nativeViewProtected;
1800
+ const x = this.originX ?? 0.5;
1801
+ if (native && typeof Windows !== 'undefined' && Windows.UI && Microsoft.UI.Xaml && Microsoft.UI.Xaml.PointHelper) {
1802
+ native.RenderTransformOrigin = Microsoft.UI.Xaml.PointHelper.FromCoordinates(x, value);
1803
+ }
1804
+ }
1805
+ catch (_e) { }
1806
+ }
1807
+ [rotateProperty.getDefault]() {
1808
+ return 0;
1809
+ }
1810
+ [rotateProperty.setNative](value) {
1811
+ this.updateNativeTransform();
1812
+ }
1813
+ [rotateXProperty.getDefault]() {
1814
+ return 0;
1815
+ }
1816
+ [rotateXProperty.setNative](value) {
1817
+ this.updateNativeTransform();
1818
+ }
1819
+ [rotateYProperty.getDefault]() {
1820
+ return 0;
1821
+ }
1822
+ [rotateYProperty.setNative](value) {
1823
+ this.updateNativeTransform();
1824
+ }
1825
+ [perspectiveProperty.getDefault]() {
1826
+ return 300;
1827
+ }
1828
+ [perspectiveProperty.setNative](value) {
1829
+ this.updateNativeTransform();
1830
+ }
1831
+ [scaleXProperty.getDefault]() {
1832
+ return 1;
1833
+ }
1834
+ [scaleXProperty.setNative](value) {
1835
+ this.updateNativeTransform();
1836
+ }
1837
+ [scaleYProperty.getDefault]() {
1838
+ return 1;
1839
+ }
1840
+ [scaleYProperty.setNative](value) {
1841
+ this.updateNativeTransform();
1842
+ }
1843
+ [translateXProperty.getDefault]() {
1844
+ return 0;
1845
+ }
1846
+ [translateXProperty.setNative](value) {
1847
+ this.updateNativeTransform();
1848
+ }
1849
+ [translateYProperty.getDefault]() {
1850
+ return 0;
1851
+ }
1852
+ [translateYProperty.setNative](value) {
1853
+ this.updateNativeTransform();
1854
+ }
1855
+ updateNativeTransform() {
1856
+ const native = this.nativeViewProtected;
1857
+ if (!native)
1858
+ return;
1859
+ try {
1860
+ const transforms = _ensureNativeTransforms(native);
1861
+ if (!transforms)
1862
+ return;
1863
+ const sx = typeof this.scaleX === 'number' ? this.scaleX : typeof this.scale === 'number' ? this.scale : 1;
1864
+ const sy = typeof this.scaleY === 'number' ? this.scaleY : typeof this.scale === 'number' ? this.scale : 1;
1865
+ transforms.scale.ScaleX = sx;
1866
+ transforms.scale.ScaleY = sy;
1867
+ transforms.rotate.Angle = this.rotate || 0;
1868
+ // translateX/Y are in DIPs — WinUI TranslateTransform also uses DIPs. No conversion.
1869
+ transforms.translate.X = this.translateX || 0;
1870
+ transforms.translate.Y = this.translateY || 0;
1871
+ try {
1872
+ const ox = this.originX ?? 0.5;
1873
+ const oy = this.originY ?? 0.5;
1874
+ native.RenderTransformOrigin = Microsoft.UI.Xaml.PointHelper.FromCoordinates(ox, oy);
1875
+ }
1876
+ catch (_e) { }
1877
+ }
1878
+ catch (_e) { }
1879
+ }
1880
+ _showNativeModalView(parent, options) {
1881
+ this._setupAsRootView({});
1882
+ super._showNativeModalView(parent, options);
1883
+ this._raiseShowingModallyEvent();
1884
+ try {
1885
+ const popup = new Microsoft.UI.Xaml.Controls.Primitives.Popup();
1886
+ const overlay = new Microsoft.UI.Xaml.Controls.Grid();
1887
+ overlay.HorizontalAlignment = 3; // Stretch
1888
+ overlay.VerticalAlignment = 3; // Stretch
1889
+ overlay.Background = new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Colors.Transparent);
1890
+ try {
1891
+ const bounds = getCurrentWindowBounds(this.nativeViewProtected);
1892
+ if (bounds) {
1893
+ overlay.Width = bounds.Width;
1894
+ overlay.Height = bounds.Height;
1895
+ }
1896
+ }
1897
+ catch (_e) { }
1898
+ const showAnimated = options && options.animated === undefined ? true : !!options.animated;
1899
+ try {
1900
+ overlay.Opacity = showAnimated ? 0 : 1;
1901
+ }
1902
+ catch (_e) { }
1903
+ try {
1904
+ try {
1905
+ this._modalPrevHorizontalAlignment = this.horizontalAlignment;
1906
+ this._modalPrevVerticalAlignment = this.verticalAlignment;
1907
+ this._modalPrevWidth = this.width;
1908
+ this._modalPrevHeight = this.height;
1909
+ try {
1910
+ this._modalPrevNativeHorizontalAlignment = this.nativeViewProtected.HorizontalAlignment;
1911
+ this._modalPrevNativeVerticalAlignment = this.nativeViewProtected.VerticalAlignment;
1912
+ this._modalPrevNativeWidth = this.nativeViewProtected.Width;
1913
+ this._modalPrevNativeHeight = this.nativeViewProtected.Height;
1914
+ }
1915
+ catch (_e) { }
1916
+ }
1917
+ catch (_e) { }
1918
+ const isStretch = options && (options.fullscreen || options.stretched);
1919
+ if (isStretch) {
1920
+ this.horizontalAlignment = 'stretch';
1921
+ this.verticalAlignment = 'stretch';
1922
+ try {
1923
+ this.nativeViewProtected.HorizontalAlignment = 3; // Stretch
1924
+ this.nativeViewProtected.VerticalAlignment = 3; // Stretch
1925
+ try {
1926
+ const bounds = getCurrentWindowBounds(this.nativeViewProtected);
1927
+ if (bounds) {
1928
+ this.width = bounds.Width;
1929
+ this.height = bounds.Height;
1930
+ this.nativeViewProtected.Width = bounds.Width;
1931
+ this.nativeViewProtected.Height = bounds.Height;
1932
+ }
1933
+ }
1934
+ catch (_e) { }
1935
+ }
1936
+ catch (_e) { }
1937
+ }
1938
+ else {
1939
+ this.horizontalAlignment = 'center';
1940
+ this.verticalAlignment = 'middle';
1941
+ try {
1942
+ this.nativeViewProtected.HorizontalAlignment = 1; // Center
1943
+ this.nativeViewProtected.VerticalAlignment = 1; // Center
1944
+ }
1945
+ catch (_e) { }
1946
+ try {
1947
+ const w = options && typeof options.width === 'number' ? options.width : options && options.ios && typeof options.ios.width === 'number' ? options.ios.width : undefined;
1948
+ const h = options && typeof options.height === 'number' ? options.height : options && options.ios && typeof options.ios.height === 'number' ? options.ios.height : undefined;
1949
+ if (typeof w === 'number' && w > 0) {
1950
+ this.width = w;
1951
+ try {
1952
+ this.nativeViewProtected.Width = w;
1953
+ }
1954
+ catch (_e) { }
1955
+ }
1956
+ else {
1957
+ this.width = unsetValue;
1958
+ try {
1959
+ this.nativeViewProtected.Width = NaN;
1960
+ }
1961
+ catch (_e) { }
1962
+ }
1963
+ if (typeof h === 'number' && h > 0) {
1964
+ this.height = h;
1965
+ try {
1966
+ this.nativeViewProtected.Height = h;
1967
+ }
1968
+ catch (_e) { }
1969
+ }
1970
+ else {
1971
+ this.height = unsetValue;
1972
+ try {
1973
+ this.nativeViewProtected.Height = NaN;
1974
+ }
1975
+ catch (_e) { }
1976
+ }
1977
+ }
1978
+ catch (_e) { }
1979
+ }
1980
+ overlay.Children.Append(this.nativeViewProtected);
1981
+ }
1982
+ catch (_e) { }
1983
+ popup.Child = overlay;
1984
+ popup.IsLightDismissEnabled = options && options.cancelable !== undefined ? !!options.cancelable : true;
1985
+ this._modalPopup = popup;
1986
+ this._modalOverlay = overlay;
1987
+ // Closed handler forwards light-dismiss to modal close logic.
1988
+ try {
1989
+ this._modalPopupClosedHandler = () => {
1990
+ if (this._isModalClosing) {
1991
+ return;
1992
+ }
1993
+ if (this._closeModalCallback) {
1994
+ this._closeModalCallback();
1995
+ }
1996
+ };
1997
+ //@ts-ignore
1998
+ popup.AddHandler(Microsoft.UI.Xaml.Controls.Primitives.Popup.ClosedEvent, this._modalPopupClosedHandler, true);
1999
+ }
2000
+ catch (_e) { }
2001
+ // A Popup with no XamlRoot throws E_UNEXPECTED (0x8000FFFF) on open. Anchor to the XamlRoot
2002
+ // of an element already in the live tree (the modal element isn't attached yet).
2003
+ try {
2004
+ const xamlRoot = parent?.nativeViewProtected?.XamlRoot
2005
+ || getCurrentWindowContent()?.XamlRoot
2006
+ || this.nativeViewProtected?.XamlRoot;
2007
+ if (xamlRoot && typeof popup.XamlRoot !== 'undefined') {
2008
+ popup.XamlRoot = xamlRoot;
2009
+ }
2010
+ }
2011
+ catch (_e) { }
2012
+ popup.IsOpen = true;
2013
+ try {
2014
+ this.callLoaded();
2015
+ }
2016
+ catch (_e) { }
2017
+ const animated = showAnimated;
2018
+ if (!this._modalAnimatedOptions) {
2019
+ this._modalAnimatedOptions = [];
2020
+ }
2021
+ this._modalAnimatedOptions.push(animated);
2022
+ this._raiseShownModallyEvent();
2023
+ try {
2024
+ if (showAnimated && this._modalOverlay) {
2025
+ this._animateNativeOpacity(this._modalOverlay, 0, 1, 240);
2026
+ }
2027
+ }
2028
+ catch (_e) { }
2029
+ }
2030
+ catch (e) {
2031
+ console.error('[View._showNativeModalView] failed to open popup modal:', e);
2032
+ }
2033
+ }
2034
+ _hideNativeModalView(_parent, whenClosedCallback) {
2035
+ if (this._isModalClosing) {
2036
+ whenClosedCallback();
2037
+ return;
2038
+ }
2039
+ this._isModalClosing = true;
2040
+ try {
2041
+ try {
2042
+ if (this._modalPopup && this._modalPopupClosedHandler && typeof this._modalPopup.removeEventListener === 'function') {
2043
+ try {
2044
+ this._modalPopup.removeEventListener('Closed', this._modalPopupClosedHandler);
2045
+ }
2046
+ catch (_e) { }
2047
+ }
2048
+ }
2049
+ catch (_e) { }
2050
+ const animated = this._modalAnimatedOptions && this._modalAnimatedOptions.length ? !!this._modalAnimatedOptions.pop() : true;
2051
+ const finalize = () => {
2052
+ try {
2053
+ if (this._modalPrevHorizontalAlignment !== undefined) {
2054
+ this.horizontalAlignment = this._modalPrevHorizontalAlignment;
2055
+ }
2056
+ if (this._modalPrevVerticalAlignment !== undefined) {
2057
+ this.verticalAlignment = this._modalPrevVerticalAlignment;
2058
+ }
2059
+ if (this._modalPrevWidth !== undefined) {
2060
+ this.width = this._modalPrevWidth;
2061
+ }
2062
+ if (this._modalPrevHeight !== undefined) {
2063
+ this.height = this._modalPrevHeight;
2064
+ }
2065
+ try {
2066
+ if (this._modalPrevNativeHorizontalAlignment !== undefined) {
2067
+ this.nativeViewProtected.HorizontalAlignment = this._modalPrevNativeHorizontalAlignment;
2068
+ }
2069
+ if (this._modalPrevNativeVerticalAlignment !== undefined) {
2070
+ this.nativeViewProtected.VerticalAlignment = this._modalPrevNativeVerticalAlignment;
2071
+ }
2072
+ if (this._modalPrevNativeWidth !== undefined) {
2073
+ this.nativeViewProtected.Width = this._modalPrevNativeWidth;
2074
+ }
2075
+ if (this._modalPrevNativeHeight !== undefined) {
2076
+ this.nativeViewProtected.Height = this._modalPrevNativeHeight;
2077
+ }
2078
+ }
2079
+ catch (_e) { }
2080
+ }
2081
+ catch (_e) { }
2082
+ try {
2083
+ if (this._modalPopup) {
2084
+ this._modalPopup.IsOpen = false;
2085
+ this._modalPopup.Child = null;
2086
+ this._modalPopup = null;
2087
+ }
2088
+ if (this._modalOverlay) {
2089
+ this._modalOverlay.Children.Clear();
2090
+ this._modalOverlay = null;
2091
+ }
2092
+ }
2093
+ catch (_e) { }
2094
+ this._isModalClosing = false;
2095
+ whenClosedCallback();
2096
+ };
2097
+ if (animated && this._modalOverlay) {
2098
+ try {
2099
+ const from = typeof this._modalOverlay.Opacity === 'number' ? this._modalOverlay.Opacity : 1;
2100
+ this._animateNativeOpacity(this._modalOverlay, from, 0, 240, finalize);
2101
+ }
2102
+ catch (_e) {
2103
+ finalize();
2104
+ }
2105
+ }
2106
+ else {
2107
+ finalize();
2108
+ }
2109
+ }
2110
+ catch (_e) { }
2111
+ }
2112
+ }
2113
+ try {
2114
+ View.prototype._nativeBackgroundState = 'unset';
2115
+ }
2116
+ catch (_e) { }
2117
+ export class ContainerView extends View {
2118
+ }
2119
+ export class CustomLayoutView extends ContainerView {
2120
+ createNativeView() {
2121
+ // Grid (single default cell), not StackPanel: a StackPanel measures its child with INFINITE
2122
+ // extent along its orientation, so a scrollable child (ListView/ScrollView) never gets a bounded
2123
+ // height and realizes every row (no virtualization). A Grid stretches its child to the bounded
2124
+ // available size — the correct fill semantics here. Children API is shared.
2125
+ return new Microsoft.UI.Xaml.Controls.Grid();
2126
+ }
2127
+ _addViewToNativeVisualTree(child, _atIndex = Number.MAX_SAFE_INTEGER) {
2128
+ super._addViewToNativeVisualTree(child);
2129
+ const nativeParent = this.nativeViewProtected;
2130
+ const nativeChild = child.nativeViewProtected;
2131
+ if (nativeParent && nativeChild) {
2132
+ const children = nativeParent.Children;
2133
+ if (children) {
2134
+ const size = children.Size;
2135
+ try {
2136
+ if (_atIndex >= 0 && _atIndex < size && _atIndex < Number.MAX_SAFE_INTEGER) {
2137
+ children.InsertAt(_atIndex, nativeChild);
2138
+ }
2139
+ else {
2140
+ children.Append(nativeChild);
2141
+ }
2142
+ }
2143
+ catch {
2144
+ return false;
2145
+ }
2146
+ try {
2147
+ if (!nativeChild.__ns_view)
2148
+ nativeChild.__ns_view = child;
2149
+ }
2150
+ catch (_e) { }
2151
+ // Do NOT call UpdateLayout() / InvalidateMeasure() / InvalidateArrange() here. Append() /
2152
+ // InsertAt() already mark the child and parent panel dirty for the next layout pass. Forcing
2153
+ // nativeParent.UpdateLayout() after EACH add makes construction O(n²) (50 children → 1275
2154
+ // synchronous layout passes). XAML batches and defers layout to one pass at frame time.
2155
+ return true;
2156
+ }
2157
+ }
2158
+ return false;
2159
+ }
2160
+ _removeViewFromNativeVisualTree(child) {
2161
+ const nativeParent = this.nativeViewProtected;
2162
+ const nativeChild = child.nativeViewProtected;
2163
+ if (nativeParent && nativeChild) {
2164
+ const children = nativeParent.Children;
2165
+ if (children) {
2166
+ const count = children.Size;
2167
+ for (let i = 0; i < count; i++) {
2168
+ if (children.GetAt(i) === nativeChild) {
2169
+ children.RemoveAt(i);
2170
+ break;
2171
+ }
2172
+ }
2173
+ }
2174
+ }
2175
+ super._removeViewFromNativeVisualTree(child);
2176
+ }
2177
+ }
2178
+ //# sourceMappingURL=index.windows.js.map