@gtkx/react 0.18.9 → 0.20.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 (212) hide show
  1. package/dist/components/list.d.ts +35 -0
  2. package/dist/components/list.d.ts.map +1 -0
  3. package/dist/components/list.js +40 -0
  4. package/dist/components/list.js.map +1 -0
  5. package/dist/generated/internal.d.ts +8 -3
  6. package/dist/generated/internal.d.ts.map +1 -1
  7. package/dist/generated/internal.js +3553 -53
  8. package/dist/generated/internal.js.map +1 -1
  9. package/dist/generated/jsx.d.ts +178 -326
  10. package/dist/generated/jsx.d.ts.map +1 -1
  11. package/dist/generated/jsx.js +0 -324
  12. package/dist/generated/jsx.js.map +1 -1
  13. package/dist/host-config.d.ts.map +1 -1
  14. package/dist/host-config.js +48 -10
  15. package/dist/host-config.js.map +1 -1
  16. package/dist/jsx.d.ts +111 -54
  17. package/dist/jsx.d.ts.map +1 -1
  18. package/dist/jsx.js +3 -28
  19. package/dist/jsx.js.map +1 -1
  20. package/dist/metadata.d.ts +1 -0
  21. package/dist/metadata.d.ts.map +1 -1
  22. package/dist/metadata.js +7 -1
  23. package/dist/metadata.js.map +1 -1
  24. package/dist/node.d.ts +0 -2
  25. package/dist/node.d.ts.map +1 -1
  26. package/dist/node.js +19 -25
  27. package/dist/node.js.map +1 -1
  28. package/dist/nodes/application.d.ts.map +1 -1
  29. package/dist/nodes/application.js +4 -0
  30. package/dist/nodes/application.js.map +1 -1
  31. package/dist/nodes/column-view-column.d.ts +19 -16
  32. package/dist/nodes/column-view-column.d.ts.map +1 -1
  33. package/dist/nodes/column-view-column.js +129 -97
  34. package/dist/nodes/column-view-column.js.map +1 -1
  35. package/dist/nodes/event-controller.d.ts +1 -0
  36. package/dist/nodes/event-controller.d.ts.map +1 -1
  37. package/dist/nodes/event-controller.js +9 -7
  38. package/dist/nodes/event-controller.js.map +1 -1
  39. package/dist/nodes/fixed-child.d.ts +1 -2
  40. package/dist/nodes/fixed-child.d.ts.map +1 -1
  41. package/dist/nodes/fixed-child.js +21 -21
  42. package/dist/nodes/fixed-child.js.map +1 -1
  43. package/dist/nodes/font-dialog-button.d.ts +1 -1
  44. package/dist/nodes/font-dialog-button.d.ts.map +1 -1
  45. package/dist/nodes/font-dialog-button.js +8 -0
  46. package/dist/nodes/font-dialog-button.js.map +1 -1
  47. package/dist/nodes/internal/accessible.d.ts +5 -0
  48. package/dist/nodes/internal/accessible.d.ts.map +1 -0
  49. package/dist/nodes/internal/accessible.js +119 -0
  50. package/dist/nodes/internal/accessible.js.map +1 -0
  51. package/dist/nodes/internal/bound-item.d.ts +4 -0
  52. package/dist/nodes/internal/bound-item.d.ts.map +1 -0
  53. package/dist/nodes/internal/bound-item.js +2 -0
  54. package/dist/nodes/internal/bound-item.js.map +1 -0
  55. package/dist/nodes/internal/construct.d.ts +3 -0
  56. package/dist/nodes/internal/construct.d.ts.map +1 -0
  57. package/dist/nodes/internal/construct.js +44 -0
  58. package/dist/nodes/internal/construct.js.map +1 -0
  59. package/dist/nodes/internal/text-buffer-controller.d.ts +4 -0
  60. package/dist/nodes/internal/text-buffer-controller.d.ts.map +1 -1
  61. package/dist/nodes/internal/text-buffer-controller.js +49 -9
  62. package/dist/nodes/internal/text-buffer-controller.js.map +1 -1
  63. package/dist/nodes/internal/widget.d.ts.map +1 -1
  64. package/dist/nodes/internal/widget.js +4 -1
  65. package/dist/nodes/internal/widget.js.map +1 -1
  66. package/dist/nodes/list-item-node.d.ts +12 -0
  67. package/dist/nodes/list-item-node.d.ts.map +1 -0
  68. package/dist/nodes/list-item-node.js +23 -0
  69. package/dist/nodes/list-item-node.js.map +1 -0
  70. package/dist/nodes/list.d.ts +100 -0
  71. package/dist/nodes/list.d.ts.map +1 -0
  72. package/dist/nodes/list.js +950 -0
  73. package/dist/nodes/list.js.map +1 -0
  74. package/dist/nodes/notebook-page.d.ts.map +1 -1
  75. package/dist/nodes/notebook-page.js +4 -0
  76. package/dist/nodes/notebook-page.js.map +1 -1
  77. package/dist/nodes/shortcut.d.ts +3 -2
  78. package/dist/nodes/shortcut.d.ts.map +1 -1
  79. package/dist/nodes/shortcut.js +19 -4
  80. package/dist/nodes/shortcut.js.map +1 -1
  81. package/dist/nodes/text-anchor.d.ts.map +1 -1
  82. package/dist/nodes/text-anchor.js +7 -1
  83. package/dist/nodes/text-anchor.js.map +1 -1
  84. package/dist/nodes/text-tag.d.ts.map +1 -1
  85. package/dist/nodes/text-tag.js +5 -1
  86. package/dist/nodes/text-tag.js.map +1 -1
  87. package/dist/nodes/text-view.d.ts +1 -0
  88. package/dist/nodes/text-view.d.ts.map +1 -1
  89. package/dist/nodes/text-view.js +4 -0
  90. package/dist/nodes/text-view.js.map +1 -1
  91. package/dist/nodes/widget.d.ts +0 -2
  92. package/dist/nodes/widget.d.ts.map +1 -1
  93. package/dist/nodes/widget.js +51 -67
  94. package/dist/nodes/widget.js.map +1 -1
  95. package/dist/nodes/window.d.ts.map +1 -1
  96. package/dist/nodes/window.js +2 -2
  97. package/dist/nodes/window.js.map +1 -1
  98. package/dist/registry.d.ts +0 -2
  99. package/dist/registry.d.ts.map +1 -1
  100. package/dist/registry.js +4 -13
  101. package/dist/registry.js.map +1 -1
  102. package/dist/types.d.ts +2 -2
  103. package/dist/types.d.ts.map +1 -1
  104. package/package.json +5 -4
  105. package/src/components/list.tsx +83 -0
  106. package/src/generated/internal.ts +3559 -49
  107. package/src/generated/jsx.ts +178 -326
  108. package/src/host-config.ts +43 -10
  109. package/src/jsx.ts +121 -62
  110. package/src/metadata.ts +8 -1
  111. package/src/node.ts +23 -25
  112. package/src/nodes/application.ts +5 -0
  113. package/src/nodes/column-view-column.ts +125 -104
  114. package/src/nodes/event-controller.ts +8 -8
  115. package/src/nodes/fixed-child.ts +24 -23
  116. package/src/nodes/font-dialog-button.ts +10 -0
  117. package/src/nodes/internal/accessible.ts +155 -0
  118. package/src/nodes/internal/bound-item.ts +4 -0
  119. package/src/nodes/internal/construct.ts +60 -0
  120. package/src/nodes/internal/text-buffer-controller.ts +51 -8
  121. package/src/nodes/internal/widget.ts +3 -1
  122. package/src/nodes/list-item-node.ts +29 -0
  123. package/src/nodes/list.ts +1082 -0
  124. package/src/nodes/notebook-page.ts +4 -0
  125. package/src/nodes/shortcut.ts +22 -5
  126. package/src/nodes/text-anchor.ts +6 -1
  127. package/src/nodes/text-tag.ts +7 -1
  128. package/src/nodes/text-view.ts +5 -0
  129. package/src/nodes/widget.ts +47 -69
  130. package/src/nodes/window.ts +2 -2
  131. package/src/registry.ts +11 -17
  132. package/src/types.ts +7 -2
  133. package/dist/fiber-root.d.ts +0 -4
  134. package/dist/fiber-root.d.ts.map +0 -1
  135. package/dist/fiber-root.js +0 -6
  136. package/dist/fiber-root.js.map +0 -1
  137. package/dist/nodes/column-view.d.ts +0 -36
  138. package/dist/nodes/column-view.d.ts.map +0 -1
  139. package/dist/nodes/column-view.js +0 -175
  140. package/dist/nodes/column-view.js.map +0 -1
  141. package/dist/nodes/drop-down.d.ts +0 -27
  142. package/dist/nodes/drop-down.d.ts.map +0 -1
  143. package/dist/nodes/drop-down.js +0 -85
  144. package/dist/nodes/drop-down.js.map +0 -1
  145. package/dist/nodes/grid-view.d.ts +0 -29
  146. package/dist/nodes/grid-view.d.ts.map +0 -1
  147. package/dist/nodes/grid-view.js +0 -85
  148. package/dist/nodes/grid-view.js.map +0 -1
  149. package/dist/nodes/internal/base-item-renderer.d.ts +0 -28
  150. package/dist/nodes/internal/base-item-renderer.d.ts.map +0 -1
  151. package/dist/nodes/internal/base-item-renderer.js +0 -86
  152. package/dist/nodes/internal/base-item-renderer.js.map +0 -1
  153. package/dist/nodes/internal/grid-item-renderer.d.ts +0 -20
  154. package/dist/nodes/internal/grid-item-renderer.d.ts.map +0 -1
  155. package/dist/nodes/internal/grid-item-renderer.js +0 -66
  156. package/dist/nodes/internal/grid-item-renderer.js.map +0 -1
  157. package/dist/nodes/internal/list-item-renderer.d.ts +0 -27
  158. package/dist/nodes/internal/list-item-renderer.d.ts.map +0 -1
  159. package/dist/nodes/internal/list-item-renderer.js +0 -131
  160. package/dist/nodes/internal/list-item-renderer.js.map +0 -1
  161. package/dist/nodes/internal/list-store.d.ts +0 -22
  162. package/dist/nodes/internal/list-store.d.ts.map +0 -1
  163. package/dist/nodes/internal/list-store.js +0 -91
  164. package/dist/nodes/internal/list-store.js.map +0 -1
  165. package/dist/nodes/internal/selection-model-controller.d.ts +0 -26
  166. package/dist/nodes/internal/selection-model-controller.d.ts.map +0 -1
  167. package/dist/nodes/internal/selection-model-controller.js +0 -79
  168. package/dist/nodes/internal/selection-model-controller.js.map +0 -1
  169. package/dist/nodes/internal/simple-list-store.d.ts +0 -20
  170. package/dist/nodes/internal/simple-list-store.d.ts.map +0 -1
  171. package/dist/nodes/internal/simple-list-store.js +0 -87
  172. package/dist/nodes/internal/simple-list-store.js.map +0 -1
  173. package/dist/nodes/internal/tree-store.d.ts +0 -34
  174. package/dist/nodes/internal/tree-store.d.ts.map +0 -1
  175. package/dist/nodes/internal/tree-store.js +0 -208
  176. package/dist/nodes/internal/tree-store.js.map +0 -1
  177. package/dist/nodes/list-item.d.ts +0 -24
  178. package/dist/nodes/list-item.d.ts.map +0 -1
  179. package/dist/nodes/list-item.js +0 -83
  180. package/dist/nodes/list-item.js.map +0 -1
  181. package/dist/nodes/list-view.d.ts +0 -29
  182. package/dist/nodes/list-view.d.ts.map +0 -1
  183. package/dist/nodes/list-view.js +0 -83
  184. package/dist/nodes/list-view.js.map +0 -1
  185. package/dist/nodes/models/grid.d.ts +0 -28
  186. package/dist/nodes/models/grid.d.ts.map +0 -1
  187. package/dist/nodes/models/grid.js +0 -69
  188. package/dist/nodes/models/grid.js.map +0 -1
  189. package/dist/nodes/models/list.d.ts +0 -31
  190. package/dist/nodes/models/list.d.ts.map +0 -1
  191. package/dist/nodes/models/list.js +0 -93
  192. package/dist/nodes/models/list.js.map +0 -1
  193. package/dist/nodes/shortcut-controller.d.ts +0 -10
  194. package/dist/nodes/shortcut-controller.d.ts.map +0 -1
  195. package/dist/nodes/shortcut-controller.js +0 -23
  196. package/dist/nodes/shortcut-controller.js.map +0 -1
  197. package/src/fiber-root.ts +0 -20
  198. package/src/nodes/column-view.ts +0 -217
  199. package/src/nodes/drop-down.ts +0 -108
  200. package/src/nodes/grid-view.ts +0 -109
  201. package/src/nodes/internal/base-item-renderer.ts +0 -108
  202. package/src/nodes/internal/grid-item-renderer.ts +0 -78
  203. package/src/nodes/internal/list-item-renderer.ts +0 -162
  204. package/src/nodes/internal/list-store.ts +0 -105
  205. package/src/nodes/internal/selection-model-controller.ts +0 -115
  206. package/src/nodes/internal/simple-list-store.ts +0 -99
  207. package/src/nodes/internal/tree-store.ts +0 -237
  208. package/src/nodes/list-item.ts +0 -107
  209. package/src/nodes/list-view.ts +0 -113
  210. package/src/nodes/models/grid.ts +0 -105
  211. package/src/nodes/models/list.ts +0 -140
  212. package/src/nodes/shortcut-controller.ts +0 -27
@@ -1,3 +1,4 @@
1
+ import { freeze, unfreeze } from "@gtkx/ffi";
1
2
  import type * as Gtk from "@gtkx/ffi/gtk";
2
3
  import React from "react";
3
4
  import type ReactReconciler from "react-reconciler";
@@ -72,6 +73,8 @@ export function createHostConfig(): HostConfig {
72
73
  supportsMutation: true,
73
74
  supportsPersistence: false,
74
75
  supportsHydration: false,
76
+ supportsMicrotasks: true,
77
+ scheduleMicrotask: (fn: () => unknown) => queueMicrotask(fn),
75
78
  isPrimaryRenderer: true,
76
79
  noTimeout: -1,
77
80
  getRootHostContext: () => ({}),
@@ -93,22 +96,42 @@ export function createHostConfig(): HostConfig {
93
96
  if (hostContext.insideTextBuffer) {
94
97
  const props = { text };
95
98
  const node = createNode("TextSegment", props, undefined, rootContainer);
96
- node.commitUpdate(null, props);
99
+ node.signalStore.blockAll();
100
+ try {
101
+ node.commitUpdate(null, props);
102
+ } finally {
103
+ node.signalStore.unblockAll();
104
+ }
97
105
  return node;
98
106
  }
99
107
  const props = { label: text };
100
108
  const node = createNode("GtkLabel", props, undefined, rootContainer);
101
- node.commitUpdate(null, props);
109
+ node.signalStore.blockAll();
110
+ try {
111
+ node.commitUpdate(null, props);
112
+ } finally {
113
+ node.signalStore.unblockAll();
114
+ }
102
115
  return node;
103
116
  },
104
117
  appendInitialChild: (parent, child) => {
105
118
  parent.appendInitialChild(child);
106
119
  },
107
120
  finalizeInitialChildren: (instance, _type, props) => {
108
- return instance.finalizeInitialChildren(props);
121
+ instance.signalStore.blockAll();
122
+ try {
123
+ return instance.finalizeInitialChildren(props);
124
+ } finally {
125
+ instance.signalStore.unblockAll();
126
+ }
109
127
  },
110
128
  commitUpdate: (instance, _type, oldProps, newProps) => {
111
- instance.commitUpdate(oldProps, newProps);
129
+ instance.signalStore.blockAll();
130
+ try {
131
+ instance.commitUpdate(oldProps, newProps);
132
+ } finally {
133
+ instance.signalStore.unblockAll();
134
+ }
112
135
  },
113
136
  commitMount: (instance) => {
114
137
  instance.commitMount();
@@ -135,14 +158,22 @@ export function createHostConfig(): HostConfig {
135
158
  parent.insertBefore(child, beforeChild);
136
159
  },
137
160
  prepareForCommit: () => {
161
+ freeze();
138
162
  return null;
139
163
  },
140
- resetAfterCommit: () => {},
164
+ resetAfterCommit: () => {
165
+ unfreeze();
166
+ },
141
167
  commitTextUpdate: (textInstance, oldText, newText) => {
142
- if (textInstance.typeName === "TextSegment") {
143
- textInstance.commitUpdate({ text: oldText }, { text: newText });
144
- } else {
145
- textInstance.commitUpdate({ label: oldText }, { label: newText });
168
+ textInstance.signalStore.blockAll();
169
+ try {
170
+ if (textInstance.typeName === "TextSegment") {
171
+ textInstance.commitUpdate({ text: oldText }, { text: newText });
172
+ } else {
173
+ textInstance.commitUpdate({ label: oldText }, { label: newText });
174
+ }
175
+ } finally {
176
+ textInstance.signalStore.unblockAll();
146
177
  }
147
178
  },
148
179
  clearContainer: () => {},
@@ -165,7 +196,9 @@ export function createHostConfig(): HostConfig {
165
196
  afterActiveInstanceBlur: () => {},
166
197
  prepareScopeUpdate: () => {},
167
198
  getInstanceFromScope: () => null,
168
- detachDeletedInstance: (instance) => instance.detachDeletedInstance(),
199
+ detachDeletedInstance: (instance) => {
200
+ instance.detachDeletedInstance();
201
+ },
169
202
  resetFormInstance: () => {},
170
203
  requestPostPaintCallback: () => {},
171
204
  shouldAttemptEagerTransition: () => false,
package/src/jsx.ts CHANGED
@@ -132,15 +132,15 @@ export type AnimationProps = {
132
132
  /**
133
133
  * Props for the Shortcut virtual element.
134
134
  *
135
- * Defines a keyboard shortcut. Must be a child of `x.ShortcutController`.
135
+ * Defines a keyboard shortcut. Must be a child of `<GtkShortcutController>`.
136
136
  *
137
137
  * @example
138
138
  * ```tsx
139
- * <x.ShortcutController>
139
+ * <GtkShortcutController>
140
140
  * <x.Shortcut trigger="<Control>s" onActivate={save} />
141
141
  * <x.Shortcut trigger={["F5", "<Control>r"]} onActivate={refresh} />
142
142
  * <x.Shortcut trigger="Escape" onActivate={cancel} disabled={!canCancel} />
143
- * </x.ShortcutController>
143
+ * </GtkShortcutController>
144
144
  * ```
145
145
  */
146
146
  export type ShortcutProps = {
@@ -174,6 +174,8 @@ export type ShortcutProps = {
174
174
  * ```
175
175
  */
176
176
  export type TextAnchorProps = {
177
+ /** Replacement character displayed when the widget is not visible (e.g. in serialized text) */
178
+ replacementChar?: string;
177
179
  /** The widget to embed at this anchor position */
178
180
  children?: ReactNode;
179
181
  };
@@ -359,8 +361,6 @@ export type ContainerSlotNames = {
359
361
 
360
362
  /**
361
363
  * Props for method-based container slot child positioning.
362
- *
363
- * @see {@link x.ContainerSlot} for type-safe usage
364
364
  */
365
365
  export type ContainerSlotProps = {
366
366
  /** The method name to call on the parent widget */
@@ -370,28 +370,36 @@ export type ContainerSlotProps = {
370
370
  };
371
371
 
372
372
  /**
373
- * Props for items in a GtkListView, GtkGridView, or GtkColumnView.
373
+ * A data item for list/grid/column views and dropdowns.
374
+ *
375
+ * Uses a discriminated union on the `section` field:
376
+ * - Regular items have `value: T` and optional tree-mode properties
377
+ * - Section headers have `value: S`, `section: true`, and required `children`
374
378
  *
375
- * When used inside a GtkListView, items can be nested to create tree hierarchies.
376
- * Tree-specific props (`indentForDepth`, `indentForIcon`, `hideExpander`) only
377
- * apply when items are used inside a GtkListView with nested children.
379
+ * Mode is detected from data shape:
380
+ * - Any item has `section: true` → section mode
381
+ * - Any item has non-empty `children` (without `section`) tree mode
382
+ * - Otherwise → flat mode
378
383
  *
379
- * @typeParam T - The type of data associated with this list item
384
+ * @typeParam T - The type of data for regular items
385
+ * @typeParam S - The type of data for section headers
380
386
  */
381
- export type ListItemProps<T = unknown> = {
382
- /** Unique identifier for this item */
383
- id: string;
384
- /** The data value for this item */
385
- value: T;
386
- /** Whether to indent based on tree depth (default: true) */
387
- indentForDepth?: boolean;
388
- /** Whether to indent for expander icon width */
389
- indentForIcon?: boolean;
390
- /** Whether to hide the expand/collapse arrow */
391
- hideExpander?: boolean;
392
- /** Nested list items (children of this item in a tree) */
393
- children?: ReactNode;
394
- };
387
+ export type ListItem<T = unknown, S = unknown> =
388
+ | {
389
+ id: string;
390
+ value: T;
391
+ section?: false | undefined;
392
+ children?: ListItem<T, S>[];
393
+ hideExpander?: boolean;
394
+ indentForDepth?: boolean;
395
+ indentForIcon?: boolean;
396
+ }
397
+ | {
398
+ id: string;
399
+ value: S;
400
+ section: true;
401
+ children: ListItem<T, S>[];
402
+ };
395
403
 
396
404
  /**
397
405
  * Props for positioning children within a GtkGrid.
@@ -447,8 +455,10 @@ export type ColumnViewColumnProps<T = unknown> = {
447
455
  id: string;
448
456
  /** Whether clicking the header sorts by this column */
449
457
  sortable?: boolean;
458
+ /** Whether this column is visible */
459
+ visible?: boolean;
450
460
  /** Function to render the cell content for each row */
451
- renderCell: (item: T | null) => ReactNode;
461
+ renderCell: (item: T) => ReactNode;
452
462
  /** Menu items for the column header context menu */
453
463
  children?: ReactNode;
454
464
  };
@@ -662,6 +672,8 @@ export type TextBufferProps = {
662
672
  type BaseListViewProps = {
663
673
  /** Estimated item height in pixels for virtualization */
664
674
  estimatedItemHeight?: number;
675
+ /** Estimated item width in pixels for virtualization */
676
+ estimatedItemWidth?: number;
665
677
  /** Array of selected item IDs */
666
678
  selected?: string[] | null;
667
679
  /** Callback fired when the selection changes */
@@ -670,28 +682,40 @@ type BaseListViewProps = {
670
682
  selectionMode?: Gtk.SelectionMode | null;
671
683
  };
672
684
 
673
- export type ListViewProps = BaseListViewProps & {
685
+ export type ListViewProps<T = unknown, S = unknown> = BaseListViewProps & {
686
+ /** Data items to display in the list */
687
+ items?: ListItem<T, S>[];
674
688
  /** Function to render each list item. The `row` parameter provides tree state for hierarchical lists. */
675
- // biome-ignore lint/suspicious/noExplicitAny: contravariant parameter requires any for typed callbacks
676
- renderItem: (item: any, row?: Gtk.TreeListRow | null) => ReactNode;
689
+ renderItem: (item: T, row?: Gtk.TreeListRow | null) => ReactNode;
677
690
  /** Whether to automatically expand new tree rows (default: false) */
678
691
  autoexpand?: boolean;
692
+ /** Function to render section headers when items contain section entries */
693
+ renderHeader?: ((item: S) => ReactNode) | null;
679
694
  };
680
695
 
681
- export type GridViewProps = BaseListViewProps & {
696
+ export type GridViewProps<T = unknown> = BaseListViewProps & {
697
+ /** Data items to display in the grid */
698
+ items?: ListItem<T>[];
682
699
  /** Function to render each grid item */
683
- // biome-ignore lint/suspicious/noExplicitAny: contravariant parameter requires any for typed callbacks
684
- renderItem: (item: any) => ReactNode;
700
+ renderItem: (item: T) => ReactNode;
685
701
  };
686
702
 
687
703
  /**
688
704
  * Props shared by single-selection dropdown widgets (GtkDropDown, AdwComboRow).
689
705
  */
690
- export type DropDownProps = {
706
+ export type DropDownProps<T = unknown, S = unknown> = {
707
+ /** Data items to display in the dropdown */
708
+ items?: ListItem<T, S>[];
691
709
  /** ID of the currently selected item */
692
710
  selectedId?: string | null;
693
711
  /** Callback fired when the selected item changes */
694
712
  onSelectionChanged?: ((id: string) => void) | null;
713
+ /** Function to render each item. Sets the primary factory, used for both button and popup list unless overridden by renderListItem. */
714
+ renderItem?: ((item: T) => ReactNode) | null;
715
+ /** Function to render items in the popup list only, overriding renderItem for the list. */
716
+ renderListItem?: ((item: T) => ReactNode) | null;
717
+ /** Function to render section headers when items contain section entries */
718
+ renderHeader?: ((item: S) => ReactNode) | null;
695
719
  };
696
720
 
697
721
  /**
@@ -735,18 +759,13 @@ export type AdjustableProps = {
735
759
  *
736
760
  * @example
737
761
  * ```tsx
738
- * import { x, GtkHeaderBar, GtkDropDown } from "@gtkx/react";
762
+ * import { x, GtkHeaderBar } from "@gtkx/react";
739
763
  *
740
764
  * <GtkHeaderBar>
741
765
  * <x.Slot for={GtkHeaderBar} id="titleWidget">
742
766
  * <GtkLabel label="App Title" />
743
767
  * </x.Slot>
744
768
  * </GtkHeaderBar>
745
- *
746
- * <GtkDropDown>
747
- * <x.ListItem id="opt1" value="Option 1" />
748
- * <x.ListItem id="opt2" value="Option 2" />
749
- * </GtkDropDown>
750
769
  * ```
751
770
  */
752
771
  export const x = {
@@ -834,28 +853,6 @@ export const x = {
834
853
  */
835
854
  NotebookPageTab: "NotebookPageTab" as const,
836
855
 
837
- /**
838
- * Element type for items in a GtkListView, GtkGridView, or GtkColumnView.
839
- *
840
- * Items can be nested to create tree hierarchies inside a GtkListView.
841
- *
842
- * @example
843
- * ```tsx
844
- * // Flat list
845
- * <GtkListView renderItem={(item) => <GtkLabel label={item.name} />}>
846
- * <x.ListItem id="1" value={{ name: "Item 1" }} />
847
- * </GtkListView>
848
- *
849
- * // Tree list (nested items)
850
- * <GtkListView renderItem={(item, row) => <GtkLabel label={item.name} />} autoexpand>
851
- * <x.ListItem id="parent" value={{ name: "Parent" }}>
852
- * <x.ListItem id="child" value={{ name: "Child" }} />
853
- * </x.ListItem>
854
- * </GtkListView>
855
- * ```
856
- */
857
- ListItem: "ListItem" as const,
858
-
859
856
  /**
860
857
  * Component for defining columns in a ColumnView (table widget).
861
858
  *
@@ -1078,7 +1075,7 @@ export const x = {
1078
1075
  /**
1079
1076
  * A keyboard shortcut definition.
1080
1077
  *
1081
- * Must be a child of `x.ShortcutController`.
1078
+ * Must be a child of `<GtkShortcutController>`.
1082
1079
  *
1083
1080
  * @example
1084
1081
  * ```tsx
@@ -1124,7 +1121,6 @@ declare global {
1124
1121
  ColumnViewColumn: ColumnViewColumnProps;
1125
1122
  FixedChild: FixedChildProps;
1126
1123
  GridChild: GridChildProps;
1127
- ListItem: ListItemProps;
1128
1124
  MenuItem: MenuItemProps;
1129
1125
  MenuSection: MenuSectionProps;
1130
1126
  MenuSubmenu: MenuSubmenuProps;
@@ -1144,7 +1140,61 @@ declare global {
1144
1140
  }
1145
1141
  }
1146
1142
 
1143
+ export type AccessibleProps = {
1144
+ accessibleAutocomplete?: Gtk.AccessibleAutocomplete;
1145
+ accessibleDescription?: string;
1146
+ accessibleHasPopup?: boolean;
1147
+ accessibleKeyShortcuts?: string;
1148
+ accessibleLabel?: string;
1149
+ accessibleLevel?: number;
1150
+ accessibleModal?: boolean;
1151
+ accessibleMultiLine?: boolean;
1152
+ accessibleMultiSelectable?: boolean;
1153
+ accessibleOrientation?: Gtk.Orientation;
1154
+ accessiblePlaceholder?: string;
1155
+ accessibleReadOnly?: boolean;
1156
+ accessibleRequired?: boolean;
1157
+ accessibleRoleDescription?: string;
1158
+ accessibleSort?: Gtk.AccessibleSort;
1159
+ accessibleValueMax?: number;
1160
+ accessibleValueMin?: number;
1161
+ accessibleValueNow?: number;
1162
+ accessibleValueText?: string;
1163
+ accessibleHelpText?: string;
1164
+
1165
+ accessibleBusy?: boolean;
1166
+ accessibleChecked?: Gtk.AccessibleTristate;
1167
+ accessibleDisabled?: boolean;
1168
+ accessibleExpanded?: boolean;
1169
+ accessibleHidden?: boolean;
1170
+ accessibleInvalid?: Gtk.AccessibleInvalidState;
1171
+ accessiblePressed?: Gtk.AccessibleTristate;
1172
+ accessibleSelected?: boolean;
1173
+ accessibleVisited?: boolean;
1174
+
1175
+ accessibleActiveDescendant?: Gtk.Widget;
1176
+ accessibleColCount?: number;
1177
+ accessibleColIndex?: number;
1178
+ accessibleColIndexText?: string;
1179
+ accessibleColSpan?: number;
1180
+ accessibleControls?: Gtk.Widget[];
1181
+ accessibleDescribedBy?: Gtk.Widget[];
1182
+ accessibleDetails?: Gtk.Widget[];
1183
+ accessibleErrorMessage?: Gtk.Widget[];
1184
+ accessibleFlowTo?: Gtk.Widget[];
1185
+ accessibleLabelledBy?: Gtk.Widget[];
1186
+ accessibleOwns?: Gtk.Widget[];
1187
+ accessiblePosInSet?: number;
1188
+ accessibleRowCount?: number;
1189
+ accessibleRowIndex?: number;
1190
+ accessibleRowIndexText?: string;
1191
+ accessibleRowSpan?: number;
1192
+ accessibleSetSize?: number;
1193
+ };
1194
+
1147
1195
  declare module "./generated/jsx.js" {
1196
+ interface WidgetProps extends AccessibleProps {}
1197
+
1148
1198
  interface GtkRangeProps extends Omit<AdjustableProps, "onValueChanged"> {
1149
1199
  /** Callback fired when the range value changes */
1150
1200
  onValueChanged?: ((value: number, self: Gtk.Range) => void) | null;
@@ -1204,6 +1254,8 @@ declare module "./generated/jsx.js" {
1204
1254
  interface GtkGridViewProps extends GridViewProps {}
1205
1255
 
1206
1256
  interface GtkColumnViewProps {
1257
+ /** Data items to display in the column view */
1258
+ items?: ListItem[];
1207
1259
  /** Array of selected row IDs */
1208
1260
  selected?: string[] | null;
1209
1261
  /** Callback fired when the selection changes */
@@ -1218,6 +1270,8 @@ declare module "./generated/jsx.js" {
1218
1270
  onSortChanged?: ((column: string | null, order: Gtk.SortType) => void) | null;
1219
1271
  /** Estimated row height in pixels for virtualization */
1220
1272
  estimatedRowHeight?: number | null;
1273
+ /** Function to render section headers when items contain section entries */
1274
+ renderHeader?: ((item: unknown) => ReactNode) | null;
1221
1275
  }
1222
1276
 
1223
1277
  interface GtkDropDownProps extends DropDownProps {}
@@ -1261,6 +1315,10 @@ declare module "./generated/jsx.js" {
1261
1315
  interface GtkFontDialogButtonProps extends DialogButtonProps {
1262
1316
  /** Callback fired when the selected font changes */
1263
1317
  onFontDescChanged?: ((fontDesc: Pango.FontDescription) => void) | null;
1318
+ /** Filter to restrict which fonts are shown in the dialog */
1319
+ filter?: Gtk.Filter | null;
1320
+ /** Custom font map to select fonts from */
1321
+ fontMap?: Pango.FontMap | null;
1264
1322
  }
1265
1323
 
1266
1324
  interface GtkAboutDialogProps {
@@ -1293,4 +1351,5 @@ declare module "./generated/jsx.js" {
1293
1351
  }
1294
1352
  }
1295
1353
 
1354
+ export { AdwComboRow, GtkColumnView, GtkDropDown, GtkGridView, GtkListView } from "./components/list.js";
1296
1355
  export * from "./generated/jsx.js";
package/src/metadata.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { NativeClass } from "@gtkx/ffi";
2
- import { PROPS, SIGNALS } from "./generated/internal.js";
2
+ import { CONSTRUCTION_META, PROPS, SIGNALS } from "./generated/internal.js";
3
3
  import type { Container } from "./types.js";
4
4
 
5
5
  const walkPrototypeChain = <T>(instance: Container, lookup: (typeName: string) => T | null): T | null => {
@@ -30,6 +30,13 @@ const walkPrototypeChain = <T>(instance: Container, lookup: (typeName: string) =
30
30
  export const resolvePropMeta = (instance: Container, key: string): [string | null, string] | null =>
31
31
  walkPrototypeChain(instance, (typeName) => PROPS[typeName]?.[key] ?? null);
32
32
 
33
+ export const isConstructOnlyProp = (instance: Container, key: string): boolean =>
34
+ walkPrototypeChain(instance, (typeName) => {
35
+ const meta = CONSTRUCTION_META[typeName];
36
+ if (!meta || !(key in meta)) return null;
37
+ return meta[key]?.constructOnly === true;
38
+ }) ?? false;
39
+
33
40
  export const resolveSignal = (instance: Container, propName: string): string | null => {
34
41
  if (propName === "onNotify") return "notify";
35
42
  return walkPrototypeChain(instance, (typeName) => SIGNALS[typeName]?.[propName] ?? null);
package/src/node.ts CHANGED
@@ -14,7 +14,6 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
14
14
  rootContainer: Container;
15
15
  parent: TParent | null = null;
16
16
  children: TChild[] = [];
17
- private childIndices = new Map<TChild, number>();
18
17
 
19
18
  constructor(typeName: string, props: TProps, container: TContainer, rootContainer: Container) {
20
19
  this.typeName = typeName;
@@ -36,6 +35,7 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
36
35
  if (parent !== null && !this.isValidParent(parent)) {
37
36
  throw new Error(`Cannot add '${this.typeName}' to '${parent.typeName}'`);
38
37
  }
38
+
39
39
  this.parent = parent;
40
40
  }
41
41
 
@@ -47,48 +47,46 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
47
47
  if (!this.isValidChild(child)) {
48
48
  throw new Error(`Cannot append '${child.typeName}' to '${this.typeName}'`);
49
49
  }
50
- this.childIndices.set(child, this.children.length);
50
+
51
+ const existingIndex = this.children.indexOf(child);
52
+ if (existingIndex !== -1) {
53
+ this.children.splice(existingIndex, 1);
54
+ }
51
55
  this.children.push(child);
52
- child.setParent(this);
56
+
57
+ if (child.parent !== this) {
58
+ child.setParent(this);
59
+ }
53
60
  }
54
61
 
55
62
  public removeChild(child: TChild): void {
56
- const index = this.childIndices.get(child);
57
- if (index !== undefined) {
58
- child.setParent(null);
63
+ child.setParent(null);
64
+ const index = this.children.indexOf(child);
65
+
66
+ if (index !== -1) {
59
67
  this.children.splice(index, 1);
60
- this.childIndices.delete(child);
61
- this.rebuildChildIndices(index);
62
68
  }
63
69
  }
64
70
 
65
71
  public insertBefore(child: TChild, before: TChild): void {
66
- const beforeIndex = this.childIndices.get(before);
67
- if (beforeIndex === undefined) {
68
- throw new Error(`Cannot find 'before' child '${before.typeName}' in '${this.typeName}'`);
72
+ if (!this.isValidChild(child)) {
73
+ throw new Error(`Cannot insert '${child.typeName}' into '${this.typeName}'`);
69
74
  }
70
75
 
71
- const existingIndex = this.childIndices.get(child);
72
- if (existingIndex !== undefined) {
76
+ const existingIndex = this.children.indexOf(child);
77
+ if (existingIndex !== -1) {
73
78
  this.children.splice(existingIndex, 1);
74
- const adjustedIndex = existingIndex < beforeIndex ? beforeIndex - 1 : beforeIndex;
75
- this.children.splice(adjustedIndex, 0, child);
76
- this.rebuildChildIndices(Math.min(existingIndex, adjustedIndex));
77
- return;
78
79
  }
79
80
 
80
- if (!this.isValidChild(child)) {
81
- throw new Error(`Cannot insert '${child.typeName}' into '${this.typeName}'`);
81
+ const beforeIndex = this.children.indexOf(before);
82
+ if (beforeIndex === -1) {
83
+ throw new Error(`Cannot find 'before' child '${before.typeName}' in '${this.typeName}'`);
82
84
  }
83
85
 
84
86
  this.children.splice(beforeIndex, 0, child);
85
- this.rebuildChildIndices(beforeIndex);
86
- child.setParent(this);
87
- }
88
87
 
89
- private rebuildChildIndices(fromIndex: number): void {
90
- for (let i = fromIndex; i < this.children.length; i++) {
91
- this.childIndices.set(this.children[i] as TChild, i);
88
+ if (child.parent !== this) {
89
+ child.setParent(this);
92
90
  }
93
91
  }
94
92
 
@@ -3,6 +3,7 @@ import { Node } from "../node.js";
3
3
  import type { Container, Props } from "../types.js";
4
4
  import { MenuNode } from "./menu.js";
5
5
  import { MenuModel } from "./models/menu.js";
6
+ import { WindowNode } from "./window.js";
6
7
 
7
8
  export class ApplicationNode extends Node<Gtk.Application, Props, Node, Node> {
8
9
  private menu: MenuModel;
@@ -51,6 +52,10 @@ export class ApplicationNode extends Node<Gtk.Application, Props, Node, Node> {
51
52
  return;
52
53
  }
53
54
 
55
+ if (child instanceof WindowNode) {
56
+ child.container.setVisible(false);
57
+ }
58
+
54
59
  super.removeChild(child);
55
60
  }
56
61
  }