@gjsify/adwaita-web 0.54.0 → 0.55.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 (439) hide show
  1. package/dist/adwaita-web.css +8066 -3033
  2. package/dist/adwaita-web.css.map +1 -1
  3. package/lib/types/adw-application-window.spec.d.ts +1 -0
  4. package/lib/types/adw-bin.spec.d.ts +1 -0
  5. package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
  6. package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
  7. package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
  8. package/lib/types/adw-preferences-row.spec.d.ts +1 -0
  9. package/lib/types/adw-shortcuts.spec.d.ts +1 -0
  10. package/lib/types/adw-tab-bar.spec.d.ts +1 -0
  11. package/lib/types/adw-tab-button.spec.d.ts +1 -0
  12. package/lib/types/adw-tab-overview.spec.d.ts +1 -0
  13. package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
  14. package/lib/types/adw-window.spec.d.ts +1 -0
  15. package/lib/types/breakpoints.d.ts +14 -3
  16. package/lib/types/construct-vectors.spec.d.ts +1 -0
  17. package/lib/types/elements/adw-application-window.d.ts +27 -0
  18. package/lib/types/elements/adw-bin.d.ts +17 -0
  19. package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
  20. package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
  21. package/lib/types/elements/adw-dialog.d.ts +10 -0
  22. package/lib/types/elements/adw-layout-slot.d.ts +19 -0
  23. package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
  24. package/lib/types/elements/adw-preferences-row.d.ts +31 -0
  25. package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
  26. package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
  27. package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
  28. package/lib/types/elements/adw-sidebar.d.ts +5 -0
  29. package/lib/types/elements/adw-switch-row.d.ts +3 -0
  30. package/lib/types/elements/adw-tab-bar.d.ts +108 -0
  31. package/lib/types/elements/adw-tab-button.d.ts +32 -0
  32. package/lib/types/elements/adw-tab-overview.d.ts +199 -0
  33. package/lib/types/elements/adw-tab-view.d.ts +19 -23
  34. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
  35. package/lib/types/elements/adw-window.d.ts +40 -0
  36. package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
  37. package/lib/types/elements/gtk-application-window.d.ts +34 -0
  38. package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
  39. package/lib/types/elements/gtk-button.d.ts +9 -0
  40. package/lib/types/elements/gtk-calendar.d.ts +159 -0
  41. package/lib/types/elements/gtk-center-box.d.ts +39 -0
  42. package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
  43. package/lib/types/elements/gtk-column-view.d.ts +50 -0
  44. package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
  45. package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
  46. package/lib/types/elements/gtk-editable-label.d.ts +33 -0
  47. package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
  48. package/lib/types/elements/gtk-entry.d.ts +20 -1
  49. package/lib/types/elements/gtk-expander.d.ts +49 -0
  50. package/lib/types/elements/gtk-fixed.d.ts +13 -0
  51. package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
  52. package/lib/types/elements/gtk-flow-box.d.ts +87 -0
  53. package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
  54. package/lib/types/elements/gtk-frame.d.ts +24 -0
  55. package/lib/types/elements/gtk-gl-area.d.ts +136 -0
  56. package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
  57. package/lib/types/elements/gtk-grid-view.d.ts +39 -0
  58. package/lib/types/elements/gtk-grid.d.ts +27 -0
  59. package/lib/types/elements/gtk-header-bar.d.ts +60 -0
  60. package/lib/types/elements/gtk-inscription.d.ts +64 -0
  61. package/lib/types/elements/gtk-label.d.ts +3 -0
  62. package/lib/types/elements/gtk-level-bar.d.ts +59 -0
  63. package/lib/types/elements/gtk-link-button.d.ts +29 -0
  64. package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
  65. package/lib/types/elements/gtk-list-box.d.ts +100 -0
  66. package/lib/types/elements/gtk-list-view.d.ts +73 -0
  67. package/lib/types/elements/gtk-media-controls.d.ts +142 -0
  68. package/lib/types/elements/gtk-notebook.d.ts +148 -0
  69. package/lib/types/elements/gtk-overlay.d.ts +22 -0
  70. package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
  71. package/lib/types/elements/gtk-paned.d.ts +121 -0
  72. package/lib/types/elements/gtk-password-entry.d.ts +27 -0
  73. package/lib/types/elements/gtk-picture.d.ts +66 -0
  74. package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
  75. package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
  76. package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
  77. package/lib/types/elements/gtk-popover.d.ts +28 -3
  78. package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
  79. package/lib/types/elements/gtk-revealer.d.ts +55 -0
  80. package/lib/types/elements/gtk-scale-button.d.ts +59 -0
  81. package/lib/types/elements/gtk-scale.d.ts +98 -0
  82. package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
  83. package/lib/types/elements/gtk-scrolled-window.d.ts +121 -0
  84. package/lib/types/elements/gtk-search-bar.d.ts +39 -0
  85. package/lib/types/elements/gtk-search-entry.d.ts +27 -0
  86. package/lib/types/elements/gtk-separator.d.ts +10 -0
  87. package/lib/types/elements/gtk-spin-button.d.ts +94 -0
  88. package/lib/types/elements/gtk-spinner.d.ts +14 -0
  89. package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
  90. package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
  91. package/lib/types/elements/gtk-stack.d.ts +154 -0
  92. package/lib/types/elements/gtk-switch.d.ts +4 -1
  93. package/lib/types/elements/gtk-text-view.d.ts +62 -0
  94. package/lib/types/elements/gtk-text.d.ts +45 -0
  95. package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
  96. package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
  97. package/lib/types/elements/gtk-video.d.ts +97 -0
  98. package/lib/types/elements/gtk-viewport.d.ts +84 -0
  99. package/lib/types/elements/gtk-window-controls.d.ts +80 -0
  100. package/lib/types/elements/gtk-window-handle.d.ts +39 -0
  101. package/lib/types/elements/gtk-window.d.ts +97 -0
  102. package/lib/types/elements/justification.d.ts +2 -0
  103. package/lib/types/elements/popover-menu.d.ts +18 -0
  104. package/lib/types/elements/roving-focus.d.ts +29 -1
  105. package/lib/types/emoji-data.d.ts +38 -0
  106. package/lib/types/font-description.d.ts +96 -0
  107. package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
  108. package/lib/types/gtk-action-bar.spec.d.ts +1 -0
  109. package/lib/types/gtk-application-window.spec.d.ts +1 -0
  110. package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
  111. package/lib/types/gtk-calendar.spec.d.ts +1 -0
  112. package/lib/types/gtk-center-box.spec.d.ts +1 -0
  113. package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
  114. package/lib/types/gtk-column-view.spec.d.ts +1 -0
  115. package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
  116. package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
  117. package/lib/types/gtk-editable-label.spec.d.ts +1 -0
  118. package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
  119. package/lib/types/gtk-expander.spec.d.ts +1 -0
  120. package/lib/types/gtk-fixed.spec.d.ts +1 -0
  121. package/lib/types/gtk-flow-box.spec.d.ts +1 -0
  122. package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
  123. package/lib/types/gtk-frame.spec.d.ts +1 -0
  124. package/lib/types/gtk-gl-area.spec.d.ts +1 -0
  125. package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
  126. package/lib/types/gtk-grid-view.spec.d.ts +1 -0
  127. package/lib/types/gtk-grid.spec.d.ts +1 -0
  128. package/lib/types/gtk-header-bar.spec.d.ts +1 -0
  129. package/lib/types/gtk-inscription.spec.d.ts +1 -0
  130. package/lib/types/gtk-level-bar.spec.d.ts +1 -0
  131. package/lib/types/gtk-link-button.spec.d.ts +1 -0
  132. package/lib/types/gtk-list-box.spec.d.ts +1 -0
  133. package/lib/types/gtk-list-view.spec.d.ts +1 -0
  134. package/lib/types/gtk-media-controls.spec.d.ts +1 -0
  135. package/lib/types/gtk-notebook.spec.d.ts +1 -0
  136. package/lib/types/gtk-overlay.spec.d.ts +1 -0
  137. package/lib/types/gtk-paned.spec.d.ts +1 -0
  138. package/lib/types/gtk-password-entry.spec.d.ts +1 -0
  139. package/lib/types/gtk-picture.spec.d.ts +1 -0
  140. package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
  141. package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
  142. package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
  143. package/lib/types/gtk-popover-role.spec.d.ts +1 -0
  144. package/lib/types/gtk-popover.spec.d.ts +1 -0
  145. package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
  146. package/lib/types/gtk-revealer.spec.d.ts +1 -0
  147. package/lib/types/gtk-scale-button.spec.d.ts +1 -0
  148. package/lib/types/gtk-scale.spec.d.ts +1 -0
  149. package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
  150. package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
  151. package/lib/types/gtk-search-bar.spec.d.ts +1 -0
  152. package/lib/types/gtk-search-entry.spec.d.ts +1 -0
  153. package/lib/types/gtk-separator.spec.d.ts +1 -0
  154. package/lib/types/gtk-spin-button.spec.d.ts +1 -0
  155. package/lib/types/gtk-spinner.spec.d.ts +1 -0
  156. package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
  157. package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
  158. package/lib/types/gtk-stack.spec.d.ts +1 -0
  159. package/lib/types/gtk-text-view.spec.d.ts +1 -0
  160. package/lib/types/gtk-text.spec.d.ts +1 -0
  161. package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
  162. package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
  163. package/lib/types/gtk-video.spec.d.ts +1 -0
  164. package/lib/types/gtk-viewport.spec.d.ts +1 -0
  165. package/lib/types/gtk-window-controls.spec.d.ts +1 -0
  166. package/lib/types/gtk-window-handle.spec.d.ts +1 -0
  167. package/lib/types/gtk-window.spec.d.ts +1 -0
  168. package/lib/types/index.d.ts +4 -1
  169. package/lib/types/list-view-dom.d.ts +44 -0
  170. package/lib/types/namespace/adw.d.ts +12 -0
  171. package/lib/types/namespace/gtk.d.ts +61 -0
  172. package/lib/types/page-setup.d.ts +100 -0
  173. package/lib/types/scroll-shading.d.ts +15 -1
  174. package/lib/types/shared-tree-builder.d.ts +30 -2
  175. package/lib/types/signals.d.ts +4 -0
  176. package/lib/types/styles.generated.d.ts +1 -1
  177. package/lib/types/tab-chip.d.ts +73 -0
  178. package/lib/types/template-classes.d.ts +11 -0
  179. package/package.json +12 -8
  180. package/scss/_action_row.scss +2 -2
  181. package/scss/_application_window.scss +26 -0
  182. package/scss/_aspect_frame.scss +25 -0
  183. package/scss/_bin.scss +26 -0
  184. package/scss/_button.scss +45 -0
  185. package/scss/_calendar.scss +176 -0
  186. package/scss/_center_box.scss +25 -0
  187. package/scss/_checks.scss +6 -1
  188. package/scss/_clamp_scrollable.scss +28 -0
  189. package/scss/_color_button.scss +67 -0
  190. package/scss/_column_view.scss +126 -0
  191. package/scss/_drag_icon.scss +55 -0
  192. package/scss/_drawing.scss +85 -0
  193. package/scss/_emoji_chooser.scss +213 -0
  194. package/scss/_entry.scss +13 -5
  195. package/scss/_entry_row.scss +1 -1
  196. package/scss/_expander.scss +113 -0
  197. package/scss/_fixed.scss +29 -0
  198. package/scss/_flow_box.scss +94 -0
  199. package/scss/_font_button.scss +63 -0
  200. package/scss/_frame.scss +41 -0
  201. package/scss/_grid.scss +19 -0
  202. package/scss/_grid_view.scss +46 -0
  203. package/scss/_gtk_about_dialog.scss +186 -0
  204. package/scss/_headerbar.scss +132 -0
  205. package/scss/_icons.generated.scss +186 -0
  206. package/scss/_inscription.scss +125 -0
  207. package/scss/_level_bar.scss +190 -0
  208. package/scss/_link.scss +82 -0
  209. package/scss/_list_box.scss +88 -0
  210. package/scss/_list_view.scss +96 -0
  211. package/scss/_media_controls.scss +139 -0
  212. package/scss/_overlay.scss +68 -0
  213. package/scss/_paned.scss +130 -0
  214. package/scss/_password_entry.scss +83 -0
  215. package/scss/_picture.scss +80 -0
  216. package/scss/_popover_bin.scss +53 -0
  217. package/scss/_popover_menu_bar.scss +104 -0
  218. package/scss/_preferences_row.scss +51 -0
  219. package/scss/_print_dialogs.scss +328 -0
  220. package/scss/_reset.scss +21 -19
  221. package/scss/_revealer.scss +37 -0
  222. package/scss/_row.scss +20 -5
  223. package/scss/_scale.scss +298 -0
  224. package/scss/_scale_button.scss +167 -0
  225. package/scss/_scrollbar.scss +230 -0
  226. package/scss/_scrolled_window.scss +131 -0
  227. package/scss/_search_bar.scss +118 -0
  228. package/scss/_search_entry.scss +86 -0
  229. package/scss/_separator.scss +44 -0
  230. package/scss/_shortcuts_dialog.scss +100 -0
  231. package/scss/_spin_button.scss +172 -0
  232. package/scss/_spin_row.scss +56 -8
  233. package/scss/_spinner.scss +85 -4
  234. package/scss/_stack.scss +729 -0
  235. package/scss/_switch.scss +13 -5
  236. package/scss/_switch_row.scss +10 -0
  237. package/scss/_tab_bar.scss +317 -0
  238. package/scss/_tab_button.scss +98 -0
  239. package/scss/_tab_overview.scss +483 -0
  240. package/scss/_text.scss +182 -0
  241. package/scss/_theme.scss +1 -1
  242. package/scss/_tree_expander.scss +62 -0
  243. package/scss/_variables.scss +1 -1
  244. package/scss/_video.scss +146 -0
  245. package/scss/_view_switcher_sidebar.scss +112 -0
  246. package/scss/_viewport.scss +50 -0
  247. package/scss/_window.scss +118 -1
  248. package/scss/_window_handle.scss +20 -0
  249. package/scss/adwaita-skin.scss +109 -0
  250. package/src/adw-action-rows.spec.ts +76 -0
  251. package/src/adw-application-window.spec.ts +118 -0
  252. package/src/adw-bin.spec.ts +75 -0
  253. package/src/adw-breakpoint-bin.spec.ts +324 -0
  254. package/src/adw-clamp-scrollable.spec.ts +189 -0
  255. package/src/adw-multi-layout-view.spec.ts +243 -0
  256. package/src/adw-preferences-row.spec.ts +164 -0
  257. package/src/adw-row-state.spec.ts +142 -0
  258. package/src/adw-shortcuts.spec.ts +155 -0
  259. package/src/adw-tab-bar.spec.ts +287 -0
  260. package/src/adw-tab-button.spec.ts +101 -0
  261. package/src/adw-tab-overview.spec.ts +443 -0
  262. package/src/adw-view-switcher-sidebar.spec.ts +281 -0
  263. package/src/adw-window.spec.ts +129 -0
  264. package/src/breakpoints.ts +25 -8
  265. package/src/capabilities.d.mts +3 -0
  266. package/src/capabilities.mjs +18 -0
  267. package/src/connect-lifecycle.spec.ts +12 -0
  268. package/src/construct-vectors.spec.ts +175 -0
  269. package/src/elements/adw-application-window.ts +154 -0
  270. package/src/elements/adw-bin.ts +101 -0
  271. package/src/elements/adw-breakpoint-bin.ts +287 -0
  272. package/src/elements/adw-clamp-scrollable.ts +207 -0
  273. package/src/elements/adw-dialog.ts +19 -0
  274. package/src/elements/adw-layout-slot.ts +95 -0
  275. package/src/elements/adw-multi-layout-view.ts +326 -0
  276. package/src/elements/adw-preferences-row.ts +150 -0
  277. package/src/elements/adw-shortcuts-dialog.ts +52 -0
  278. package/src/elements/adw-shortcuts-item.ts +171 -0
  279. package/src/elements/adw-shortcuts-section.ts +86 -0
  280. package/src/elements/adw-sidebar.ts +42 -3
  281. package/src/elements/adw-switch-row.ts +28 -1
  282. package/src/elements/adw-tab-bar.ts +529 -0
  283. package/src/elements/adw-tab-button.ts +194 -0
  284. package/src/elements/adw-tab-overview.ts +1011 -0
  285. package/src/elements/adw-tab-view.ts +66 -145
  286. package/src/elements/adw-toggle-group.ts +20 -5
  287. package/src/elements/adw-view-stack.ts +18 -0
  288. package/src/elements/adw-view-switcher-sidebar.ts +332 -0
  289. package/src/elements/adw-window.ts +185 -11
  290. package/src/elements/checks.ts +35 -8
  291. package/src/elements/gtk-about-dialog.ts +811 -0
  292. package/src/elements/gtk-application-window.ts +108 -0
  293. package/src/elements/gtk-aspect-frame.ts +160 -0
  294. package/src/elements/gtk-button.ts +12 -0
  295. package/src/elements/gtk-calendar.ts +757 -0
  296. package/src/elements/gtk-center-box.ts +184 -0
  297. package/src/elements/gtk-color-dialog-button.ts +278 -0
  298. package/src/elements/gtk-column-view.ts +320 -0
  299. package/src/elements/gtk-drag-icon.ts +257 -0
  300. package/src/elements/gtk-drawing-area.ts +303 -0
  301. package/src/elements/gtk-editable-label.ts +204 -0
  302. package/src/elements/gtk-emoji-chooser.ts +542 -0
  303. package/src/elements/gtk-entry.ts +39 -5
  304. package/src/elements/gtk-expander.ts +301 -0
  305. package/src/elements/gtk-fixed.ts +99 -0
  306. package/src/elements/gtk-flow-box-child.ts +68 -0
  307. package/src/elements/gtk-flow-box.ts +480 -0
  308. package/src/elements/gtk-font-dialog-button.ts +432 -0
  309. package/src/elements/gtk-frame.ts +174 -0
  310. package/src/elements/gtk-gl-area.ts +476 -0
  311. package/src/elements/gtk-graphics-offload.ts +130 -0
  312. package/src/elements/gtk-grid-view.ts +168 -0
  313. package/src/elements/gtk-grid.ts +127 -0
  314. package/src/elements/gtk-header-bar.ts +273 -0
  315. package/src/elements/gtk-inscription.ts +387 -0
  316. package/src/elements/gtk-label.ts +19 -11
  317. package/src/elements/gtk-level-bar.ts +323 -0
  318. package/src/elements/gtk-link-button.ts +138 -0
  319. package/src/elements/gtk-list-box-row.ts +195 -0
  320. package/src/elements/gtk-list-box.ts +413 -0
  321. package/src/elements/gtk-list-view.ts +370 -0
  322. package/src/elements/gtk-media-controls.ts +509 -0
  323. package/src/elements/gtk-notebook.ts +664 -0
  324. package/src/elements/gtk-overlay.ts +149 -0
  325. package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
  326. package/src/elements/gtk-paned.ts +597 -0
  327. package/src/elements/gtk-password-entry.ts +170 -0
  328. package/src/elements/gtk-picture.ts +254 -0
  329. package/src/elements/gtk-popover-bin.ts +332 -0
  330. package/src/elements/gtk-popover-menu-bar.ts +355 -0
  331. package/src/elements/gtk-popover-menu.ts +339 -0
  332. package/src/elements/gtk-popover.ts +92 -10
  333. package/src/elements/gtk-print-unix-dialog.ts +989 -0
  334. package/src/elements/gtk-progress-bar.ts +24 -11
  335. package/src/elements/gtk-revealer.ts +406 -0
  336. package/src/elements/gtk-scale-button.ts +358 -0
  337. package/src/elements/gtk-scale.ts +492 -0
  338. package/src/elements/gtk-scrollbar.ts +528 -0
  339. package/src/elements/gtk-scrolled-window.ts +642 -0
  340. package/src/elements/gtk-search-bar.ts +200 -0
  341. package/src/elements/gtk-search-entry.ts +172 -0
  342. package/src/elements/gtk-separator.ts +52 -0
  343. package/src/elements/gtk-spin-button.ts +475 -0
  344. package/src/elements/gtk-spinner.ts +126 -0
  345. package/src/elements/gtk-stack-sidebar.ts +212 -0
  346. package/src/elements/gtk-stack-switcher.ts +279 -0
  347. package/src/elements/gtk-stack.ts +748 -0
  348. package/src/elements/gtk-switch.ts +52 -19
  349. package/src/elements/gtk-text-view.ts +290 -0
  350. package/src/elements/gtk-text.ts +219 -0
  351. package/src/elements/gtk-toggle-button.ts +239 -0
  352. package/src/elements/gtk-tree-expander.ts +243 -0
  353. package/src/elements/gtk-video.ts +423 -0
  354. package/src/elements/gtk-viewport.ts +277 -0
  355. package/src/elements/gtk-window-controls.ts +341 -0
  356. package/src/elements/gtk-window-handle.ts +221 -0
  357. package/src/elements/gtk-window.ts +366 -0
  358. package/src/elements/justification.ts +24 -0
  359. package/src/elements/popover-menu.ts +24 -0
  360. package/src/elements/roving-focus.ts +53 -21
  361. package/src/emoji-data.ts +577 -0
  362. package/src/entry-rows.spec.ts +31 -0
  363. package/src/font-description.ts +238 -0
  364. package/src/gtk-about-dialog.spec.ts +250 -0
  365. package/src/gtk-action-bar.spec.ts +89 -0
  366. package/src/gtk-application-window.spec.ts +107 -0
  367. package/src/gtk-aspect-frame.spec.ts +114 -0
  368. package/src/gtk-calendar.spec.ts +589 -0
  369. package/src/gtk-center-box.spec.ts +125 -0
  370. package/src/gtk-color-dialog-button.spec.ts +141 -0
  371. package/src/gtk-column-view.spec.ts +154 -0
  372. package/src/gtk-drag-icon.spec.ts +190 -0
  373. package/src/gtk-drawing-area.spec.ts +211 -0
  374. package/src/gtk-editable-label.spec.ts +190 -0
  375. package/src/gtk-emoji-chooser.spec.ts +173 -0
  376. package/src/gtk-entry.spec.ts +23 -0
  377. package/src/gtk-expander.spec.ts +170 -0
  378. package/src/gtk-fixed.spec.ts +97 -0
  379. package/src/gtk-flow-box.spec.ts +289 -0
  380. package/src/gtk-font-dialog-button.spec.ts +166 -0
  381. package/src/gtk-frame.spec.ts +119 -0
  382. package/src/gtk-gl-area.spec.ts +361 -0
  383. package/src/gtk-graphics-offload.spec.ts +119 -0
  384. package/src/gtk-grid-view.spec.ts +106 -0
  385. package/src/gtk-grid.spec.ts +122 -0
  386. package/src/gtk-header-bar.spec.ts +202 -0
  387. package/src/gtk-inscription.spec.ts +235 -0
  388. package/src/gtk-level-bar.spec.ts +199 -0
  389. package/src/gtk-link-button.spec.ts +117 -0
  390. package/src/gtk-list-box.spec.ts +422 -0
  391. package/src/gtk-list-view.spec.ts +203 -0
  392. package/src/gtk-media-controls.spec.ts +295 -0
  393. package/src/gtk-notebook.spec.ts +546 -0
  394. package/src/gtk-overlay.spec.ts +141 -0
  395. package/src/gtk-paned.spec.ts +267 -0
  396. package/src/gtk-password-entry.spec.ts +150 -0
  397. package/src/gtk-picture.spec.ts +172 -0
  398. package/src/gtk-popover-bin.spec.ts +229 -0
  399. package/src/gtk-popover-menu-bar.spec.ts +296 -0
  400. package/src/gtk-popover-menu.spec.ts +261 -0
  401. package/src/gtk-popover-role.spec.ts +138 -0
  402. package/src/gtk-popover.spec.ts +264 -0
  403. package/src/gtk-print-dialogs.spec.ts +251 -0
  404. package/src/gtk-revealer.spec.ts +164 -0
  405. package/src/gtk-scale-button.spec.ts +218 -0
  406. package/src/gtk-scale.spec.ts +213 -0
  407. package/src/gtk-scrollbar.spec.ts +244 -0
  408. package/src/gtk-scrolled-window.spec.ts +262 -0
  409. package/src/gtk-search-bar.spec.ts +179 -0
  410. package/src/gtk-search-entry.spec.ts +142 -0
  411. package/src/gtk-separator.spec.ts +74 -0
  412. package/src/gtk-spin-button.spec.ts +186 -0
  413. package/src/gtk-spinner.spec.ts +130 -0
  414. package/src/gtk-stack-sidebar.spec.ts +217 -0
  415. package/src/gtk-stack-switcher.spec.ts +249 -0
  416. package/src/gtk-stack.spec.ts +432 -0
  417. package/src/gtk-text-view.spec.ts +189 -0
  418. package/src/gtk-text.spec.ts +163 -0
  419. package/src/gtk-toggle-button.spec.ts +186 -0
  420. package/src/gtk-tree-expander.spec.ts +201 -0
  421. package/src/gtk-video.spec.ts +240 -0
  422. package/src/gtk-viewport.spec.ts +140 -0
  423. package/src/gtk-window-controls.spec.ts +232 -0
  424. package/src/gtk-window-handle.spec.ts +158 -0
  425. package/src/gtk-window.spec.ts +190 -0
  426. package/src/index.ts +9 -1
  427. package/src/keyboard-operable.spec.ts +33 -1
  428. package/src/list-view-dom.ts +121 -0
  429. package/src/namespace/adw.ts +12 -0
  430. package/src/namespace/gtk.ts +61 -0
  431. package/src/page-setup.ts +222 -0
  432. package/src/scroll-shading.ts +18 -1
  433. package/src/shared-tree-builder.ts +263 -21
  434. package/src/signals.ts +15 -0
  435. package/src/style-classes.spec.ts +9 -8
  436. package/src/styles.generated.ts +1 -1
  437. package/src/tab-chip.ts +200 -0
  438. package/src/template-classes.ts +45 -0
  439. package/src/test.browser.mts +152 -0
@@ -0,0 +1,277 @@
1
+ // <gtk-viewport> — a window onto a child too large to show whole: GTK's answer for a
2
+ // widget that does not know how to scroll itself.
3
+ //
4
+ // WHAT IT IS, in GTK's own words: "Implements scrollability for widgets that don't support
5
+ // scrolling on their own" (gtkviewport.c:44-46). It has ONE property of its own
6
+ // (`scroll-to-focus`) and takes its two adjustments from `Gtk.Scrollable`, the interface
7
+ // every scroller in a `GtkScrolledWindow` is addressed through (gtkviewport.c:318-327). This
8
+ // element is therefore the CONTENT HALF of `<gtk-scrolled-window>`, exactly as it is in GTK:
9
+ // setting a non-scrollable child on a scrolled window makes the window "add the child to a
10
+ // Gtk.Viewport and then set the viewport as the child" (gtkscrolledwindow.c:780-793), which
11
+ // is `gtk_scrolled_window_set_child` doing it at gtkscrolledwindow.c:4362-4374.
12
+ //
13
+ // THE ADJUSTMENTS ARE GTK'S ARITHMETIC, VERBATIM. `viewport_set_adjustment_values`
14
+ // (gtkviewport.c:159-189) configures each axis as
15
+ //
16
+ // configure(value, lower: 0, upper: child_size, step: viewport_size * 0.1,
17
+ // page: viewport_size * 0.9, page_size: viewport_size)
18
+ //
19
+ // so a step is a tenth of the window and a page nine tenths of it — which is why a
20
+ // GtkViewport under the mouse wheel moves by the same PROPORTION whatever it wraps. In a
21
+ // browser those six numbers are what the scrollport already knows: `scrollLeft` /
22
+ // `scrollTop` are the value, `scrollWidth` / `scrollHeight` the upper and `clientWidth` /
23
+ // `clientHeight` the page size. The port READS them; it does not recompute a layout GTK
24
+ // negotiated itself. The two are one element HERE for the same reason they are two nodes
25
+ // there: the `viewport` node is the box that clips and the one that is measured, and a
26
+ // browser scrollport is both at once.
27
+ //
28
+ // THE CHILD IS ALLOCATED ITS NATURAL SIZE, never shrunk — `gtk_viewport_size_allocate`
29
+ // measures the child and hands it `MAX (allocated, nat)` (gtkviewport.c:517-545, 550-560),
30
+ // so content wider than the window makes the window scroll sideways instead of being squeezed
31
+ // into it. The CSS spelling of "never shrink it" is `flex: none` on the child of a flex
32
+ // scrollport, which is in `_viewport.scss`; a child that shrinks instead reports its minimum
33
+ // as its size, which is the one thing a viewport exists to avoid.
34
+ //
35
+ // `scroll-to-focus` is the other property, and it is `gtk_viewport_scroll_to` driven by
36
+ // `notify::focus-widget`: when the focus widget changes, the viewport moves its adjustment
37
+ // so the focused widget is inside the window (`focus_change_handler`, gtkviewport.c:620-643).
38
+ // A `focusin` on the host is the same reach — it fires for every focus change inside this
39
+ // subtree and nowhere else, which is also the `FOCUS_WITHIN` test the C applies first
40
+ // (gtkviewport.c:625-626). `scrollIntoView` then replaces `gtk_scroll_info_compute_scroll`
41
+ // and the two animated `set_value` calls (gtkviewport.c:742-758).
42
+ //
43
+ // NO SLOT BINDING, deliberately. The element HAS no internal structure: a child's place is
44
+ // the child list, so there is nothing to route it into, and `bindSlottedChildren` exists for
45
+ // the elements that built a box around their content. `<gtk-scrolled-window>`'s own binding
46
+ // targets THIS element, so the child's placement is one hop from the widget the author wrote
47
+ // it in.
48
+ //
49
+ // A11y: GTK gave the widget the role `GTK_ACCESSIBLE_ROLE_GENERIC` in 4.12
50
+ // (gtkviewport.c:353-354), so the element carries `role="generic"` and nothing else: what is
51
+ // inside the window is what a screen reader announces.
52
+ //
53
+ // NOT PORTED: `hscroll-policy` / `vscroll-policy` (`GtkScrollable`'s enum) choose whether
54
+ // `upper` is the child's MINIMUM size or its NATURAL size — the `GTK_SCROLL_MINIMUM` branch
55
+ // of the same `size_allocate` (gtkviewport.c:532-535, 541-544) — and a browser scrollport has
56
+ // exactly one content size. `hadjustment` / `vadjustment` are read-only properties here for
57
+ // the same reason `GtkViewport` keeps configuring them: it OWNS them
58
+ // (gtkviewport.c:479-500, `viewport_set_adjustment_values` reconfiguring on every allocation).
59
+ //
60
+ // Reference: refs/gtk/gtk/gtkviewport.c (size_allocate, viewport_set_adjustment_values,
61
+ // scroll_to_focus, focus_change_handler, scroll_to)
62
+ // Reference: refs/gtk/gtk/gtkscrolledwindow.c:785-793 (a non-scrollable child is wrapped)
63
+ // Copyright (c) The GTK Team. LGPLv2.1+.
64
+ // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
65
+
66
+ import { clampAdjustmentValue, normalizeAdjustment, type AdwAdjustment } from '@gjsify/adwaita-core';
67
+
68
+ /** `viewport_set_adjustment_values`'s two fractions of the visible size. */
69
+ const STEP_FRACTION = 0.1;
70
+ const PAGE_FRACTION = 0.9;
71
+
72
+ /** The two axes a scrollport reports numbers for, in the order GTK declares them. */
73
+ const AXES = ['horizontal', 'vertical'] as const;
74
+ type Axis = (typeof AXES)[number];
75
+
76
+ /** The adjustment of a scrollport on one axis — GTK's configure, read off the DOM. */
77
+ function axisAdjustment(clip: HTMLElement, axis: Axis): AdwAdjustment {
78
+ const vertical = axis === 'vertical';
79
+ const viewportSize = vertical ? clip.clientHeight : clip.clientWidth;
80
+ const contentSize = vertical ? clip.scrollHeight : clip.scrollWidth;
81
+ const value = vertical ? clip.scrollTop : clip.scrollLeft;
82
+ return normalizeAdjustment({
83
+ value,
84
+ lower: 0,
85
+ // GTK holds `upper` at the child size and lets `set_value`'s clamp deal with a
86
+ // content smaller than the window; `normalizeAdjustment` wants `upper >= lower`,
87
+ // so the larger of the two is what the range is written with.
88
+ upper: Math.max(contentSize, viewportSize),
89
+ stepIncrement: viewportSize * STEP_FRACTION,
90
+ pageIncrement: viewportSize * PAGE_FRACTION,
91
+ pageSize: viewportSize,
92
+ });
93
+ }
94
+
95
+ export class GtkViewport extends HTMLElement {
96
+ private _observer: ResizeObserver | null = null;
97
+ private _watch: MutationObserver | null = null;
98
+
99
+ static get observedAttributes() {
100
+ return ['scroll-to-focus'];
101
+ }
102
+
103
+ /**
104
+ * `GtkScrollable:hadjustment` — the horizontal position, as the portable value.
105
+ *
106
+ * Computed on read rather than stored: `viewport_set_adjustment_values` reconfigures both
107
+ * axes on every allocation, so there is no state to cache and nothing an author can write.
108
+ */
109
+ get hadjustment(): AdwAdjustment {
110
+ return axisAdjustment(this, 'horizontal');
111
+ }
112
+
113
+ /** `GtkScrollable:vadjustment` — the vertical position, as the portable value. */
114
+ get vadjustment(): AdwAdjustment {
115
+ return axisAdjustment(this, 'vertical');
116
+ }
117
+
118
+ /**
119
+ * `GtkViewport:scroll-to-focus` — whether the viewport keeps the focus widget in view.
120
+ * TRUE in the pspec (gtkviewport.c:338-340), and an unknown value is the DEFAULT rather
121
+ * than "off": only the literal `false` turns it off, which is what FALSE in a construct
122
+ * bag means and what a boolean attribute has to spell out to be read at all.
123
+ */
124
+ get scrollToFocus(): boolean {
125
+ return this.getAttribute('scroll-to-focus') !== 'false';
126
+ }
127
+
128
+ set scrollToFocus(value: boolean) {
129
+ this.setAttribute('scroll-to-focus', value ? 'true' : 'false');
130
+ }
131
+
132
+ connectedCallback() {
133
+ this.setAttribute('role', 'generic');
134
+ // `scroll` does not bubble, so the platform's own is heard here, on the node that
135
+ // raised it, and re-announced as GTK's `GtkAdjustment::value-changed`.
136
+ this.addEventListener('scroll', this._announce, { passive: true });
137
+ this._observe();
138
+ this._bindFocus();
139
+ }
140
+
141
+ disconnectedCallback() {
142
+ this.removeEventListener('scroll', this._announce);
143
+ this.removeEventListener('focusin', this._onFocusIn);
144
+ }
145
+
146
+ /**
147
+ * Announce whenever a number the adjustments are made of has changed.
148
+ *
149
+ * GTK reconfigures both axes on every allocation — `viewport_set_adjustment_values` runs
150
+ * out of `size_allocate`, and `gtk_adjustment_configure` compresses the notifications into
151
+ * one. A browser reports the same event as the scrollport's own box changing (this
152
+ * element) and its CONTENT changing (the children), and neither is the other's: a child
153
+ * that grows leaves the viewport's box exactly where it was, which is why BOTH are watched.
154
+ * The content watcher is what makes a scrolled window's bars appear the moment content
155
+ * lands in it, and it is `gtk_widget_queue_resize` on the child propagating upward.
156
+ */
157
+ private _observe(): void {
158
+ if (this._observer !== null) return;
159
+ this._observer = new ResizeObserver(() => this._announce());
160
+ this._observer.observe(this);
161
+ for (const child of this.children) this._observer.observe(child);
162
+ // A child added or removed is a size to watch; `observe` is idempotent, so the ones
163
+ // already watched are simply named again.
164
+ this._watch = new MutationObserver(() => {
165
+ for (const child of this.children) this._observer?.observe(child);
166
+ this._announce();
167
+ });
168
+ this._watch.observe(this, { childList: true });
169
+ }
170
+
171
+ attributeChangedCallback(name: string) {
172
+ // The handler's own registration depends on the value, so a change re-binds it — the
173
+ // shape `scripts/check-adwaita-connect-rebind.mjs` accepts. GTK notifies here too
174
+ // (gtkviewport.c:617), and a property notify is the whole of that call.
175
+ this.dispatchEvent(
176
+ new CustomEvent(`notify::${name}`, { bubbles: true, detail: { [name]: this.scrollToFocus } }),
177
+ );
178
+ if (!this.isConnected) return;
179
+ this._bindFocus();
180
+ }
181
+
182
+ /**
183
+ * `gtk_adjustment_set_value` on one of the viewport's OWN adjustments — the half of a
184
+ * scrolled window's job that moves content rather than deciding whether to.
185
+ *
186
+ * The value is clamped first, which is `set_value`'s documented behaviour ("The value is
187
+ * clamped to lie between lower and upper") and the reason a bar may ask for a position its
188
+ * own range has just outgrown.
189
+ */
190
+ scrollAxis(axis: Axis, value: number): void {
191
+ const adjustment = axisAdjustment(this, axis);
192
+ const next = clampAdjustmentValue(adjustment, value);
193
+ if (next === adjustment.value) return;
194
+ if (axis === 'vertical') this.scrollTop = next;
195
+ else this.scrollLeft = next;
196
+ this._announce();
197
+ }
198
+
199
+ /**
200
+ * `gtk_viewport_scroll_to` — move the position so `descendant` is inside the window.
201
+ * `null` asks only for the position to be CLAMPED, which is what a content that shrank
202
+ * needs from a value that was legal a moment ago.
203
+ *
204
+ * GTK animates the move (`gtk_adjustment_animate_to_value`, gtkviewport.c:757-758) for
205
+ * `ANIMATION_DURATION` unless the widget should not animate — the browser's spelling of
206
+ * that setting is `prefers-reduced-motion`, and `scrollIntoView`'s own `behavior` is where
207
+ * the choice belongs.
208
+ *
209
+ * `scrollTo` IS a platform method with an options form, so both are here and the KIND of
210
+ * the argument decides: a node is GTK's `scroll_to(descendant)` and anything else is the
211
+ * scrollport's own `scrollTo(options)`, which this element is (one element for the two
212
+ * GTK nodes is the port's own decision, see the header).
213
+ */
214
+ scrollTo(descendant: Element | null): void;
215
+ scrollTo(options?: ScrollToOptions): void;
216
+ scrollTo(x: number, y: number): void;
217
+ scrollTo(descendantOrOptions?: Element | ScrollToOptions | number | null, y?: number): void {
218
+ if (!(descendantOrOptions instanceof Element) && descendantOrOptions !== null) {
219
+ Element.prototype.scrollTo.call(this, descendantOrOptions as ScrollToOptions | undefined, y);
220
+ return;
221
+ }
222
+ const descendant = descendantOrOptions;
223
+ if (
224
+ descendant !== null &&
225
+ descendant !== this &&
226
+ typeof (descendant as HTMLElement).scrollIntoView === 'function'
227
+ ) {
228
+ (descendant as HTMLElement).scrollIntoView({
229
+ block: 'nearest',
230
+ inline: 'nearest',
231
+ behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',
232
+ });
233
+ this._announce();
234
+ return;
235
+ }
236
+ for (const axis of AXES) {
237
+ const adjustment = axisAdjustment(this, axis);
238
+ const clamped = clampAdjustmentValue(adjustment, adjustment.value);
239
+ if (clamped === adjustment.value) continue;
240
+ if (axis === 'vertical') this.scrollTop = clamped;
241
+ else this.scrollLeft = clamped;
242
+ }
243
+ this._announce();
244
+ }
245
+
246
+ /** The adjustments of both axes, as the detail of the `value-changed` event. */
247
+ get adjustments(): { hadjustment: AdwAdjustment; vadjustment: AdwAdjustment } {
248
+ return { hadjustment: this.hadjustment, vadjustment: this.vadjustment };
249
+ }
250
+
251
+ /**
252
+ * One event for both adjustments: a browser scroll moves them together, and the C's
253
+ * `GtkAdjustment::value-changed` is a signal of the ADJUSTMENT rather than of the viewport
254
+ * that reads it. Bubbling, so `<gtk-scrolled-window>` hears it without holding a reference
255
+ * to anything in here.
256
+ */
257
+ private _announce = (): void => {
258
+ this.dispatchEvent(new CustomEvent('value-changed', { bubbles: true, detail: this.adjustments }));
259
+ };
260
+
261
+ /** `setup_focus_change_handler` / `clear_focus_change_handler`, on one event instead of a root signal. */
262
+ private _bindFocus(): void {
263
+ this.removeEventListener('focusin', this._onFocusIn);
264
+ if (!this.scrollToFocus) return;
265
+ this.addEventListener('focusin', this._onFocusIn);
266
+ }
267
+
268
+ private _onFocusIn = (event: Event): void => {
269
+ const target = event.target;
270
+ // The event came from inside, which is the C's `FOCUS_WITHIN` test; a target that is
271
+ // the host itself is the viewport taking focus, not a widget to reveal.
272
+ if (!(target instanceof Element) || target === this) return;
273
+ this.scrollTo(target);
274
+ };
275
+ }
276
+
277
+ customElements.define('gtk-viewport', GtkViewport);
@@ -0,0 +1,341 @@
1
+ // <gtk-window-controls> — the window-frame buttons of ONE side: the window icon and the
2
+ // minimize, maximize and close buttons, in the order the decoration layout names them.
3
+ // GTK puts one of these at each end of a header bar and binds the bar's `decoration-layout`
4
+ // to both (gtkheaderbar.c:222-249), which is why `side` exists at all.
5
+ //
6
+ // WHAT IT IS NOT: A TOPLEVEL. `update_window_buttons` opens with the root check and
7
+ // RETURNS on it — no `GtkWindow` above, `set_empty (self, TRUE)` and nothing else
8
+ // (gtkwindowcontrols.c:257-264). A `GtkWindowControls` outside a window therefore draws
9
+ // nothing, and the story on /gjsify/gtk/windows/ puts one inside a `<gtk-window>` for
10
+ // exactly that reason. A browser has no toplevel and no window manager to ask, so the ROOT
11
+ // here is the nearest `<gtk-window>`/`<adw-window>` ancestor: this is the widget's DECISION
12
+ // LOGIC run against an in-page frame, not a window.
13
+ //
14
+ // WHICH BUTTONS, IN WHICH ORDER — `update_window_buttons` (gtkwindowcontrols.c:247-426):
15
+ // · the layout is split on the FIRST ':' into a start half and an end half, and a layout
16
+ // with no ':' is the SAME half on both sides (:141-166). `side` picks which half and
17
+ // defaults to `start` (`GTK_PACK_START`, :68); setting it swaps the `start`/`end` style
18
+ // classes exactly as `gtk_window_controls_set_side` does (:496-522).
19
+ // · the half is split on ',' and each token builds one child, or none:
20
+ // `icon` — a `GtkImage`, SOVEREIGN WINDOWS ONLY: a modal window or one with a
21
+ // `transient-for` has no window icon of its own, and the image is
22
+ // dropped altogether when there is no icon to show (:299-317)
23
+ // `minimize` — `window-minimize-symbolic`, sovereign only (:318-341)
24
+ // `maximize` — `window-maximize-symbolic`, or `window-restore-symbolic` while the
25
+ // window is `maximized`, and only while the window is `resizable`
26
+ // (:342-367)
27
+ // `close` — `window-close-symbolic`, and only while the window is `deletable`
28
+ // (:368-393)
29
+ // A token that names none of them builds nothing, which is what the tail of the loop
30
+ // does with it (:416-419).
31
+ // · `empty` is TRUE when no token produced a button. It is a real GObject property with a
32
+ // notify of its own (`set_empty`, :216-230), so it is read, styled and notified here.
33
+ //
34
+ // WHERE THE LAYOUT COMES FROM, AND WHY THAT IS THE ONE DIVERGENCE. `get_layout` takes this
35
+ // widget's own `decoration-layout`, and failing that the DISPLAY-WIDE
36
+ // `Gtk.Settings:gtk-decoration-layout` (:129-140). A browser page has no settings object,
37
+ // so the port's second stop is the FRAME's own `decoration-layout` — the nearest thing a
38
+ // page has to a display-wide default, and the one value a page author can actually reach.
39
+ // With neither, the default is GNOME's `:minimize,maximize,close`, which is the layout
40
+ // libadwaita's own rules are drawn against (`_header-bar.scss:175-207` styles the three
41
+ // buttons and nothing else).
42
+ //
43
+ // WHAT RE-RENDERS IT. `window_notify_cb` (:428-441) re-runs `update_window_buttons` for six
44
+ // window properties — `deletable`, `icon-name`, `maximized`, `modal`, `resizable`,
45
+ // `transient-for` — and `gtk_window_controls_root` (:443-467) adds `notify::gtk-decoration-layout`
46
+ // from the settings. All seven are observed here, the last one through the frame.
47
+ //
48
+ // KEYS: GTK sets `can_focus = FALSE` on all three buttons (:333, :364, :394) because the
49
+ // window manager owns those actions. NOT PORTED, AND DELIBERATELY: a page has no window
50
+ // manager, and taking the only close/minimize affordance out of the tab order would leave a
51
+ // keyboard user unable to reach them at all. The buttons stay ordinary buttons.
52
+ //
53
+ // A11Y: `role="group"`, GtkWindowControls's own accessible role (:95). Each button carries
54
+ // the LABEL GTK gives it (`Minimize` / `Maximize` / `Close`, :337, :362, :390) and the icon
55
+ // node is `aria-hidden`, as GTK marks those images `presentation`.
56
+ //
57
+ // Events: `notify::<prop>` (CustomEvent, bubbles, detail `{ <prop>: value }`) on every real
58
+ // change, and `window-action` (CustomEvent, bubbles, detail `{ action }`) when a button is
59
+ // pressed — the `GtkActionable` action name of the button that was pressed
60
+ // (`window.minimize`, `window.toggle-maximized`, `window.close`). `<gtk-window>` listens for
61
+ // it and performs the default.
62
+ //
63
+ // Reference: refs/gtk/gtk/gtkwindowcontrols.c
64
+ // Reference: refs/libadwaita/src/stylesheet/widgets/_header-bar.scss (GtkWindowControls)
65
+ // Copyright (c) The GTK Team. LGPLv2.1+.
66
+ // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
67
+
68
+ import { createGtkImage } from './gtk-image.js';
69
+
70
+ /** `Gtk.PackType`, spelled as the GIR's enum nicks (gtkwindowcontrols.c:68). */
71
+ type PackSide = 'start' | 'end';
72
+
73
+ /** What a press asks for: the `GtkActionable` action name minus its `window.` prefix. */
74
+ export type WindowAction = 'minimize' | 'toggle-maximized' | 'close';
75
+
76
+ /** The button one token builds: its style class, its icon, its accessible name. */
77
+ interface ControlSpec {
78
+ readonly className: string;
79
+ readonly icon: string;
80
+ readonly label: string;
81
+ readonly action: WindowAction;
82
+ }
83
+
84
+ /** The three button glyphs, named as C names them (:333, :364, :377, :382). */
85
+ const TOKEN_BUTTONS: Record<'minimize' | 'maximize' | 'close', ControlSpec> = {
86
+ minimize: { className: 'minimize', icon: 'window-minimize-symbolic', label: 'Minimize', action: 'minimize' },
87
+ maximize: {
88
+ className: 'maximize',
89
+ icon: 'window-maximize-symbolic',
90
+ label: 'Maximize',
91
+ action: 'toggle-maximized',
92
+ },
93
+ close: { className: 'close', icon: 'window-close-symbolic', label: 'Close', action: 'close' },
94
+ };
95
+
96
+ /** What `maximize` draws while the window is maximized instead (gtkwindowcontrols.c:347-350). */
97
+ const RESTORE_ICON_NAME = 'window-restore-symbolic';
98
+ const RESTORE_LABEL = 'Restore';
99
+
100
+ /**
101
+ * GNOME's `Gtk.Settings:gtk-decoration-layout`, and the layout libadwaita's rules are drawn
102
+ * against. The port's stand-in for the settings object — see the header.
103
+ */
104
+ const DEFAULT_DECORATION_LAYOUT = ':minimize,maximize,close';
105
+
106
+ /**
107
+ * The window properties `window_notify_cb` re-renders on (:430-436), plus `decoration-layout`
108
+ * standing in for the settings notify of :445-447.
109
+ */
110
+ const WINDOW_ATTRIBUTES = [
111
+ 'decoration-layout',
112
+ 'deletable',
113
+ 'icon-name',
114
+ 'maximized',
115
+ 'modal',
116
+ 'resizable',
117
+ 'transient-for',
118
+ ];
119
+
120
+ /** `closest` over both window spellings — `Adw.Window` is a `GtkWindow` subclass. */
121
+ const WINDOW_SELECTOR = 'gtk-window, adw-window';
122
+
123
+ export class GtkWindowControls extends HTMLElement {
124
+ private _initialized = false;
125
+ private _empty = true;
126
+ /** The frame this controls answers to, or `null` outside one (`:257-263`). */
127
+ private _window: Element | null = null;
128
+ private _windowObserver: MutationObserver | null = null;
129
+
130
+ static get observedAttributes() {
131
+ return ['decoration-layout', 'side', 'use-native-controls'];
132
+ }
133
+
134
+ /** `Gtk.WindowControls:side` — which half of the decoration layout to build. */
135
+ get side(): PackSide {
136
+ return this.getAttribute('side') === 'end' ? 'end' : 'start';
137
+ }
138
+
139
+ set side(value: PackSide) {
140
+ this._write('side', value);
141
+ }
142
+
143
+ /** `Gtk.WindowControls:use-native-controls` — a macOS-only branch; see KNOWN_GAPS. */
144
+ get useNativeControls(): boolean {
145
+ return this.hasAttribute('use-native-controls');
146
+ }
147
+
148
+ /**
149
+ * `Gtk.WindowControls:empty` (read-only in GIR). TRUE — the GIR default (:618) — until a
150
+ * render finds a button, which is what happens outside a window.
151
+ */
152
+ get empty(): boolean {
153
+ return this._empty;
154
+ }
155
+
156
+ /** `setAttribute` for a write that changes nothing; every GObject setter here returns early. */
157
+ private _write(name: string, value: string): void {
158
+ if (this.getAttribute(name) !== value) this.setAttribute(name, value);
159
+ }
160
+
161
+ connectedCallback() {
162
+ if (this._initialized) {
163
+ this._followWindow();
164
+ return;
165
+ }
166
+ this._initialized = true;
167
+
168
+ this.setAttribute('role', 'group');
169
+ this._followWindow();
170
+ }
171
+
172
+ disconnectedCallback() {
173
+ this._windowObserver?.disconnect();
174
+ this._windowObserver = null;
175
+ this._window = null;
176
+ }
177
+
178
+ attributeChangedCallback(name: string) {
179
+ if (!this._initialized) return;
180
+ this._render();
181
+ this.dispatchEvent(
182
+ new CustomEvent(`notify::${name}`, { bubbles: true, detail: { [name]: this._property(name) } }),
183
+ );
184
+ }
185
+
186
+ private _property(name: string): boolean | string {
187
+ return name === 'side' ? this.side : this.hasAttribute(name);
188
+ }
189
+
190
+ /**
191
+ * Bind to the frame above, the way `gtk_window_controls_root` binds to the settings and
192
+ * to `notify` on the window (:443-462) — then run the build, which is that handler's
193
+ * first act. Re-established on every connect: a bar moved into another window has a new
194
+ * ancestor, which is why C does it in `root` and drops it in `unroot`.
195
+ */
196
+ private _followWindow() {
197
+ this._windowObserver?.disconnect();
198
+ this._applyEmptyClass();
199
+ this._window = this.closest(WINDOW_SELECTOR);
200
+ if (this._window !== null) {
201
+ this._windowObserver ??= new MutationObserver(() => this._render());
202
+ this._windowObserver.observe(this._window, { attributes: true, attributeFilter: WINDOW_ATTRIBUTES });
203
+ }
204
+ this._render();
205
+ }
206
+
207
+ /** `is_sovereign_window` (gtkwindowcontrols.c:272-273): neither modal nor transient. */
208
+ private _isSovereign(window: Element): boolean {
209
+ return !window.hasAttribute('modal') && !window.hasAttribute('transient-for');
210
+ }
211
+
212
+ /**
213
+ * The window property, in ITS polarity. `resizable` and `deletable` default TRUE
214
+ * (gtkwindow.c:893, :1024) and `maximized` defaults FALSE (:1044), so one helper reading
215
+ * them alike would put a maximize button on a window that cannot be maximized — and a
216
+ * restore glyph on every window there is.
217
+ */
218
+ private _windowFlag(element: Element, name: 'resizable' | 'deletable'): boolean {
219
+ return element.getAttribute(name) !== 'false';
220
+ }
221
+
222
+ /**
223
+ * `get_layout` (gtkwindowcontrols.c:120-176), reduced to what a page can answer: this
224
+ * widget's own value, then the frame's, then GNOME's default. The first ':' splits the
225
+ * layout in two, and a layout with no ':' is the same half on both sides.
226
+ */
227
+ private _layoutHalf(): string | null {
228
+ const layout =
229
+ this.getAttribute('decoration-layout') ??
230
+ this._window?.getAttribute('decoration-layout') ??
231
+ DEFAULT_DECORATION_LAYOUT;
232
+ if (layout === '') return null;
233
+ const colon = layout.indexOf(':');
234
+ if (colon === -1) return layout;
235
+ return this.side === 'start' ? layout.slice(0, colon) : layout.slice(colon + 1);
236
+ }
237
+
238
+ /** `update_window_buttons` (gtkwindowcontrols.c:247-426), token for token. */
239
+ private _render(): void {
240
+ // `gtk_window_controls_set_side` moves the style class as part of the same rebuild
241
+ // (:496-522), so both halves of that setter live here.
242
+ this.classList.toggle('start', this.side === 'start');
243
+ this.classList.toggle('end', this.side === 'end');
244
+ // `clear_controls` runs before anything is built (:271), so a window that stopped
245
+ // being resizable loses its maximize button rather than keeping a stale one.
246
+ this.replaceChildren();
247
+ this._applyEmptyClass();
248
+
249
+ const window = this._window;
250
+ if (window === null) {
251
+ this._setEmpty(true);
252
+ return;
253
+ }
254
+ const sovereign = this._isSovereign(window);
255
+ const half = this._layoutHalf();
256
+ if (half === null) {
257
+ this._setEmpty(true);
258
+ return;
259
+ }
260
+
261
+ let empty = true;
262
+ for (const token of half.split(',')) {
263
+ const button = this._buttonFor(token, window, sovereign);
264
+ if (button === null) continue;
265
+ this.appendChild(button);
266
+ empty = false;
267
+ }
268
+ this._setEmpty(empty);
269
+ }
270
+
271
+ /** Every render writes the class, whether the VALUE moved or not — see {@link _setEmpty}. */
272
+ private _applyEmptyClass(): void {
273
+ this.classList.toggle('empty', this._empty);
274
+ }
275
+
276
+ /** The one child a token builds, or `null` for the tokens that build nothing here. */
277
+ private _buttonFor(token: string, window: Element, sovereign: boolean): HTMLElement | null {
278
+ if (token === 'icon') return sovereign ? this._windowIcon(window) : null;
279
+ if (token === 'minimize') return sovereign ? this._controlButton(TOKEN_BUTTONS.minimize) : null;
280
+ if (token === 'maximize') {
281
+ if (!sovereign || !this._windowFlag(window, 'resizable')) return null;
282
+ // :347-350 — the glyph and the label follow the window's state, not the token.
283
+ const maximized = window.hasAttribute('maximized');
284
+ return this._controlButton(
285
+ maximized
286
+ ? { ...TOKEN_BUTTONS.maximize, icon: RESTORE_ICON_NAME, label: RESTORE_LABEL }
287
+ : TOKEN_BUTTONS.maximize,
288
+ );
289
+ }
290
+ if (token === 'close') {
291
+ return this._windowFlag(window, 'deletable') ? this._controlButton(TOKEN_BUTTONS.close) : null;
292
+ }
293
+ return null;
294
+ }
295
+
296
+ /**
297
+ * `update_window_icon` (gtkwindowcontrols.c:187-210): the icon node is `presentation` to
298
+ * an accessibility tree, and it is dropped when the window has no icon — which for a
299
+ * frame means no `icon-name`, the analogue of a window with no `GdkPaintable`.
300
+ */
301
+ private _windowIcon(window: Element): HTMLElement | null {
302
+ const name = window.getAttribute('icon-name');
303
+ return name === null || name === '' ? null : createGtkImage(name, 'icon');
304
+ }
305
+
306
+ /**
307
+ * One `GtkButton` with its `GtkImage` child. A plain `<button>` rather than
308
+ * `<gtk-button>`: libadwaita's own rules select `windowcontrols > button > image`
309
+ * (refs/libadwaita/src/stylesheet/widgets/_header-bar.scss:178-202), so the button IS
310
+ * the widget node here, and `<gtk-button>` would wrap a second one inside it.
311
+ */
312
+ private _controlButton(spec: ControlSpec): HTMLElement {
313
+ const button = document.createElement('button');
314
+ button.className = `adw-window-controls-button ${spec.className}`;
315
+ button.setAttribute('aria-label', spec.label);
316
+ button.title = spec.label;
317
+ button.appendChild(createGtkImage(spec.icon));
318
+ button.addEventListener('click', () =>
319
+ this.dispatchEvent(new CustomEvent('window-action', { bubbles: true, detail: { action: spec.action } })),
320
+ );
321
+ return button;
322
+ }
323
+
324
+ /**
325
+ * `set_empty` (gtkwindowcontrols.c:216-230): a style class, and a notify of its own.
326
+ *
327
+ * The early return is about the NOTIFY, and it is the whole of C's `if (empty ==
328
+ * self->empty) return;`. The CLASS is not part of that comparison here: `empty` starts
329
+ * TRUE, so the first render outside a window is a no-op against C and the widget was
330
+ * left wearing nothing at all — a property whose style class only appears once it has
331
+ * been wrong and then right again.
332
+ */
333
+ private _setEmpty(empty: boolean): void {
334
+ if (empty === this._empty) return;
335
+ this._empty = empty;
336
+ this._applyEmptyClass();
337
+ this.dispatchEvent(new CustomEvent('notify::empty', { bubbles: true, detail: { empty } }));
338
+ }
339
+ }
340
+
341
+ customElements.define('gtk-window-controls', GtkWindowControls);