@gtkx/react 0.18.8 → 0.19.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 (184) hide show
  1. package/dist/generated/internal.d.ts +6 -0
  2. package/dist/generated/internal.d.ts.map +1 -1
  3. package/dist/generated/internal.js +331 -44
  4. package/dist/generated/internal.js.map +1 -1
  5. package/dist/generated/jsx.d.ts +178 -2
  6. package/dist/generated/jsx.d.ts.map +1 -1
  7. package/dist/generated/jsx.js.map +1 -1
  8. package/dist/host-config.d.ts.map +1 -1
  9. package/dist/host-config.js +46 -10
  10. package/dist/host-config.js.map +1 -1
  11. package/dist/jsx.d.ts +133 -13
  12. package/dist/jsx.d.ts.map +1 -1
  13. package/dist/jsx.js +41 -2
  14. package/dist/jsx.js.map +1 -1
  15. package/dist/metadata.d.ts +1 -0
  16. package/dist/metadata.d.ts.map +1 -1
  17. package/dist/metadata.js +3 -1
  18. package/dist/metadata.js.map +1 -1
  19. package/dist/node.d.ts +2 -0
  20. package/dist/node.d.ts.map +1 -1
  21. package/dist/node.js +22 -6
  22. package/dist/node.js.map +1 -1
  23. package/dist/nodes/column-view-column.d.ts +5 -1
  24. package/dist/nodes/column-view-column.d.ts.map +1 -1
  25. package/dist/nodes/column-view-column.js +31 -7
  26. package/dist/nodes/column-view-column.js.map +1 -1
  27. package/dist/nodes/column-view.d.ts +4 -3
  28. package/dist/nodes/column-view.d.ts.map +1 -1
  29. package/dist/nodes/column-view.js +47 -12
  30. package/dist/nodes/column-view.js.map +1 -1
  31. package/dist/nodes/drop-down.d.ts +12 -2
  32. package/dist/nodes/drop-down.d.ts.map +1 -1
  33. package/dist/nodes/drop-down.js +151 -5
  34. package/dist/nodes/drop-down.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 +11 -3
  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/grid-view.d.ts +6 -5
  48. package/dist/nodes/grid-view.d.ts.map +1 -1
  49. package/dist/nodes/grid-view.js +23 -18
  50. package/dist/nodes/grid-view.js.map +1 -1
  51. package/dist/nodes/internal/accessible.d.ts +5 -0
  52. package/dist/nodes/internal/accessible.d.ts.map +1 -0
  53. package/dist/nodes/internal/accessible.js +119 -0
  54. package/dist/nodes/internal/accessible.js.map +1 -0
  55. package/dist/nodes/internal/base-item-renderer.d.ts.map +1 -1
  56. package/dist/nodes/internal/base-item-renderer.js +0 -1
  57. package/dist/nodes/internal/base-item-renderer.js.map +1 -1
  58. package/dist/nodes/internal/construct.d.ts +10 -0
  59. package/dist/nodes/internal/construct.d.ts.map +1 -0
  60. package/dist/nodes/internal/construct.js +68 -0
  61. package/dist/nodes/internal/construct.js.map +1 -0
  62. package/dist/nodes/internal/grid-item-renderer.d.ts +1 -0
  63. package/dist/nodes/internal/grid-item-renderer.d.ts.map +1 -1
  64. package/dist/nodes/internal/grid-item-renderer.js +6 -0
  65. package/dist/nodes/internal/grid-item-renderer.js.map +1 -1
  66. package/dist/nodes/internal/header-item-renderer.d.ts +23 -0
  67. package/dist/nodes/internal/header-item-renderer.d.ts.map +1 -0
  68. package/dist/nodes/internal/header-item-renderer.js +87 -0
  69. package/dist/nodes/internal/header-item-renderer.js.map +1 -0
  70. package/dist/nodes/internal/header-renderer-manager.d.ts +13 -0
  71. package/dist/nodes/internal/header-renderer-manager.d.ts.map +1 -0
  72. package/dist/nodes/internal/header-renderer-manager.js +20 -0
  73. package/dist/nodes/internal/header-renderer-manager.js.map +1 -0
  74. package/dist/nodes/internal/list-item-renderer.d.ts +1 -0
  75. package/dist/nodes/internal/list-item-renderer.d.ts.map +1 -1
  76. package/dist/nodes/internal/list-item-renderer.js +6 -0
  77. package/dist/nodes/internal/list-item-renderer.js.map +1 -1
  78. package/dist/nodes/internal/list-store.d.ts +10 -11
  79. package/dist/nodes/internal/list-store.d.ts.map +1 -1
  80. package/dist/nodes/internal/list-store.js +28 -29
  81. package/dist/nodes/internal/list-store.js.map +1 -1
  82. package/dist/nodes/internal/sectioned-list-store.d.ts +50 -0
  83. package/dist/nodes/internal/sectioned-list-store.d.ts.map +1 -0
  84. package/dist/nodes/internal/sectioned-list-store.js +250 -0
  85. package/dist/nodes/internal/sectioned-list-store.js.map +1 -0
  86. package/dist/nodes/internal/selection-helpers.d.ts +12 -0
  87. package/dist/nodes/internal/selection-helpers.d.ts.map +1 -0
  88. package/dist/nodes/internal/selection-helpers.js +25 -0
  89. package/dist/nodes/internal/selection-helpers.js.map +1 -0
  90. package/dist/nodes/internal/selection-model-controller.d.ts.map +1 -1
  91. package/dist/nodes/internal/selection-model-controller.js +3 -0
  92. package/dist/nodes/internal/selection-model-controller.js.map +1 -1
  93. package/dist/nodes/internal/simple-list-store.d.ts +7 -12
  94. package/dist/nodes/internal/simple-list-store.d.ts.map +1 -1
  95. package/dist/nodes/internal/simple-list-store.js +58 -35
  96. package/dist/nodes/internal/simple-list-store.js.map +1 -1
  97. package/dist/nodes/internal/text-buffer-controller.d.ts +4 -0
  98. package/dist/nodes/internal/text-buffer-controller.d.ts.map +1 -1
  99. package/dist/nodes/internal/text-buffer-controller.js +49 -9
  100. package/dist/nodes/internal/text-buffer-controller.js.map +1 -1
  101. package/dist/nodes/internal/tree-store.d.ts +3 -0
  102. package/dist/nodes/internal/tree-store.d.ts.map +1 -1
  103. package/dist/nodes/internal/tree-store.js +55 -10
  104. package/dist/nodes/internal/tree-store.js.map +1 -1
  105. package/dist/nodes/list-section.d.ts +27 -0
  106. package/dist/nodes/list-section.d.ts.map +1 -0
  107. package/dist/nodes/list-section.js +43 -0
  108. package/dist/nodes/list-section.js.map +1 -0
  109. package/dist/nodes/list-view.d.ts +6 -3
  110. package/dist/nodes/list-view.d.ts.map +1 -1
  111. package/dist/nodes/list-view.js +54 -14
  112. package/dist/nodes/list-view.js.map +1 -1
  113. package/dist/nodes/models/list.d.ts +13 -5
  114. package/dist/nodes/models/list.d.ts.map +1 -1
  115. package/dist/nodes/models/list.js +135 -21
  116. package/dist/nodes/models/list.js.map +1 -1
  117. package/dist/nodes/shortcut.d.ts +3 -2
  118. package/dist/nodes/shortcut.d.ts.map +1 -1
  119. package/dist/nodes/shortcut.js +19 -4
  120. package/dist/nodes/shortcut.js.map +1 -1
  121. package/dist/nodes/text-anchor.d.ts.map +1 -1
  122. package/dist/nodes/text-anchor.js +7 -1
  123. package/dist/nodes/text-anchor.js.map +1 -1
  124. package/dist/nodes/text-tag.d.ts.map +1 -1
  125. package/dist/nodes/text-tag.js +5 -1
  126. package/dist/nodes/text-tag.js.map +1 -1
  127. package/dist/nodes/text-view.d.ts +1 -0
  128. package/dist/nodes/text-view.d.ts.map +1 -1
  129. package/dist/nodes/text-view.js +4 -0
  130. package/dist/nodes/text-view.js.map +1 -1
  131. package/dist/nodes/widget.d.ts +0 -2
  132. package/dist/nodes/widget.d.ts.map +1 -1
  133. package/dist/nodes/widget.js +44 -61
  134. package/dist/nodes/widget.js.map +1 -1
  135. package/dist/registry.d.ts.map +1 -1
  136. package/dist/registry.js +2 -2
  137. package/dist/registry.js.map +1 -1
  138. package/package.json +3 -3
  139. package/src/generated/internal.ts +333 -44
  140. package/src/generated/jsx.ts +178 -2
  141. package/src/host-config.ts +41 -10
  142. package/src/jsx.ts +166 -15
  143. package/src/metadata.ts +5 -1
  144. package/src/node.ts +20 -6
  145. package/src/nodes/column-view-column.ts +35 -7
  146. package/src/nodes/column-view.ts +63 -13
  147. package/src/nodes/drop-down.ts +182 -6
  148. package/src/nodes/event-controller.ts +11 -3
  149. package/src/nodes/fixed-child.ts +24 -23
  150. package/src/nodes/font-dialog-button.ts +10 -0
  151. package/src/nodes/grid-view.ts +29 -19
  152. package/src/nodes/internal/accessible.ts +156 -0
  153. package/src/nodes/internal/base-item-renderer.ts +0 -1
  154. package/src/nodes/internal/construct.ts +90 -0
  155. package/src/nodes/internal/grid-item-renderer.ts +7 -0
  156. package/src/nodes/internal/header-item-renderer.ts +105 -0
  157. package/src/nodes/internal/header-renderer-manager.ts +33 -0
  158. package/src/nodes/internal/list-item-renderer.ts +7 -0
  159. package/src/nodes/internal/list-store.ts +32 -30
  160. package/src/nodes/internal/sectioned-list-store.ts +287 -0
  161. package/src/nodes/internal/selection-helpers.ts +35 -0
  162. package/src/nodes/internal/selection-model-controller.ts +4 -0
  163. package/src/nodes/internal/simple-list-store.ts +60 -43
  164. package/src/nodes/internal/text-buffer-controller.ts +51 -8
  165. package/src/nodes/internal/tree-store.ts +61 -9
  166. package/src/nodes/list-section.ts +64 -0
  167. package/src/nodes/list-view.ts +65 -14
  168. package/src/nodes/models/list.ts +147 -37
  169. package/src/nodes/shortcut.ts +22 -5
  170. package/src/nodes/text-anchor.ts +6 -1
  171. package/src/nodes/text-tag.ts +7 -1
  172. package/src/nodes/text-view.ts +5 -0
  173. package/src/nodes/widget.ts +45 -62
  174. package/src/registry.ts +4 -2
  175. package/dist/nodes/models/grid.d.ts +0 -28
  176. package/dist/nodes/models/grid.d.ts.map +0 -1
  177. package/dist/nodes/models/grid.js +0 -69
  178. package/dist/nodes/models/grid.js.map +0 -1
  179. package/dist/nodes/shortcut-controller.d.ts +0 -10
  180. package/dist/nodes/shortcut-controller.d.ts.map +0 -1
  181. package/dist/nodes/shortcut-controller.js +0 -23
  182. package/dist/nodes/shortcut-controller.js.map +0 -1
  183. package/src/nodes/models/grid.ts +0 -105
  184. package/src/nodes/shortcut-controller.ts +0 -27
package/src/jsx.ts CHANGED
@@ -7,7 +7,14 @@ import type * as GtkSource from "@gtkx/ffi/gtksource";
7
7
  import type * as Pango from "@gtkx/ffi/pango";
8
8
  import type { ReactElement, ReactNode } from "react";
9
9
  import { createElement } from "react";
10
- import type { WidgetSlotNames } from "./generated/jsx.js";
10
+ import type {
11
+ AdwComboRowProps as IntrinsicAdwComboRowProps,
12
+ GtkColumnViewProps as IntrinsicGtkColumnViewProps,
13
+ GtkDropDownProps as IntrinsicGtkDropDownProps,
14
+ GtkGridViewProps as IntrinsicGtkGridViewProps,
15
+ GtkListViewProps as IntrinsicGtkListViewProps,
16
+ WidgetSlotNames,
17
+ } from "./generated/jsx.js";
11
18
 
12
19
  /**
13
20
  * CSS properties that can be animated on a widget.
@@ -132,15 +139,15 @@ export type AnimationProps = {
132
139
  /**
133
140
  * Props for the Shortcut virtual element.
134
141
  *
135
- * Defines a keyboard shortcut. Must be a child of `x.ShortcutController`.
142
+ * Defines a keyboard shortcut. Must be a child of `<GtkShortcutController>`.
136
143
  *
137
144
  * @example
138
145
  * ```tsx
139
- * <x.ShortcutController>
146
+ * <GtkShortcutController>
140
147
  * <x.Shortcut trigger="<Control>s" onActivate={save} />
141
148
  * <x.Shortcut trigger={["F5", "<Control>r"]} onActivate={refresh} />
142
149
  * <x.Shortcut trigger="Escape" onActivate={cancel} disabled={!canCancel} />
143
- * </x.ShortcutController>
150
+ * </GtkShortcutController>
144
151
  * ```
145
152
  */
146
153
  export type ShortcutProps = {
@@ -174,6 +181,8 @@ export type ShortcutProps = {
174
181
  * ```
175
182
  */
176
183
  export type TextAnchorProps = {
184
+ /** Replacement character displayed when the widget is not visible (e.g. in serialized text) */
185
+ replacementChar?: string;
177
186
  /** The widget to embed at this anchor position */
178
187
  children?: ReactNode;
179
188
  };
@@ -359,8 +368,6 @@ export type ContainerSlotNames = {
359
368
 
360
369
  /**
361
370
  * Props for method-based container slot child positioning.
362
- *
363
- * @see {@link x.ContainerSlot} for type-safe usage
364
371
  */
365
372
  export type ContainerSlotProps = {
366
373
  /** The method name to call on the parent widget */
@@ -393,6 +400,23 @@ export type ListItemProps<T = unknown> = {
393
400
  children?: ReactNode;
394
401
  };
395
402
 
403
+ /**
404
+ * Props for section headers in a GtkListView, GtkGridView, or GtkColumnView.
405
+ *
406
+ * Wraps ListItems to group them into sections. When used with `renderHeader`,
407
+ * the view displays a header at the top of each section.
408
+ *
409
+ * @typeParam T - The type of data associated with this section header
410
+ */
411
+ export type ListSectionProps<T = unknown> = {
412
+ /** Unique identifier for this section */
413
+ id: string;
414
+ /** The data value passed to `renderHeader` for this section */
415
+ value: T;
416
+ /** ListItem children belonging to this section */
417
+ children?: ReactNode;
418
+ };
419
+
396
420
  /**
397
421
  * Props for positioning children within a GtkGrid.
398
422
  *
@@ -447,6 +471,8 @@ export type ColumnViewColumnProps<T = unknown> = {
447
471
  id: string;
448
472
  /** Whether clicking the header sorts by this column */
449
473
  sortable?: boolean;
474
+ /** Whether this column is visible */
475
+ visible?: boolean;
450
476
  /** Function to render the cell content for each row */
451
477
  renderCell: (item: T | null) => ReactNode;
452
478
  /** Menu items for the column header context menu */
@@ -670,28 +696,34 @@ type BaseListViewProps = {
670
696
  selectionMode?: Gtk.SelectionMode | null;
671
697
  };
672
698
 
673
- export type ListViewProps = BaseListViewProps & {
699
+ export type ListViewProps<T = unknown, S = unknown> = BaseListViewProps & {
674
700
  /** 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;
701
+ renderItem: (item: T | null, row?: Gtk.TreeListRow | null) => ReactNode;
677
702
  /** Whether to automatically expand new tree rows (default: false) */
678
703
  autoexpand?: boolean;
704
+ /** Function to render section headers when using ListSection children */
705
+ renderHeader?: ((item: S | null) => ReactNode) | null;
679
706
  };
680
707
 
681
- export type GridViewProps = BaseListViewProps & {
708
+ export type GridViewProps<T = unknown> = BaseListViewProps & {
682
709
  /** Function to render each grid item */
683
- // biome-ignore lint/suspicious/noExplicitAny: contravariant parameter requires any for typed callbacks
684
- renderItem: (item: any) => ReactNode;
710
+ renderItem: (item: T | null) => ReactNode;
685
711
  };
686
712
 
687
713
  /**
688
714
  * Props shared by single-selection dropdown widgets (GtkDropDown, AdwComboRow).
689
715
  */
690
- export type DropDownProps = {
716
+ export type DropDownProps<T = unknown, S = unknown> = {
691
717
  /** ID of the currently selected item */
692
718
  selectedId?: string | null;
693
719
  /** Callback fired when the selected item changes */
694
720
  onSelectionChanged?: ((id: string) => void) | null;
721
+ /** Function to render each item. Sets the primary factory, used for both button and popup list unless overridden by renderListItem. */
722
+ renderItem?: ((item: T | null) => ReactNode) | null;
723
+ /** Function to render items in the popup list only, overriding renderItem for the list. */
724
+ renderListItem?: ((item: T | null) => ReactNode) | null;
725
+ /** Function to render section headers when using ListSection children */
726
+ renderHeader?: ((item: S | null) => ReactNode) | null;
695
727
  };
696
728
 
697
729
  /**
@@ -854,7 +886,32 @@ export const x = {
854
886
  * </GtkListView>
855
887
  * ```
856
888
  */
857
- ListItem: "ListItem" as const,
889
+ ListItem<T = unknown>(props: ListItemProps<T>): ReactElement {
890
+ return createElement("ListItem", props, props.children);
891
+ },
892
+
893
+ /**
894
+ * Component for defining section headers in list/grid views.
895
+ *
896
+ * Wraps ListItems to group them into sections with headers.
897
+ * Requires `renderHeader` on the parent view to render the header content.
898
+ *
899
+ * @example
900
+ * ```tsx
901
+ * <GtkGridView
902
+ * renderItem={(item) => <GtkLabel label={item.name} />}
903
+ * renderHeader={(header) => <GtkLabel label={header.title} />}
904
+ * >
905
+ * <x.ListSection id="section1" value={{ title: "Section 1" }}>
906
+ * <x.ListItem id="a" value={{ name: "Item A" }} />
907
+ * <x.ListItem id="b" value={{ name: "Item B" }} />
908
+ * </x.ListSection>
909
+ * </GtkGridView>
910
+ * ```
911
+ */
912
+ ListSection<T = unknown>(props: ListSectionProps<T>): ReactElement {
913
+ return createElement("ListSection", props, props.children);
914
+ },
858
915
 
859
916
  /**
860
917
  * Component for defining columns in a ColumnView (table widget).
@@ -1078,7 +1135,7 @@ export const x = {
1078
1135
  /**
1079
1136
  * A keyboard shortcut definition.
1080
1137
  *
1081
- * Must be a child of `x.ShortcutController`.
1138
+ * Must be a child of `<GtkShortcutController>`.
1082
1139
  *
1083
1140
  * @example
1084
1141
  * ```tsx
@@ -1125,6 +1182,7 @@ declare global {
1125
1182
  FixedChild: FixedChildProps;
1126
1183
  GridChild: GridChildProps;
1127
1184
  ListItem: ListItemProps;
1185
+ ListSection: ListSectionProps;
1128
1186
  MenuItem: MenuItemProps;
1129
1187
  MenuSection: MenuSectionProps;
1130
1188
  MenuSubmenu: MenuSubmenuProps;
@@ -1144,7 +1202,61 @@ declare global {
1144
1202
  }
1145
1203
  }
1146
1204
 
1205
+ export type AccessibleProps = {
1206
+ accessibleAutocomplete?: Gtk.AccessibleAutocomplete;
1207
+ accessibleDescription?: string;
1208
+ accessibleHasPopup?: boolean;
1209
+ accessibleKeyShortcuts?: string;
1210
+ accessibleLabel?: string;
1211
+ accessibleLevel?: number;
1212
+ accessibleModal?: boolean;
1213
+ accessibleMultiLine?: boolean;
1214
+ accessibleMultiSelectable?: boolean;
1215
+ accessibleOrientation?: Gtk.Orientation;
1216
+ accessiblePlaceholder?: string;
1217
+ accessibleReadOnly?: boolean;
1218
+ accessibleRequired?: boolean;
1219
+ accessibleRoleDescription?: string;
1220
+ accessibleSort?: Gtk.AccessibleSort;
1221
+ accessibleValueMax?: number;
1222
+ accessibleValueMin?: number;
1223
+ accessibleValueNow?: number;
1224
+ accessibleValueText?: string;
1225
+ accessibleHelpText?: string;
1226
+
1227
+ accessibleBusy?: boolean;
1228
+ accessibleChecked?: Gtk.AccessibleTristate;
1229
+ accessibleDisabled?: boolean;
1230
+ accessibleExpanded?: boolean;
1231
+ accessibleHidden?: boolean;
1232
+ accessibleInvalid?: Gtk.AccessibleInvalidState;
1233
+ accessiblePressed?: Gtk.AccessibleTristate;
1234
+ accessibleSelected?: boolean;
1235
+ accessibleVisited?: boolean;
1236
+
1237
+ accessibleActiveDescendant?: Gtk.Widget;
1238
+ accessibleColCount?: number;
1239
+ accessibleColIndex?: number;
1240
+ accessibleColIndexText?: string;
1241
+ accessibleColSpan?: number;
1242
+ accessibleControls?: Gtk.Widget[];
1243
+ accessibleDescribedBy?: Gtk.Widget[];
1244
+ accessibleDetails?: Gtk.Widget[];
1245
+ accessibleErrorMessage?: Gtk.Widget[];
1246
+ accessibleFlowTo?: Gtk.Widget[];
1247
+ accessibleLabelledBy?: Gtk.Widget[];
1248
+ accessibleOwns?: Gtk.Widget[];
1249
+ accessiblePosInSet?: number;
1250
+ accessibleRowCount?: number;
1251
+ accessibleRowIndex?: number;
1252
+ accessibleRowIndexText?: string;
1253
+ accessibleRowSpan?: number;
1254
+ accessibleSetSize?: number;
1255
+ };
1256
+
1147
1257
  declare module "./generated/jsx.js" {
1258
+ interface WidgetProps extends AccessibleProps {}
1259
+
1148
1260
  interface GtkRangeProps extends Omit<AdjustableProps, "onValueChanged"> {
1149
1261
  /** Callback fired when the range value changes */
1150
1262
  onValueChanged?: ((value: number, self: Gtk.Range) => void) | null;
@@ -1218,6 +1330,8 @@ declare module "./generated/jsx.js" {
1218
1330
  onSortChanged?: ((column: string | null, order: Gtk.SortType) => void) | null;
1219
1331
  /** Estimated row height in pixels for virtualization */
1220
1332
  estimatedRowHeight?: number | null;
1333
+ /** Function to render section headers when using ListSection children */
1334
+ renderHeader?: ((item: unknown) => ReactNode) | null;
1221
1335
  }
1222
1336
 
1223
1337
  interface GtkDropDownProps extends DropDownProps {}
@@ -1261,6 +1375,10 @@ declare module "./generated/jsx.js" {
1261
1375
  interface GtkFontDialogButtonProps extends DialogButtonProps {
1262
1376
  /** Callback fired when the selected font changes */
1263
1377
  onFontDescChanged?: ((fontDesc: Pango.FontDescription) => void) | null;
1378
+ /** Filter to restrict which fonts are shown in the dialog */
1379
+ filter?: Gtk.Filter | null;
1380
+ /** Custom font map to select fonts from */
1381
+ fontMap?: Pango.FontMap | null;
1264
1382
  }
1265
1383
 
1266
1384
  interface GtkAboutDialogProps {
@@ -1293,4 +1411,37 @@ declare module "./generated/jsx.js" {
1293
1411
  }
1294
1412
  }
1295
1413
 
1414
+ export function GtkListView<T = unknown, S = unknown>(
1415
+ props: Omit<IntrinsicGtkListViewProps, keyof ListViewProps> & ListViewProps<T, S> & { children?: ReactNode },
1416
+ ): ReactElement {
1417
+ return createElement("GtkListView", props);
1418
+ }
1419
+
1420
+ export function GtkGridView<T = unknown>(
1421
+ props: Omit<IntrinsicGtkGridViewProps, keyof GridViewProps> & GridViewProps<T> & { children?: ReactNode },
1422
+ ): ReactElement {
1423
+ return createElement("GtkGridView", props);
1424
+ }
1425
+
1426
+ export function GtkDropDown<T = unknown, S = unknown>(
1427
+ props: Omit<IntrinsicGtkDropDownProps, keyof DropDownProps> & DropDownProps<T, S> & { children?: ReactNode },
1428
+ ): ReactElement {
1429
+ return createElement("GtkDropDown", props);
1430
+ }
1431
+
1432
+ export function AdwComboRow<T = unknown, S = unknown>(
1433
+ props: Omit<IntrinsicAdwComboRowProps, keyof DropDownProps> & DropDownProps<T, S> & { children?: ReactNode },
1434
+ ): ReactElement {
1435
+ return createElement("AdwComboRow", props);
1436
+ }
1437
+
1438
+ export function GtkColumnView<T = unknown>(
1439
+ props: Omit<IntrinsicGtkColumnViewProps, "renderHeader"> & {
1440
+ renderHeader?: ((item: T | null) => ReactNode) | null;
1441
+ children?: ReactNode;
1442
+ },
1443
+ ): ReactElement {
1444
+ return createElement("GtkColumnView", props);
1445
+ }
1446
+
1296
1447
  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 { CONSTRUCT_ONLY_PROPS, 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,10 @@ 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) => (CONSTRUCT_ONLY_PROPS[typeName]?.[key] !== undefined ? true : null)) ??
35
+ false;
36
+
33
37
  export const resolveSignal = (instance: Container, propName: string): string | null => {
34
38
  if (propName === "onNotify") return "notify";
35
39
  return walkPrototypeChain(instance, (typeName) => SIGNALS[typeName]?.[propName] ?? null);
package/src/node.ts CHANGED
@@ -15,6 +15,7 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
15
15
  parent: TParent | null = null;
16
16
  children: TChild[] = [];
17
17
  private childIndices = new Map<TChild, number>();
18
+ private childrenDirty = false;
18
19
 
19
20
  constructor(typeName: string, props: TProps, container: TContainer, rootContainer: Container) {
20
21
  this.typeName = typeName;
@@ -44,6 +45,7 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
44
45
  }
45
46
 
46
47
  public appendChild(child: TChild): void {
48
+ if (this.childrenDirty) this.flushChildRemovals();
47
49
  if (!this.isValidChild(child)) {
48
50
  throw new Error(`Cannot append '${child.typeName}' to '${this.typeName}'`);
49
51
  }
@@ -53,16 +55,18 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
53
55
  }
54
56
 
55
57
  public removeChild(child: TChild): void {
56
- const index = this.childIndices.get(child);
57
- if (index !== undefined) {
58
- child.setParent(null);
59
- this.children.splice(index, 1);
60
- this.childIndices.delete(child);
61
- this.rebuildChildIndices(index);
58
+ if (!this.childIndices.has(child)) return;
59
+ child.setParent(null);
60
+ this.childIndices.delete(child);
61
+ if (!this.childrenDirty) {
62
+ this.childrenDirty = true;
63
+ queueMicrotask(() => this.flushChildRemovals());
62
64
  }
63
65
  }
64
66
 
65
67
  public insertBefore(child: TChild, before: TChild): void {
68
+ if (this.childrenDirty) this.flushChildRemovals();
69
+
66
70
  const beforeIndex = this.childIndices.get(before);
67
71
  if (beforeIndex === undefined) {
68
72
  throw new Error(`Cannot find 'before' child '${before.typeName}' in '${this.typeName}'`);
@@ -86,6 +90,16 @@ export class Node<TContainer = any, TProps = any, TParent extends Node = any, TC
86
90
  child.setParent(this);
87
91
  }
88
92
 
93
+ private flushChildRemovals(): void {
94
+ if (!this.childrenDirty) return;
95
+ this.childrenDirty = false;
96
+ this.children = this.children.filter((c) => this.childIndices.has(c));
97
+ this.childIndices.clear();
98
+ for (let i = 0; i < this.children.length; i++) {
99
+ this.childIndices.set(this.children[i] as TChild, i);
100
+ }
101
+ }
102
+
89
103
  private rebuildChildIndices(fromIndex: number): void {
90
104
  for (let i = fromIndex; i < this.children.length; i++) {
91
105
  this.childIndices.set(this.children[i] as TChild, i);
@@ -3,7 +3,9 @@ import * as Gtk from "@gtkx/ffi/gtk";
3
3
  import type { ColumnViewColumnProps } from "../jsx.js";
4
4
  import type { Node } from "../node.js";
5
5
  import type { Container } from "../types.js";
6
+ import { GridItemRenderer } from "./internal/grid-item-renderer.js";
6
7
  import { ListItemRenderer } from "./internal/list-item-renderer.js";
8
+ import type { ListStore } from "./internal/list-store.js";
7
9
  import { hasChanged } from "./internal/props.js";
8
10
  import type { TreeStore } from "./internal/tree-store.js";
9
11
  import { MenuNode } from "./menu.js";
@@ -20,16 +22,17 @@ export class ColumnViewColumnNode extends VirtualNode<ColumnViewColumnProps, Wid
20
22
  return parent instanceof WidgetNode && parent.container instanceof Gtk.ColumnView;
21
23
  }
22
24
  private column: Gtk.ColumnViewColumn;
23
- private itemRenderer: ListItemRenderer;
25
+ private treeRenderer: ListItemRenderer | null;
26
+ private flatRenderer: GridItemRenderer | null = null;
24
27
  private menu: MenuModel | null = null;
25
28
  private actionGroup: Gio.SimpleActionGroup | null = null;
26
29
  private columnView: Gtk.ColumnView | null = null;
27
30
 
28
31
  constructor(typeName: string, props: ColumnViewColumnProps, container: undefined, rootContainer: Container) {
29
32
  super(typeName, props, container, rootContainer);
30
- this.itemRenderer = new ListItemRenderer(this.signalStore);
33
+ this.treeRenderer = new ListItemRenderer(this.signalStore);
31
34
  this.column = new Gtk.ColumnViewColumn();
32
- this.column.setFactory(this.itemRenderer.getFactory());
35
+ this.column.setFactory(this.treeRenderer.getFactory());
33
36
  }
34
37
 
35
38
  public override appendChild(child: MenuNode): void {
@@ -61,7 +64,8 @@ export class ColumnViewColumnNode extends VirtualNode<ColumnViewColumnProps, Wid
61
64
 
62
65
  public override detachDeletedInstance(): void {
63
66
  this.cleanupMenu();
64
- this.itemRenderer.dispose();
67
+ this.treeRenderer?.dispose();
68
+ this.flatRenderer?.dispose();
65
69
  super.detachDeletedInstance();
66
70
  }
67
71
 
@@ -69,12 +73,31 @@ export class ColumnViewColumnNode extends VirtualNode<ColumnViewColumnProps, Wid
69
73
  return this.column;
70
74
  }
71
75
 
76
+ public rebindItem(id: string): void {
77
+ this.treeRenderer?.rebindItem(id);
78
+ this.flatRenderer?.rebindItem(id);
79
+ }
80
+
72
81
  public setStore(model: TreeStore | null): void {
73
- this.itemRenderer.setStore(model);
82
+ this.treeRenderer?.setStore(model);
83
+ }
84
+
85
+ public setFlatStore(store: ListStore): void {
86
+ if (this.treeRenderer) {
87
+ this.treeRenderer.dispose();
88
+ this.treeRenderer = null;
89
+ }
90
+ if (!this.flatRenderer) {
91
+ this.flatRenderer = new GridItemRenderer(this.signalStore);
92
+ this.flatRenderer.setRenderFn(this.props.renderCell);
93
+ this.column.setFactory(this.flatRenderer.getFactory());
94
+ }
95
+ this.flatRenderer.setStore(store);
74
96
  }
75
97
 
76
98
  public setEstimatedRowHeight(height: number | null): void {
77
- this.itemRenderer.setEstimatedItemHeight(height);
99
+ this.treeRenderer?.setEstimatedItemHeight(height);
100
+ this.flatRenderer?.setEstimatedItemHeight(height);
78
101
  }
79
102
 
80
103
  public attachToColumnView(columnView: Gtk.ColumnView): void {
@@ -121,7 +144,8 @@ export class ColumnViewColumnNode extends VirtualNode<ColumnViewColumnProps, Wid
121
144
 
122
145
  private applyOwnProps(oldProps: ColumnViewColumnProps | null, newProps: ColumnViewColumnProps): void {
123
146
  if (hasChanged(oldProps, newProps, "renderCell")) {
124
- this.itemRenderer.setRenderFn(newProps.renderCell);
147
+ this.treeRenderer?.setRenderFn(newProps.renderCell);
148
+ this.flatRenderer?.setRenderFn(newProps.renderCell);
125
149
  }
126
150
 
127
151
  if (hasChanged(oldProps, newProps, "title")) {
@@ -153,6 +177,10 @@ export class ColumnViewColumnNode extends VirtualNode<ColumnViewColumnProps, Wid
153
177
  }
154
178
  }
155
179
 
180
+ if (hasChanged(oldProps, newProps, "visible")) {
181
+ this.column.setVisible(newProps.visible ?? true);
182
+ }
183
+
156
184
  if (hasChanged(oldProps, newProps, "sortable")) {
157
185
  if (newProps.sortable) {
158
186
  this.column.setSorter(new Gtk.StringSorter());
@@ -5,24 +5,34 @@ import type { Container } from "../types.js";
5
5
  import { ColumnViewColumnNode } from "./column-view-column.js";
6
6
  import { ContainerSlotNode } from "./container-slot.js";
7
7
  import { EventControllerNode } from "./event-controller.js";
8
+ import type { HeaderItemRenderer } from "./internal/header-item-renderer.js";
9
+ import { updateHeaderRenderer } from "./internal/header-renderer-manager.js";
8
10
  import { filterProps, hasChanged } from "./internal/props.js";
9
11
  import { ListItemNode } from "./list-item.js";
12
+ import { ListSectionNode } from "./list-section.js";
10
13
  import { ListModel, type ListModelProps } from "./models/list.js";
11
14
  import { SlotNode } from "./slot.js";
12
15
  import { WidgetNode } from "./widget.js";
13
16
 
14
- const OWN_PROPS = ["sortColumn", "sortOrder", "onSortChanged", "estimatedRowHeight"] as const;
17
+ const OWN_PROPS = ["sortColumn", "sortOrder", "onSortChanged", "estimatedRowHeight", "renderHeader"] as const;
15
18
 
16
19
  type ColumnViewProps = Pick<GtkColumnViewProps, (typeof OWN_PROPS)[number]> & ListModelProps;
17
- type ColumnViewChild = ListItemNode | ColumnViewColumnNode | EventControllerNode | SlotNode | ContainerSlotNode;
20
+ type ColumnViewChild =
21
+ | ListItemNode
22
+ | ListSectionNode
23
+ | ColumnViewColumnNode
24
+ | EventControllerNode
25
+ | SlotNode
26
+ | ContainerSlotNode;
18
27
 
19
28
  export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps, ColumnViewChild> {
20
- private handleSortChange: (() => void) | null = null;
21
29
  private list: ListModel;
30
+ private headerRenderer: HeaderItemRenderer | null = null;
22
31
 
23
32
  public override isValidChild(child: Node): boolean {
24
33
  return (
25
34
  child instanceof ListItemNode ||
35
+ child instanceof ListSectionNode ||
26
36
  child instanceof ColumnViewColumnNode ||
27
37
  child instanceof EventControllerNode ||
28
38
  child instanceof SlotNode ||
@@ -34,6 +44,9 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
34
44
 
35
45
  constructor(typeName: string, props: ColumnViewProps, container: Gtk.ColumnView, rootContainer: Container) {
36
46
  super(typeName, props, container, rootContainer);
47
+
48
+ const flat = props.renderHeader != null;
49
+
37
50
  this.list = new ListModel(
38
51
  { owner: this, signalStore: this.signalStore },
39
52
  {
@@ -41,13 +54,19 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
41
54
  selected: props.selected,
42
55
  onSelectionChanged: props.onSelectionChanged,
43
56
  },
57
+ flat,
44
58
  );
59
+ this.list.setOnItemUpdated((id) => {
60
+ for (const column of this.columnNodes) {
61
+ column.rebindItem(id);
62
+ }
63
+ });
45
64
  }
46
65
 
47
66
  public override appendChild(child: ColumnViewChild): void {
48
67
  super.appendChild(child);
49
68
 
50
- if (child instanceof ListItemNode) {
69
+ if (child instanceof ListItemNode || child instanceof ListSectionNode) {
51
70
  this.list.appendChild(child);
52
71
  return;
53
72
  }
@@ -60,7 +79,11 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
60
79
  this.container.removeColumn(existingColumn);
61
80
  }
62
81
 
63
- child.setStore(this.list.getStore());
82
+ if (this.list.isFlatMode()) {
83
+ child.setFlatStore(this.list.getFlatStore());
84
+ } else {
85
+ child.setStore(this.list.getStore());
86
+ }
64
87
  child.setEstimatedRowHeight(this.estimatedRowHeight);
65
88
  this.container.appendColumn(child.getColumn());
66
89
  child.attachToColumnView(this.container);
@@ -70,8 +93,8 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
70
93
  public override insertBefore(child: ColumnViewChild, before: ColumnViewChild): void {
71
94
  super.insertBefore(child, before);
72
95
 
73
- if (child instanceof ListItemNode) {
74
- if (before instanceof ListItemNode) {
96
+ if (child instanceof ListItemNode || child instanceof ListSectionNode) {
97
+ if (before instanceof ListItemNode || before instanceof ListSectionNode) {
75
98
  this.list.insertBefore(child, before);
76
99
  }
77
100
  return;
@@ -85,7 +108,11 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
85
108
  this.container.removeColumn(existingColumn);
86
109
  }
87
110
 
88
- child.setStore(this.list.getStore());
111
+ if (this.list.isFlatMode()) {
112
+ child.setFlatStore(this.list.getFlatStore());
113
+ } else {
114
+ child.setStore(this.list.getStore());
115
+ }
89
116
  child.setEstimatedRowHeight(this.estimatedRowHeight);
90
117
 
91
118
  if (before instanceof ColumnViewColumnNode) {
@@ -100,7 +127,7 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
100
127
  }
101
128
 
102
129
  public override removeChild(child: ColumnViewChild): void {
103
- if (child instanceof ListItemNode) {
130
+ if (child instanceof ListItemNode || child instanceof ListSectionNode) {
104
131
  this.list.removeChild(child);
105
132
  super.removeChild(child);
106
133
  return;
@@ -130,7 +157,14 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
130
157
  public override commitUpdate(oldProps: ColumnViewProps | null, newProps: ColumnViewProps): void {
131
158
  super.commitUpdate(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
132
159
  this.applyOwnProps(oldProps, newProps);
160
+
161
+ const previousModel = this.list.getSelectionModel();
133
162
  this.list.updateProps(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
163
+ const currentModel = this.list.getSelectionModel();
164
+
165
+ if (previousModel !== currentModel) {
166
+ this.container.setModel(currentModel);
167
+ }
134
168
  }
135
169
 
136
170
  public override commitMount(): void {
@@ -141,6 +175,7 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
141
175
 
142
176
  public override detachDeletedInstance(): void {
143
177
  this.columnNodes.clear();
178
+ this.headerRenderer?.dispose();
144
179
  super.detachDeletedInstance();
145
180
  }
146
181
 
@@ -150,11 +185,13 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
150
185
  const onSortChanged = newProps.onSortChanged;
151
186
 
152
187
  if (sorter instanceof Gtk.ColumnViewSorter) {
153
- this.handleSortChange = () => {
154
- onSortChanged?.(sorter.getPrimarySortColumn()?.getId() ?? null, sorter.getPrimarySortOrder());
155
- };
188
+ const handleSortChange = onSortChanged
189
+ ? () => {
190
+ onSortChanged(sorter.getPrimarySortColumn()?.getId() ?? null, sorter.getPrimarySortOrder());
191
+ }
192
+ : undefined;
156
193
 
157
- this.signalStore.set(this, sorter, "changed", this.handleSortChange);
194
+ this.signalStore.set(this, sorter, "changed", handleSortChange);
158
195
  }
159
196
  }
160
197
 
@@ -175,6 +212,19 @@ export class ColumnViewNode extends WidgetNode<Gtk.ColumnView, ColumnViewProps,
175
212
  column.setEstimatedRowHeight(this.estimatedRowHeight);
176
213
  }
177
214
  }
215
+
216
+ if (hasChanged(oldProps, newProps, "renderHeader")) {
217
+ this.headerRenderer = updateHeaderRenderer(
218
+ this.headerRenderer,
219
+ {
220
+ signalStore: this.signalStore,
221
+ isEnabled: () => this.list.isFlatMode(),
222
+ resolveItem: (id) => this.list.getFlatStore().getHeaderValue(id),
223
+ setFactory: (factory) => this.container.setHeaderFactory(factory),
224
+ },
225
+ newProps.renderHeader,
226
+ );
227
+ }
178
228
  }
179
229
 
180
230
  private findColumn<T>(predicate: (column: Gtk.ColumnViewColumn, index: number) => T | null): T | null {