@gjsify/adwaita-web 0.64.1 → 0.64.2-next.254

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 (335) hide show
  1. package/README.md +22 -88
  2. package/dist/adwaita-web.css +91 -12
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/accessible.d.ts +8 -0
  5. package/lib/types/application.d.ts +1 -1
  6. package/lib/types/child-slot.spec.d.ts +1 -0
  7. package/lib/types/compute-expand.d.ts +5 -0
  8. package/lib/types/compute-expand.spec.d.ts +1 -0
  9. package/lib/types/container-fill.spec.d.ts +1 -0
  10. package/lib/types/elements/adw-about-dialog.d.ts +28 -1
  11. package/lib/types/elements/adw-action-row.d.ts +2 -1
  12. package/lib/types/elements/adw-alert-dialog.d.ts +80 -19
  13. package/lib/types/elements/adw-avatar.d.ts +2 -1
  14. package/lib/types/elements/adw-banner.d.ts +2 -1
  15. package/lib/types/elements/adw-bin.d.ts +2 -1
  16. package/lib/types/elements/adw-bottom-sheet.d.ts +5 -4
  17. package/lib/types/elements/adw-breakpoint-bin.d.ts +2 -1
  18. package/lib/types/elements/adw-button-content.d.ts +2 -1
  19. package/lib/types/elements/adw-button-row.d.ts +2 -1
  20. package/lib/types/elements/adw-card.d.ts +2 -1
  21. package/lib/types/elements/adw-carousel.d.ts +20 -3
  22. package/lib/types/elements/adw-clamp-scrollable.d.ts +2 -1
  23. package/lib/types/elements/adw-clamp.d.ts +2 -1
  24. package/lib/types/elements/adw-combo-row.d.ts +6 -1
  25. package/lib/types/elements/adw-data-grid.d.ts +2 -1
  26. package/lib/types/elements/adw-dialog.d.ts +2 -1
  27. package/lib/types/elements/adw-entry-row.d.ts +2 -1
  28. package/lib/types/elements/adw-expander-row.d.ts +2 -1
  29. package/lib/types/elements/adw-header-bar.d.ts +2 -1
  30. package/lib/types/elements/adw-inline-view-switcher.d.ts +3 -2
  31. package/lib/types/elements/adw-layout-slot.d.ts +2 -1
  32. package/lib/types/elements/adw-multi-layout-view.d.ts +2 -1
  33. package/lib/types/elements/adw-navigation-split-view.d.ts +2 -1
  34. package/lib/types/elements/adw-navigation-view.d.ts +3 -2
  35. package/lib/types/elements/adw-overlay-split-view.d.ts +2 -1
  36. package/lib/types/elements/adw-preferences-dialog.d.ts +5 -4
  37. package/lib/types/elements/adw-preferences-group.d.ts +2 -1
  38. package/lib/types/elements/adw-preferences-row.d.ts +2 -1
  39. package/lib/types/elements/adw-shortcut-label.d.ts +2 -1
  40. package/lib/types/elements/adw-shortcuts-item.d.ts +2 -1
  41. package/lib/types/elements/adw-shortcuts-section.d.ts +2 -1
  42. package/lib/types/elements/adw-sidebar.d.ts +4 -3
  43. package/lib/types/elements/adw-spin-row.d.ts +2 -1
  44. package/lib/types/elements/adw-spinner.d.ts +2 -1
  45. package/lib/types/elements/adw-split-button.d.ts +2 -1
  46. package/lib/types/elements/adw-status-page.d.ts +2 -1
  47. package/lib/types/elements/adw-switch-row.d.ts +4 -1
  48. package/lib/types/elements/adw-tab-bar.d.ts +2 -1
  49. package/lib/types/elements/adw-tab-button.d.ts +2 -1
  50. package/lib/types/elements/adw-tab-overview.d.ts +2 -1
  51. package/lib/types/elements/adw-tab-view.d.ts +3 -2
  52. package/lib/types/elements/adw-toast-overlay.d.ts +10 -30
  53. package/lib/types/elements/adw-toggle-group.d.ts +3 -2
  54. package/lib/types/elements/adw-toolbar-view.d.ts +2 -1
  55. package/lib/types/elements/adw-view-stack.d.ts +3 -1
  56. package/lib/types/elements/adw-view-switcher-bar.d.ts +2 -1
  57. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +2 -1
  58. package/lib/types/elements/adw-view-switcher.d.ts +3 -2
  59. package/lib/types/elements/adw-window-features.d.ts +8 -0
  60. package/lib/types/elements/adw-window-title.d.ts +2 -1
  61. package/lib/types/elements/adw-wrap-box.d.ts +2 -1
  62. package/lib/types/elements/checks.d.ts +16 -1
  63. package/lib/types/elements/gtk-action-bar.d.ts +2 -1
  64. package/lib/types/elements/gtk-application-window.d.ts +1 -4
  65. package/lib/types/elements/gtk-aspect-frame.d.ts +2 -1
  66. package/lib/types/elements/gtk-box.d.ts +5 -1
  67. package/lib/types/elements/gtk-button.d.ts +5 -7
  68. package/lib/types/elements/gtk-calendar.d.ts +2 -1
  69. package/lib/types/elements/gtk-center-box.d.ts +2 -1
  70. package/lib/types/elements/gtk-color-dialog-button.d.ts +2 -1
  71. package/lib/types/elements/gtk-drag-icon.d.ts +2 -1
  72. package/lib/types/elements/gtk-drawing-area.d.ts +2 -1
  73. package/lib/types/elements/gtk-drop-down.d.ts +2 -1
  74. package/lib/types/elements/gtk-editable-label.d.ts +2 -1
  75. package/lib/types/elements/gtk-emoji-chooser.d.ts +2 -0
  76. package/lib/types/elements/gtk-entry.d.ts +2 -1
  77. package/lib/types/elements/gtk-expander.d.ts +2 -1
  78. package/lib/types/elements/gtk-fixed.d.ts +2 -1
  79. package/lib/types/elements/gtk-flow-box-child.d.ts +2 -1
  80. package/lib/types/elements/gtk-flow-box.d.ts +2 -1
  81. package/lib/types/elements/gtk-font-dialog-button.d.ts +2 -1
  82. package/lib/types/elements/gtk-frame.d.ts +2 -1
  83. package/lib/types/elements/gtk-gl-area.d.ts +2 -1
  84. package/lib/types/elements/gtk-graphics-offload.d.ts +2 -1
  85. package/lib/types/elements/gtk-grid.d.ts +2 -1
  86. package/lib/types/elements/gtk-header-bar.d.ts +2 -1
  87. package/lib/types/elements/gtk-image.d.ts +5 -6
  88. package/lib/types/elements/gtk-inscription.d.ts +2 -1
  89. package/lib/types/elements/gtk-label.d.ts +10 -3
  90. package/lib/types/elements/gtk-level-bar.d.ts +2 -1
  91. package/lib/types/elements/gtk-list-box-row.d.ts +2 -1
  92. package/lib/types/elements/gtk-list-box.d.ts +2 -1
  93. package/lib/types/elements/gtk-list-view.d.ts +2 -1
  94. package/lib/types/elements/gtk-media-controls.d.ts +2 -1
  95. package/lib/types/elements/gtk-menu-button.d.ts +2 -1
  96. package/lib/types/elements/gtk-notebook.d.ts +2 -1
  97. package/lib/types/elements/gtk-overlay.d.ts +2 -1
  98. package/lib/types/elements/gtk-paned.d.ts +2 -1
  99. package/lib/types/elements/gtk-picture.d.ts +2 -1
  100. package/lib/types/elements/gtk-popover-bin.d.ts +2 -1
  101. package/lib/types/elements/gtk-popover-menu-bar.d.ts +2 -1
  102. package/lib/types/elements/gtk-popover-menu.d.ts +2 -0
  103. package/lib/types/elements/gtk-popover.d.ts +7 -1
  104. package/lib/types/elements/gtk-progress-bar.d.ts +2 -1
  105. package/lib/types/elements/gtk-revealer.d.ts +2 -1
  106. package/lib/types/elements/gtk-scale-button.d.ts +2 -1
  107. package/lib/types/elements/gtk-scale.d.ts +2 -1
  108. package/lib/types/elements/gtk-scrollbar.d.ts +2 -1
  109. package/lib/types/elements/gtk-scrolled-window.d.ts +2 -1
  110. package/lib/types/elements/gtk-search-bar.d.ts +2 -1
  111. package/lib/types/elements/gtk-separator.d.ts +2 -1
  112. package/lib/types/elements/gtk-spin-button.d.ts +2 -1
  113. package/lib/types/elements/gtk-spinner.d.ts +2 -1
  114. package/lib/types/elements/gtk-stack-page.d.ts +29 -0
  115. package/lib/types/elements/gtk-stack-sidebar.d.ts +2 -1
  116. package/lib/types/elements/gtk-stack-switcher.d.ts +2 -1
  117. package/lib/types/elements/gtk-stack.d.ts +13 -1
  118. package/lib/types/elements/gtk-switch.d.ts +2 -1
  119. package/lib/types/elements/gtk-text-view.d.ts +2 -1
  120. package/lib/types/elements/gtk-text.d.ts +2 -1
  121. package/lib/types/elements/gtk-tree-expander.d.ts +2 -1
  122. package/lib/types/elements/gtk-video.d.ts +2 -1
  123. package/lib/types/elements/gtk-viewport.d.ts +2 -1
  124. package/lib/types/elements/gtk-window-controls.d.ts +2 -1
  125. package/lib/types/elements/gtk-window-handle.d.ts +2 -1
  126. package/lib/types/elements/gtk-window.d.ts +18 -2
  127. package/lib/types/gobject-elements.d.ts +15 -0
  128. package/lib/types/gtksource/gtk-source-view.d.ts +2 -1
  129. package/lib/types/{icon-registry.spec.d.ts → icon-masks.spec.d.ts} +1 -1
  130. package/lib/types/icon-theme.d.ts +15 -35
  131. package/lib/types/index.d.ts +3 -4
  132. package/lib/types/learn6502-window.spec.d.ts +1 -0
  133. package/lib/types/namespace/adw.d.ts +1 -1
  134. package/lib/types/namespace/gdk.d.ts +1 -0
  135. package/lib/types/namespace/gio.d.ts +1 -0
  136. package/lib/types/namespace/glib.d.ts +2 -1
  137. package/lib/types/namespace/gtk.d.ts +2 -0
  138. package/lib/types/shared-tree-builder.d.ts +31 -0
  139. package/lib/types/signals.d.ts +5 -1
  140. package/lib/types/source-view/adw-source-view.d.ts +2 -1
  141. package/lib/types/styles.generated.d.ts +1 -1
  142. package/lib/types/widget-element.d.ts +25 -0
  143. package/lib/types/widget-map.spec.d.ts +1 -0
  144. package/lib/types/widget-mapping.d.ts +7 -0
  145. package/lib/types/widget-visible.spec.d.ts +1 -0
  146. package/lib/types/window-maximize.spec.d.ts +1 -0
  147. package/package.json +13 -12
  148. package/scss/_about_dialog.scss +46 -1
  149. package/scss/_bin.scss +6 -3
  150. package/scss/_checks.scss +5 -2
  151. package/scss/_icon.scss +3 -4
  152. package/scss/_labels.scss +2 -2
  153. package/scss/_overlay.scss +11 -1
  154. package/scss/_overlay_split_view.scss +10 -0
  155. package/scss/_reset.scss +1 -1
  156. package/scss/_scrolled_window.scss +15 -0
  157. package/scss/_stack.scss +8 -2
  158. package/scss/_toast.scss +13 -3
  159. package/scss/_widget.scss +5 -1
  160. package/scss/_window.scss +16 -0
  161. package/src/accessible.ts +29 -0
  162. package/src/adw-about-dialog.spec.ts +81 -12
  163. package/src/adw-action-rows.spec.ts +2 -2
  164. package/src/adw-alert-dialog.spec.ts +71 -5
  165. package/src/adw-bin.spec.ts +4 -3
  166. package/src/adw-button-content.spec.ts +1 -1
  167. package/src/adw-carousel.spec.ts +49 -1
  168. package/src/adw-header-bar.spec.ts +18 -0
  169. package/src/adw-toast-overlay.spec.ts +68 -24
  170. package/src/application.spec.ts +44 -0
  171. package/src/application.ts +13 -18
  172. package/src/capabilities.mjs +2 -3
  173. package/src/checks.spec.ts +51 -1
  174. package/src/child-slot.spec.ts +71 -0
  175. package/src/compute-expand.spec.ts +186 -0
  176. package/src/compute-expand.ts +134 -0
  177. package/src/construct-vectors.spec.ts +97 -7
  178. package/src/container-fill.spec.ts +70 -0
  179. package/src/elements/adw-about-dialog.ts +152 -9
  180. package/src/elements/adw-action-row.ts +2 -1
  181. package/src/elements/adw-alert-dialog.ts +177 -46
  182. package/src/elements/adw-application-window.ts +0 -3
  183. package/src/elements/adw-avatar.ts +2 -1
  184. package/src/elements/adw-banner.ts +2 -1
  185. package/src/elements/adw-bin.ts +2 -1
  186. package/src/elements/adw-bottom-sheet.ts +5 -4
  187. package/src/elements/adw-breakpoint-bin.ts +5 -1
  188. package/src/elements/adw-button-content.ts +2 -1
  189. package/src/elements/adw-button-row.ts +2 -1
  190. package/src/elements/adw-card.ts +2 -1
  191. package/src/elements/adw-carousel.ts +62 -3
  192. package/src/elements/adw-clamp-scrollable.ts +2 -1
  193. package/src/elements/adw-clamp.ts +2 -1
  194. package/src/elements/adw-combo-row.ts +15 -1
  195. package/src/elements/adw-data-grid.ts +2 -1
  196. package/src/elements/adw-dialog.ts +5 -2
  197. package/src/elements/adw-entry-row.ts +2 -1
  198. package/src/elements/adw-expander-row.ts +2 -1
  199. package/src/elements/adw-header-bar.ts +6 -1
  200. package/src/elements/adw-inline-view-switcher.ts +3 -2
  201. package/src/elements/adw-layout-slot.ts +2 -1
  202. package/src/elements/adw-multi-layout-view.ts +2 -1
  203. package/src/elements/adw-navigation-split-view.ts +2 -1
  204. package/src/elements/adw-navigation-view.ts +3 -2
  205. package/src/elements/adw-overlay-split-view.ts +2 -1
  206. package/src/elements/adw-preferences-dialog.ts +6 -4
  207. package/src/elements/adw-preferences-group.ts +2 -1
  208. package/src/elements/adw-preferences-row.ts +2 -1
  209. package/src/elements/adw-shortcut-label.ts +2 -1
  210. package/src/elements/adw-shortcuts-item.ts +2 -1
  211. package/src/elements/adw-shortcuts-section.ts +2 -1
  212. package/src/elements/adw-sidebar.ts +4 -3
  213. package/src/elements/adw-spin-row.ts +2 -1
  214. package/src/elements/adw-spinner.ts +2 -1
  215. package/src/elements/adw-split-button.ts +2 -1
  216. package/src/elements/adw-status-page.ts +2 -1
  217. package/src/elements/adw-switch-row.ts +10 -1
  218. package/src/elements/adw-tab-bar.ts +2 -1
  219. package/src/elements/adw-tab-button.ts +2 -1
  220. package/src/elements/adw-tab-overview.ts +2 -1
  221. package/src/elements/adw-tab-view.ts +3 -2
  222. package/src/elements/adw-toast-overlay.ts +37 -70
  223. package/src/elements/adw-toggle-group.ts +3 -2
  224. package/src/elements/adw-toolbar-view.ts +2 -1
  225. package/src/elements/adw-view-stack.ts +39 -1
  226. package/src/elements/adw-view-switcher-bar.ts +2 -1
  227. package/src/elements/adw-view-switcher-sidebar.ts +2 -1
  228. package/src/elements/adw-view-switcher.ts +3 -2
  229. package/src/elements/adw-window-features.ts +14 -2
  230. package/src/elements/adw-window-title.ts +2 -1
  231. package/src/elements/adw-wrap-box.ts +2 -1
  232. package/src/elements/checks.ts +85 -6
  233. package/src/elements/gtk-action-bar.ts +2 -1
  234. package/src/elements/gtk-application-window.ts +3 -8
  235. package/src/elements/gtk-aspect-frame.ts +2 -1
  236. package/src/elements/gtk-box.ts +56 -1
  237. package/src/elements/gtk-button.ts +10 -22
  238. package/src/elements/gtk-calendar.ts +2 -1
  239. package/src/elements/gtk-center-box.ts +2 -1
  240. package/src/elements/gtk-color-dialog-button.ts +2 -1
  241. package/src/elements/gtk-drag-icon.ts +6 -1
  242. package/src/elements/gtk-drawing-area.ts +2 -1
  243. package/src/elements/gtk-drop-down.ts +2 -1
  244. package/src/elements/gtk-editable-label.ts +2 -1
  245. package/src/elements/gtk-emoji-chooser.ts +5 -0
  246. package/src/elements/gtk-entry.ts +2 -1
  247. package/src/elements/gtk-expander.ts +2 -1
  248. package/src/elements/gtk-fixed.ts +2 -1
  249. package/src/elements/gtk-flow-box-child.ts +5 -2
  250. package/src/elements/gtk-flow-box.ts +2 -1
  251. package/src/elements/gtk-font-dialog-button.ts +2 -1
  252. package/src/elements/gtk-frame.ts +2 -1
  253. package/src/elements/gtk-gl-area.ts +2 -1
  254. package/src/elements/gtk-graphics-offload.ts +5 -2
  255. package/src/elements/gtk-grid.ts +2 -1
  256. package/src/elements/gtk-header-bar.ts +2 -1
  257. package/src/elements/gtk-image.ts +14 -15
  258. package/src/elements/gtk-inscription.ts +2 -1
  259. package/src/elements/gtk-label.ts +12 -5
  260. package/src/elements/gtk-level-bar.ts +2 -1
  261. package/src/elements/gtk-list-box-row.ts +5 -2
  262. package/src/elements/gtk-list-box.ts +2 -1
  263. package/src/elements/gtk-list-view.ts +2 -1
  264. package/src/elements/gtk-media-controls.ts +2 -1
  265. package/src/elements/gtk-menu-button.ts +2 -1
  266. package/src/elements/gtk-notebook.ts +2 -1
  267. package/src/elements/gtk-overlay.ts +7 -5
  268. package/src/elements/gtk-paned.ts +9 -2
  269. package/src/elements/gtk-picture.ts +2 -1
  270. package/src/elements/gtk-popover-bin.ts +5 -1
  271. package/src/elements/gtk-popover-menu-bar.ts +2 -1
  272. package/src/elements/gtk-popover-menu.ts +5 -0
  273. package/src/elements/gtk-popover.ts +14 -1
  274. package/src/elements/gtk-progress-bar.ts +2 -1
  275. package/src/elements/gtk-revealer.ts +2 -1
  276. package/src/elements/gtk-scale-button.ts +2 -1
  277. package/src/elements/gtk-scale.ts +2 -1
  278. package/src/elements/gtk-scrollbar.ts +2 -1
  279. package/src/elements/gtk-scrolled-window.ts +8 -5
  280. package/src/elements/gtk-search-bar.ts +7 -6
  281. package/src/elements/gtk-separator.ts +2 -1
  282. package/src/elements/gtk-spin-button.ts +2 -1
  283. package/src/elements/gtk-spinner.ts +2 -1
  284. package/src/elements/gtk-stack-page.ts +112 -0
  285. package/src/elements/gtk-stack-sidebar.ts +2 -1
  286. package/src/elements/gtk-stack-switcher.ts +2 -1
  287. package/src/elements/gtk-stack.ts +52 -6
  288. package/src/elements/gtk-switch.ts +2 -1
  289. package/src/elements/gtk-text-view.ts +2 -1
  290. package/src/elements/gtk-text.ts +2 -1
  291. package/src/elements/gtk-tree-expander.ts +5 -1
  292. package/src/elements/gtk-video.ts +2 -1
  293. package/src/elements/gtk-viewport.ts +6 -1
  294. package/src/elements/gtk-window-controls.ts +2 -1
  295. package/src/elements/gtk-window-handle.ts +6 -1
  296. package/src/elements/gtk-window.ts +57 -7
  297. package/src/gio.spec.ts +13 -3
  298. package/src/glib.spec.ts +9 -0
  299. package/src/gobject-elements.ts +44 -2
  300. package/src/gtk-label.spec.ts +39 -0
  301. package/src/gtk-scrolled-window.spec.ts +6 -6
  302. package/src/gtk-stack.spec.ts +170 -0
  303. package/src/gtk-value-doors.spec.ts +19 -0
  304. package/src/gtk-window.spec.ts +2 -2
  305. package/src/gtksource/gtk-source-view.ts +2 -1
  306. package/src/{icon-registry.spec.ts → icon-masks.spec.ts} +4 -100
  307. package/src/icon-theme.spec.ts +69 -124
  308. package/src/icon-theme.ts +29 -119
  309. package/src/index.ts +5 -9
  310. package/src/keyboard-operable.spec.ts +3 -3
  311. package/src/learn6502-window.spec.ts +113 -0
  312. package/src/namespace/adw.ts +1 -1
  313. package/src/namespace/gdk.ts +1 -0
  314. package/src/namespace/gio.ts +8 -0
  315. package/src/namespace/glib.ts +4 -0
  316. package/src/namespace/gtk.ts +2 -0
  317. package/src/shared-tree-builder.ts +131 -13
  318. package/src/shared-trees.spec.ts +15 -1
  319. package/src/signals.ts +11 -2
  320. package/src/slotted-children.spec.ts +24 -0
  321. package/src/slotted-children.ts +18 -2
  322. package/src/source-view/adw-source-view.ts +2 -1
  323. package/src/split-button.spec.ts +1 -1
  324. package/src/style-manager.spec.ts +13 -0
  325. package/src/style-manager.ts +6 -0
  326. package/src/styles.generated.ts +1 -1
  327. package/src/test.browser.mts +16 -2
  328. package/src/value-lists.spec.ts +3 -3
  329. package/src/widget-element.ts +75 -0
  330. package/src/widget-map.spec.ts +116 -0
  331. package/src/widget-mapping.ts +100 -0
  332. package/src/widget-visible.spec.ts +148 -0
  333. package/src/window-maximize.spec.ts +93 -0
  334. package/lib/types/icon-registry.d.ts +0 -37
  335. package/src/icon-registry.ts +0 -163
@@ -47,6 +47,7 @@
47
47
  import { normalizeBoxOrientation, type BoxOrientation } from '@gjsify/adwaita-core';
48
48
 
49
49
  import type { GtkStack } from './gtk-stack.js';
50
+ import { WidgetElement } from '../widget-element.js';
50
51
 
51
52
  /** The `GtkStackPageInfo` half this element reads — it never looks inside `child`. */
52
53
  interface SwitcherPage {
@@ -65,7 +66,7 @@ interface SwitcherButton {
65
66
  icon: HTMLElement;
66
67
  }
67
68
 
68
- export class GtkStackSwitcher extends HTMLElement {
69
+ export class GtkStackSwitcher extends WidgetElement {
69
70
  private _stack: GtkStack | null = null;
70
71
  private _initialized = false;
71
72
  private _nodes: SwitcherButton[] = [];
@@ -92,6 +92,8 @@
92
92
  // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
93
93
 
94
94
  import type { DispatchedSignals } from '../signals.js';
95
+ import { STACK_PAGE_ATTRIBUTES, pageChildOf } from './gtk-stack-page.js';
96
+ import { WidgetElement } from '../widget-element.js';
95
97
 
96
98
  /** `GtkStackTransitionType`, spelled as the GIR's enum nicks (gtkstack.c:95-121). */
97
99
  export type StackTransitionType =
@@ -225,7 +227,7 @@ export interface GtkStackPageInfo {
225
227
  /** The five `GtkStackPage` string/boolean properties this element reads off a child. */
226
228
  const PAGE_ATTRIBUTES = ['name', 'title', 'icon-name', 'use-underline', 'needs-attention'];
227
229
 
228
- export class GtkStack extends HTMLElement {
230
+ export class GtkStack extends WidgetElement {
229
231
  /** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
230
232
  static readonly signals: DispatchedSignals = {
231
233
  'notify::visible-child': 'notify::visible-child',
@@ -416,7 +418,7 @@ export class GtkStack extends HTMLElement {
416
418
  }
417
419
  this._initialized = true;
418
420
  this.setAttribute('role', 'group');
419
- for (const child of this._children()) this._initChild(child);
421
+ for (const child of this._children()) this._initChild(this._adopt(child));
420
422
  // `add_page` connects the stack to each page's `notify::visible`
421
423
  // (gtkstack.c:1771), which is the browser's mutation observer.
422
424
  this._observer = new MutationObserver((records) => this._onChildMutations(records));
@@ -531,12 +533,44 @@ export class GtkStack extends HTMLElement {
531
533
  return this._children().find((child) => this._page(child).visible) ?? null;
532
534
  }
533
535
 
536
+ /**
537
+ * A `<gtk-stack-page>` record, adopted: its child takes the record's place and its properties
538
+ * as the attributes this element reads, the way `<adw-view-stack>` adopts a declared page.
539
+ * Any other child is already the page and is handed back as it is.
540
+ */
541
+ private _adopt(child: HTMLElement): HTMLElement {
542
+ if (child.localName !== 'gtk-stack-page') return child;
543
+ const content = pageChildOf(child);
544
+ if (content === null) {
545
+ throw new Error(
546
+ `<gtk-stack-page name="${child.getAttribute('name') ?? ''}"> has no child, so the stack would ` +
547
+ 'hold a page with nothing to show.',
548
+ );
549
+ }
550
+ for (const name of STACK_PAGE_ATTRIBUTES) {
551
+ const value = child.getAttribute(name);
552
+ if (value !== null) content.setAttribute(name, value);
553
+ }
554
+ if (child.hasAttribute('hidden')) content.setAttribute('hidden', '');
555
+ content.removeAttribute('slot');
556
+ this.replaceChild(content, child);
557
+ return content;
558
+ }
559
+
534
560
  private _initChild(child: HTMLElement): void {
535
561
  if (this._initializedChildren.has(child)) return;
536
562
  this._initializedChildren.add(child);
537
563
  child.classList.add('adw-stack-page');
538
564
  }
539
565
 
566
+ /**
567
+ * The observer watches the subtree, so an attribute record can come from anything below a
568
+ * page; only a direct child is a page of THIS stack (a nested stack's pages are its own).
569
+ */
570
+ private _isPage(node: Node): boolean {
571
+ return node.parentNode === this;
572
+ }
573
+
540
574
  private _onChildMutations(records: MutationRecord[]): void {
541
575
  let added = 0;
542
576
  let removed = 0;
@@ -544,13 +578,24 @@ export class GtkStack extends HTMLElement {
544
578
  let pageUpdated = false;
545
579
  for (const record of records) {
546
580
  if (record.type === 'childList') {
581
+ // The observer watches the subtree for page attributes; a child list changing
582
+ // below a page (an adopted record handing over its child) is not a page change.
583
+ if (record.target !== this) continue;
547
584
  for (const node of Array.from(record.removedNodes)) {
585
+ // A record leaving as its child takes its place was never a page here.
586
+ if (node instanceof HTMLElement && node.localName === 'gtk-stack-page') continue;
548
587
  if (node === this._visible) this._visible = null;
549
588
  if (node === this._lastVisible) this._lastVisible = null;
550
589
  removed += 1;
551
590
  }
552
591
  for (const node of Array.from(record.addedNodes)) {
553
592
  if (!(node instanceof HTMLElement)) continue;
593
+ // An adopted record is replaced by its child, and that replacement is the
594
+ // childList record the page is counted in.
595
+ if (node.localName === 'gtk-stack-page') {
596
+ if (node.parentElement === this) this._adopt(node);
597
+ continue;
598
+ }
554
599
  this._initChild(node);
555
600
  added += 1;
556
601
  if (position === -1) position = this._children().indexOf(node);
@@ -558,13 +603,14 @@ export class GtkStack extends HTMLElement {
558
603
  continue;
559
604
  }
560
605
  const name = record.attributeName;
561
- if (!name) continue;
606
+ if (!name || !this._isPage(record.target)) continue;
562
607
  if (name === 'hidden') {
563
608
  // `stack_child_visibility_notify_cb` (gtkstack.c:1617-1633) re-runs
564
609
  // `update_child_visible`: hiding the visible page falls back to the first
565
610
  // visible one (which is what the NULL means, :1437-1450), and showing one
566
- // while none is visible picks it up (:3147-3149).
567
- this._show(this._firstVisible());
611
+ // while none is visible picks it up (:3147-3149). Any other change leaves
612
+ // the selection where it is.
613
+ if (!this._visible || !this._page(this._visible).visible) this._show(this._firstVisible());
568
614
  continue;
569
615
  }
570
616
  if (PAGE_ATTRIBUTES.includes(name)) pageUpdated = true;
@@ -579,7 +625,7 @@ export class GtkStack extends HTMLElement {
579
625
  }
580
626
  if (pageUpdated) {
581
627
  for (const record of records) {
582
- if (record.type !== 'attributes') continue;
628
+ if (record.type !== 'attributes' || !this._isPage(record.target)) continue;
583
629
  const target = record.target as HTMLElement;
584
630
  this.dispatchEvent(new CustomEvent('page-updated', { bubbles: true, detail: { child: target } }));
585
631
  // `gtk_stack_page_set_name` re-notifies `visible-child-name` when the
@@ -40,8 +40,9 @@
40
40
  // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
41
41
 
42
42
  import type { DispatchedSignals } from '../signals.js';
43
+ import { WidgetElement } from '../widget-element.js';
43
44
 
44
- export class GtkSwitch extends HTMLElement {
45
+ export class GtkSwitch extends WidgetElement {
45
46
  /** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
46
47
  static readonly signals: DispatchedSignals = {
47
48
  'notify::active': 'notify::active',
@@ -50,6 +50,7 @@
50
50
  import { normalizeLabelJustify, type LabelJustification } from '@gjsify/adwaita-core';
51
51
 
52
52
  import { JUSTIFY_TEXT_ALIGN } from './justification.js';
53
+ import { WidgetElement } from '../widget-element.js';
53
54
 
54
55
  /** `Gtk.WrapMode`'s four nicks, in enum order — Pango's enum, and the DEFAULT is `none`. */
55
56
  const WRAP_MODES = ['none', 'char', 'word', 'word-char'] as const;
@@ -83,7 +84,7 @@ export interface GtkTextBufferSubset {
83
84
  get_text(): string;
84
85
  }
85
86
 
86
- export class GtkTextView extends HTMLElement {
87
+ export class GtkTextView extends WidgetElement {
87
88
  private _area!: HTMLTextAreaElement;
88
89
  private _initialized = false;
89
90
 
@@ -43,8 +43,9 @@
43
43
  // arithmetic composed from @gjsify/adwaita-core.
44
44
 
45
45
  import { ENTRY_ROW_MAX_LENGTH_LIMIT, clampEntryText, entryTextLength } from '@gjsify/adwaita-core';
46
+ import { WidgetElement } from '../widget-element.js';
46
47
 
47
- export class GtkText extends HTMLElement {
48
+ export class GtkText extends WidgetElement {
48
49
  private _input!: HTMLInputElement;
49
50
  private _initialized = false;
50
51
  private _maxLength = 0;
@@ -59,11 +59,13 @@ import {
59
59
  } from '@gjsify/adwaita-core';
60
60
 
61
61
  import { createGtkImage } from './gtk-image.js';
62
+ import { bindSlottedChildren } from '../slotted-children.js';
63
+ import { WidgetElement } from '../widget-element.js';
62
64
 
63
65
  /** `TIMEOUT_EXPAND` — how long a drag hovers before the row opens (gtktreeexpander.c:717). */
64
66
  const TIMEOUT_EXPAND = 500;
65
67
 
66
- export class GtkTreeExpander extends HTMLElement {
68
+ export class GtkTreeExpander extends WidgetElement {
67
69
  private _ornaments: HTMLElement[] = [];
68
70
  private _expandTimer: ReturnType<typeof setTimeout> | null = null;
69
71
  private _initialized = false;
@@ -142,6 +144,8 @@ export class GtkTreeExpander extends HTMLElement {
142
144
  if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
143
145
  this._wire();
144
146
  }
147
+ // The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
148
+ bindSlottedChildren(this, [{ name: 'child', into: this }]);
145
149
  this.setAttribute('role', 'button');
146
150
  this._render();
147
151
  }
@@ -65,6 +65,7 @@
65
65
  // Reference: refs/libadwaita/src/stylesheet/widgets/_misc.scss:74-81 (the 64/32 osd icon)
66
66
  // Copyright (c) The GTK Team. LGPLv2.1+.
67
67
  // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
68
+ import { WidgetElement } from '../widget-element.js';
68
69
 
69
70
  /** The four overlay glyphs, in the order `update_overlay_icon` tests them (gtkvideo.c:626-646). */
70
71
  export type VideoOverlayIcon =
@@ -99,7 +100,7 @@ export const READY_ICON = 'media-playback-start-symbolic';
99
100
  /** `gtk_video_reveal_controls`' timeout: 3 s (gtkvideo.c:130). */
100
101
  export const CONTROLS_HIDE_MS = 3000;
101
102
 
102
- export class GtkVideo extends HTMLElement {
103
+ export class GtkVideo extends WidgetElement {
103
104
  private _initialized = false;
104
105
  private _overlayIcon!: HTMLButtonElement;
105
106
  private _controlsRevealer!: HTMLDivElement;
@@ -65,6 +65,9 @@
65
65
 
66
66
  import { clampAdjustmentValue, normalizeAdjustment, type AdwAdjustment } from '@gjsify/adwaita-core';
67
67
 
68
+ import { bindSlottedChildren } from '../slotted-children.js';
69
+ import { WidgetElement } from '../widget-element.js';
70
+
68
71
  /** `viewport_set_adjustment_values`'s two fractions of the visible size. */
69
72
  const STEP_FRACTION = 0.1;
70
73
  const PAGE_FRACTION = 0.9;
@@ -92,7 +95,7 @@ function axisAdjustment(clip: HTMLElement, axis: Axis): AdwAdjustment {
92
95
  });
93
96
  }
94
97
 
95
- export class GtkViewport extends HTMLElement {
98
+ export class GtkViewport extends WidgetElement {
96
99
  private _observer: ResizeObserver | null = null;
97
100
  private _watch: MutationObserver | null = null;
98
101
 
@@ -130,6 +133,8 @@ export class GtkViewport extends HTMLElement {
130
133
  }
131
134
 
132
135
  connectedCallback() {
136
+ // The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
137
+ bindSlottedChildren(this, [{ name: 'child', into: this }]);
133
138
  this.setAttribute('role', 'generic');
134
139
  // `scroll` does not bubble, so the platform's own is heard here, on the node that
135
140
  // raised it, and re-announced as GTK's `GtkAdjustment::value-changed`.
@@ -66,6 +66,7 @@
66
66
  // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
67
67
 
68
68
  import { createGtkImage } from './gtk-image.js';
69
+ import { WidgetElement } from '../widget-element.js';
69
70
 
70
71
  /** `Gtk.PackType`, spelled as the GIR's enum nicks (gtkwindowcontrols.c:68). */
71
72
  type PackSide = 'start' | 'end';
@@ -120,7 +121,7 @@ const WINDOW_ATTRIBUTES = [
120
121
  /** `closest` over both window spellings — `Adw.Window` is a `GtkWindow` subclass. */
121
122
  const WINDOW_SELECTOR = 'gtk-window, adw-window';
122
123
 
123
- export class GtkWindowControls extends HTMLElement {
124
+ export class GtkWindowControls extends WidgetElement {
124
125
  private _initialized = false;
125
126
  private _empty = true;
126
127
  /** The frame this controls answers to, or `null` outside one (`:257-263`). */
@@ -46,6 +46,9 @@
46
46
  // Copyright (c) The GTK Team. LGPLv2.1+.
47
47
  // Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
48
48
 
49
+ import { bindSlottedChildren } from '../slotted-children.js';
50
+ import { WidgetElement } from '../widget-element.js';
51
+
49
52
  /** `GtkTitlebarGesture`'s three gestures, as `perform_titlebar_action` decides them. */
50
53
  export type GtkTitlebarGesture = 'double-click' | 'middle-click' | 'right-click';
51
54
 
@@ -112,7 +115,7 @@ function gestureOf(button: number, presses: number): GtkTitlebarGesture | null {
112
115
  */
113
116
  const DRAG_THRESHOLD = 1;
114
117
 
115
- export class GtkWindowHandle extends HTMLElement {
118
+ export class GtkWindowHandle extends WidgetElement {
116
119
  /** The gesture in progress, as `priv->drag_start_x` / `_y` are in the C. */
117
120
  private _drag: { pointerId: number; x: number; y: number } | null = null;
118
121
 
@@ -146,6 +149,8 @@ export class GtkWindowHandle extends HTMLElement {
146
149
  }
147
150
 
148
151
  connectedCallback() {
152
+ // The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
153
+ bindSlottedChildren(this, [{ name: 'child', into: this }]);
149
154
  this.setAttribute('role', 'generic');
150
155
  this.addEventListener('click', this._onClick);
151
156
  this.addEventListener('pointerdown', this._onPointerDown);
@@ -47,7 +47,7 @@
47
47
  // difference a reader of the DOM can see.
48
48
  //
49
49
  // NOT PORTED, and why — these are the KNOWN_GAPS of this element:
50
- // `application`, `child`, `default-widget`, `display` — WIDGET- or
50
+ // `child`, `default-widget`, `display` — WIDGET- or
51
51
  // DISPLAY-typed, so a slot or an id here rather than an attribute; `child` and
52
52
  // `titlebar` ARE slots and `transient-for` only has to be PRESENT.
53
53
  // `destroy-with-parent` — a DOM node has no destroy, and a page has no parent to destroy.
@@ -69,8 +69,14 @@
69
69
  // `set_focus(el)` calls `el.focus()` and refuses an element outside the frame;
70
70
  // `set_focus(null)` blurs. `focusin`/`focusout` notify; focus that merely
71
71
  // leaves with the window (blur) keeps its widget, as GTK keeps it.
72
- // `maximized` — the FRAME's state (square corners), not the browser window's: a page cannot
73
- // maximize its browser window. `is_maximized()` reads it.
72
+ // `maximized` — the FRAME's state, not the browser window's: a page cannot maximize its
73
+ // browser window. Square corners, and — as in GTK, where a maximized or
74
+ // fullscreen window ignores its default size and fills the work area — the frame
75
+ // fills its CONTAINING BLOCK (the viewport for a frame in a full-height body)
76
+ // until it is unmaximized, which restores `default-width`/`default-height`.
77
+ // `fullscreened` does the same. `maximize()`, `unmaximize()`, `fullscreen()`,
78
+ // `unfullscreen()` and `toggle-maximized` all write the attribute. `is_maximized()`
79
+ // reads it.
74
80
  // `present()` — un-hides the frame; a page cannot raise or focus a window it does not own.
75
81
  //
76
82
  // THE GOBJECT DOOR (ADR 0096 § 3, ADR 0098 Amendment 3): `connect` / `disconnect` / `emit` /
@@ -92,6 +98,7 @@
92
98
 
93
99
  import { CloseRequestState, WindowMemberState, type WindowMemberProperty } from '@gjsify/adwaita-core';
94
100
 
101
+ import { defineWindowApplication, type GtkApplication } from '../application.js';
95
102
  import { installElementApi } from '../gobject-elements.js';
96
103
  import type { DispatchedSignals } from '../signals.js';
97
104
  import { bindSlottedChildren, type AdwSlot } from '../slotted-children.js';
@@ -99,9 +106,11 @@ import { bindSlottedChildren, type AdwSlot } from '../slotted-children.js';
99
106
  // Registers <gtk-window-controls>: a frame with no titlebar of its own draws the two of them
100
107
  // a server-side decoration would have, so importing the window alone must define them.
101
108
  import './gtk-window-controls.js';
109
+ import { fillsContainingBlock } from './adw-window-features.js';
102
110
  import type { WindowAction } from './gtk-window-controls.js';
111
+ import { WidgetElement } from '../widget-element.js';
103
112
 
104
- export class GtkWindow extends HTMLElement {
113
+ export class GtkWindow extends WidgetElement {
105
114
  private _initialized = false;
106
115
  /** The `titlebar` strip — `protected` so a subclass can place its own strips around it. */
107
116
  protected _titlebar!: HTMLDivElement;
@@ -138,6 +147,7 @@ export class GtkWindow extends HTMLElement {
138
147
  'close-request': 'close-request',
139
148
  ...Object.fromEntries(
140
149
  [
150
+ 'application',
141
151
  'decorated',
142
152
  'default-height',
143
153
  'default-width',
@@ -156,6 +166,11 @@ export class GtkWindow extends HTMLElement {
156
166
  ),
157
167
  };
158
168
 
169
+ /** `Gtk.Window:application`: setting it is `add_window` (ADR 0098), set by `defineWindowApplication`. */
170
+ declare application: GtkApplication | null;
171
+ declare get_application: () => GtkApplication | null;
172
+ declare set_application: (application: GtkApplication | null) => void;
173
+
159
174
  /** `GObject.Object.connect`: installed per instance by `installElementApi`; refuses an undeclared signal. */
160
175
  declare connect: (signal: string, handler: (emitter: this, ...args: any[]) => unknown) => number;
161
176
  /** `GObject.Object.disconnect`: throws for an id that is not connected. */
@@ -239,6 +254,15 @@ export class GtkWindow extends HTMLElement {
239
254
  this._write('maximized', String(value));
240
255
  }
241
256
 
257
+ /** `Gtk.Window:fullscreened` — square corners, and the frame fills its containing block. */
258
+ get fullscreened(): boolean {
259
+ return this._offByDefault('fullscreened');
260
+ }
261
+
262
+ set fullscreened(value: boolean) {
263
+ this._write('fullscreened', String(value));
264
+ }
265
+
242
266
  /** `Gtk.Window:resizable` — whether the maximize button may appear. */
243
267
  get resizable(): boolean {
244
268
  return this._onByDefault('resizable');
@@ -300,7 +324,14 @@ export class GtkWindow extends HTMLElement {
300
324
  this._content.className = 'adw-gtk-window-content';
301
325
 
302
326
  bindSlottedChildren(this, [
303
- ...(this.hasTitlebar() ? [{ name: 'titlebar', into: this._titlebar }] : []),
327
+ // `child` only beside a titlebar: AdwWindow and AdwApplicationWindow inherit
328
+ // `Gtk.Window:child` and abort on it (adw-window.c:180), `content` is theirs.
329
+ ...(this.hasTitlebar()
330
+ ? [
331
+ { name: 'titlebar', into: this._titlebar },
332
+ { name: 'child', into: this._content },
333
+ ]
334
+ : []),
304
335
  ...this.extraSlots(),
305
336
  { into: this._content },
306
337
  ]).install(...this.strips());
@@ -431,16 +462,18 @@ export class GtkWindow extends HTMLElement {
431
462
 
432
463
  /**
433
464
  * The frame's box from `default-width` / `default-height`; an absent one clears it. A subclass
434
- * with a size request of its own (`Adw.Window`'s 360×200) replaces this.
465
+ * with a size request of its own (`Adw.Window`'s 360×200) replaces this. A maximized or
466
+ * fullscreened frame carries no inline size: the skin fills its containing block.
435
467
  */
436
468
  protected renderFrameSize(): void {
469
+ const fills = fillsContainingBlock(this);
437
470
  const width = Number.parseFloat(this.getAttribute('default-width') ?? '');
438
471
  const height = Number.parseFloat(this.getAttribute('default-height') ?? '');
439
472
  for (const [axis, value] of [
440
473
  ['width', width],
441
474
  ['height', height],
442
475
  ] as const) {
443
- if (Number.isFinite(value) && value > 0) this.style.setProperty(axis, `${value}px`);
476
+ if (!fills && Number.isFinite(value) && value > 0) this.style.setProperty(axis, `${value}px`);
444
477
  else this.style.removeProperty(axis);
445
478
  }
446
479
  }
@@ -598,6 +631,23 @@ export class GtkWindow extends HTMLElement {
598
631
  unmaximize(): void {
599
632
  this._write('maximized', 'false');
600
633
  }
634
+
635
+ /** `gtk_window_is_fullscreen`: the FRAME's state, like {@link is_maximized}. */
636
+ is_fullscreen(): boolean {
637
+ return this.fullscreened;
638
+ }
639
+
640
+ /** `gtk_window_fullscreen`; asynchronous in GTK, so the property is written directly. */
641
+ fullscreen(): void {
642
+ this._write('fullscreened', 'true');
643
+ }
644
+
645
+ /** `gtk_window_unfullscreen`, with the same caveat as {@link fullscreen}. */
646
+ unfullscreen(): void {
647
+ this._write('fullscreened', 'false');
648
+ }
601
649
  }
602
650
 
603
651
  customElements.define('gtk-window', GtkWindow);
652
+
653
+ defineWindowApplication(GtkWindow.prototype);
package/src/gio.spec.ts CHANGED
@@ -1,23 +1,33 @@
1
- // The `Gio.SimpleActionGroup` door of adwaita-web, held to the shared vectors (real GJS is their
2
- // oracle, in adwaita-core's `simple-action-group.gjs.spec.ts`). The door is the namespace module itself.
1
+ // The `Gio.SimpleActionGroup` and `Gio.AppInfo` door of adwaita-web, held to the shared vectors (real GJS
2
+ // is their oracle, in adwaita-core's `simple-action-group.gjs.spec.ts` / `gio-app-info.gjs.spec.ts`). The door is the namespace module itself.
3
3
 
4
4
  import {
5
+ GIO_APP_INFO_VECTORS,
5
6
  SIMPLE_ACTION_GROUP_VECTORS,
7
+ driveGioAppInfoVectors,
6
8
  driveSimpleActionGroupVectors,
7
9
  type GioActionsLike,
10
+ type GioAppInfoSubject,
8
11
  } from '@gjsify/adwaita-core/conformance';
9
12
  import { describe, expect, it } from '@gjsify/unit';
10
13
 
14
+ import * as Gdk from './namespace/gdk.js';
11
15
  import * as Gio from './namespace/gio.js';
16
+ import * as GLib from './namespace/glib.js';
12
17
 
13
18
  export const GioActionsTest = async () => {
14
19
  await driveSimpleActionGroupVectors(
15
20
  { name: 'adwaita-web', isOracle: false, Gio: Gio as unknown as GioActionsLike },
16
21
  { describe, it, expect },
17
22
  );
23
+ await driveGioAppInfoVectors(
24
+ { name: 'adwaita-web', isOracle: false, display: true, Gio, GLib, Gdk } as unknown as GioAppInfoSubject,
25
+ { describe, it, expect },
26
+ GIO_APP_INFO_VECTORS,
27
+ );
18
28
  await describe('adwaita-web: Gio door', async () => {
19
29
  await it('has a vector row for every vector', () => {
20
- expect(SIMPLE_ACTION_GROUP_VECTORS.length > 0).toBe(true);
30
+ expect(SIMPLE_ACTION_GROUP_VECTORS.length > 0 && GIO_APP_INFO_VECTORS.length > 0).toBe(true);
21
31
  });
22
32
  });
23
33
  };
package/src/glib.spec.ts CHANGED
@@ -5,10 +5,13 @@
5
5
  import {
6
6
  GLIB_PATH_VECTORS,
7
7
  GLIB_TIMER_VECTORS,
8
+ GLIB_URI_VECTORS,
8
9
  driveGLibPathVectors,
9
10
  driveGLibTimerVectors,
11
+ driveGLibUriVectors,
10
12
  type GLibPathsLike,
11
13
  type GLibTimersLike,
14
+ type GLibUriLike,
12
15
  } from '@gjsify/adwaita-core/conformance';
13
16
  import { describe, expect, it } from '@gjsify/unit';
14
17
 
@@ -36,6 +39,12 @@ export const GLibTest = async () => {
36
39
  await driveGLibPathVectors(
37
40
  { name: 'adwaita-web', isOracle: false, GLib: GLib as unknown as GLibPathsLike },
38
41
  { describe, it, expect },
42
+ GLIB_PATH_VECTORS,
43
+ );
44
+ await driveGLibUriVectors(
45
+ { name: 'adwaita-web', isOracle: false, GLib: GLib as unknown as GLibUriLike },
46
+ { describe, it, expect },
47
+ GLIB_URI_VECTORS,
39
48
  );
40
49
  await describe('adwaita-web: GLib door', async () => {
41
50
  await it('answers the host-bound path questions with the root and no data dirs', () => {
@@ -17,6 +17,7 @@
17
17
  import { attributeOf, propertyOf } from '@gjsify/adwaita-core/tags';
18
18
 
19
19
  import { dispatchedSignalsOf } from './signals.js';
20
+ import { watchMapping } from './widget-mapping.js';
20
21
 
21
22
  type Handler = (emitter: HTMLElement, ...args: unknown[]) => unknown;
22
23
 
@@ -60,7 +61,7 @@ function inheritedDescriptor(el: object, member: string): PropertyDescriptor | u
60
61
  return undefined;
61
62
  }
62
63
 
63
- function notifyElement(el: HTMLElement, name: string): void {
64
+ export function notifyElement(el: HTMLElement, name: string): void {
64
65
  const member = propertyOf(name);
65
66
  el.dispatchEvent(
66
67
  new CustomEvent(eventOf(el, `notify::${name}`), {
@@ -122,7 +123,12 @@ export function listenElement(el: HTMLElement, signal: string, handler: Handler)
122
123
  }
123
124
  };
124
125
  el.addEventListener(event, listener);
125
- return () => el.removeEventListener(event, listener);
126
+ // `map` / `unmap` have no DOM event behind them: the element is watched while one is connected.
127
+ const unwatch = signal === 'map' || signal === 'unmap' ? watchMapping(el) : undefined;
128
+ return () => {
129
+ el.removeEventListener(event, listener);
130
+ unwatch?.();
131
+ };
126
132
  }
127
133
 
128
134
  export function dispatchElement(el: HTMLElement, signal: string, args: readonly unknown[]): void {
@@ -154,6 +160,42 @@ export function installSensitive(el: HTMLElement): void {
154
160
  });
155
161
  }
156
162
 
163
+ const VISIBLE_INSTALLED = new WeakSet<Element>();
164
+
165
+ /** The `visible` getters `WidgetElement` puts on its prototype, so a class's own accessor tells apart. */
166
+ export const WIDGET_VISIBLE_GETTERS = new WeakSet<() => boolean>();
167
+
168
+ /**
169
+ * Whether `el` has the port's `visible` — from `installVisible` or from `WidgetElement`. An
170
+ * element with its own `visible` (`<gtk-stack-page>`) keeps its own and answers false.
171
+ */
172
+ export function hasInstalledVisible(el: Element): boolean {
173
+ if (VISIBLE_INSTALLED.has(el)) return true;
174
+ const getter = inheritedDescriptor(el, 'visible')?.get;
175
+ return getter !== undefined && WIDGET_VISIBLE_GETTERS.has(getter as () => boolean);
176
+ }
177
+
178
+ /**
179
+ * `Gtk.Widget:visible` for an element that has no member of that name: `gtk_widget_get_visible`
180
+ * and `gtk_widget_set_visible` over the DOM's `hidden` attribute, TRUE until hidden as GtkWidget's
181
+ * default is. It notifies only where the element declares `notify::visible`. An element that already declares `visible` (`<gtk-stack-page>`, `<adw-sidebar>`)
182
+ * keeps its own, and so does a registered class whose prototype has the port's members.
183
+ */
184
+ export function installVisible(el: HTMLElement): void {
185
+ if ('visible' in el) return;
186
+ VISIBLE_INSTALLED.add(el);
187
+ const define = (name: string, descriptor: PropertyDescriptor) =>
188
+ Object.defineProperty(el, name, { configurable: true, enumerable: false, ...descriptor });
189
+ const set = (value: unknown): void => {
190
+ const before = !el.hidden;
191
+ el.toggleAttribute('hidden', !value);
192
+ if (!el.hidden !== before && 'notify::visible' in dispatchedSignalsOf(el)) notifyElement(el, 'visible');
193
+ };
194
+ define('visible', { get: () => !el.hidden, set });
195
+ if (!('get_visible' in el)) define('get_visible', { value: () => !el.hidden, writable: true });
196
+ if (!('set_visible' in el)) define('set_visible', { value: set, writable: true });
197
+ }
198
+
157
199
  /** Gives `el` the instance API of a registered class, over the events it declares. */
158
200
  export function installElementApi(el: HTMLElement): void {
159
201
  // A registered class already has the core's four methods on its prototype.
@@ -325,6 +325,45 @@ export const GtkLabelTest = async () => {
325
325
  host.remove();
326
326
  });
327
327
 
328
+ // GtkLabel's wrapping MINIMUM is its longest word, not the width it first wrapped
329
+ // at. A pinned `width` made the measured block the label's min-content, so a card
330
+ // around it in a narrower pane (Learn6502's preferences: 343px) grew past the pane.
331
+ await it('lets a wrapped label shrink below the width it first wrapped at', () => {
332
+ const long = 'word '.repeat(60).trim();
333
+ const { el, host } = mount(long);
334
+ host.style.display = 'flex';
335
+ const card = document.createElement('div');
336
+ host.appendChild(card);
337
+ card.appendChild(el);
338
+ el.wrap = true;
339
+ const first = el.querySelector('.adw-label-text')!.getBoundingClientRect().width;
340
+ expect(first > 300).toBe(true);
341
+ host.style.width = '200px';
342
+ expect(card.getBoundingClientRect().width <= 200).toBe(true);
343
+ expect(el.getBoundingClientRect().width <= 200).toBe(true);
344
+ host.remove();
345
+ });
346
+
347
+ // gtk_label_measure: a wrapping label's minimum is its widest word, its natural
348
+ // width the text on one line (capped only by `max-width-chars`).
349
+ await it('gives a wrapped label its widest word as minimum, one line as natural', () => {
350
+ const long = 'word '.repeat(60).trim();
351
+ const { el, host } = mount(long);
352
+ el.wrap = true;
353
+ const word = mount('word');
354
+ const wordWidth = textRect(word.el).width;
355
+ word.host.remove();
356
+ const line = mount(long);
357
+ line.host.style.width = 'max-content';
358
+ const lineWidth = textRect(line.el).width;
359
+ line.host.remove();
360
+ host.style.width = 'min-content';
361
+ expect(el.getBoundingClientRect().width <= Math.ceil(wordWidth) + 1).toBe(true);
362
+ host.style.width = 'max-content';
363
+ expect(Math.abs(el.getBoundingClientRect().width - lineWidth) <= 1).toBe(true);
364
+ host.remove();
365
+ });
366
+
328
367
  await it('is not selectable unless asked, as GtkLabel', () => {
329
368
  const { el, host } = mount('Hi');
330
369
  expect(getComputedStyle(el).userSelect).toBe('none');
@@ -216,24 +216,24 @@ export const GtkScrolledWindowTest = async () => {
216
216
  await fill(el, 10);
217
217
  // No request is made until one of the two asks for it: GTK only measures the
218
218
  // child when `propagate-natural-*` or a NEVER policy does (gtkscrolledwindow.c:1841-1844).
219
- expect(el.viewport.style.minHeight).toBe('');
219
+ expect(el.viewport.style.height).toBe('');
220
220
  // The NATURAL request is the child's `max-content` size, propagated up.
221
221
  el.setAttribute('propagate-natural-height', '');
222
222
  expect(el.propagateNaturalHeight).toBe(true);
223
- expect(el.viewport.style.minHeight).toBe('max-content');
223
+ expect(el.viewport.style.height).toBe('max-content');
224
224
  // A NEVER policy adds the child's MINIMUM request instead — and the natural one
225
225
  // is the larger of the two, so it wins when both are asked for. Both are FALSE in
226
226
  // the pspec, so their ATTRIBUTE is their presence, as `has-frame` is.
227
227
  el.removeAttribute('propagate-natural-height');
228
228
  el.setAttribute('vscrollbar-policy', 'never');
229
- expect(el.viewport.style.minHeight).toBe('min-content');
229
+ expect(el.viewport.style.height).toBe('min-content');
230
230
  // With both asked for on ONE axis the natural wins, and the other axis keeps
231
231
  // its own answer.
232
232
  el.setAttribute('propagate-natural-width', '');
233
- expect(el.viewport.style.minWidth).toBe('max-content');
234
- expect(el.viewport.style.minHeight).toBe('min-content');
233
+ expect(el.viewport.style.width).toBe('max-content');
234
+ expect(el.viewport.style.height).toBe('min-content');
235
235
  el.setAttribute('propagate-natural-height', '');
236
- expect(el.viewport.style.minHeight).toBe('max-content');
236
+ expect(el.viewport.style.height).toBe('max-content');
237
237
  host.remove();
238
238
  });
239
239
  });