@gtkx/react 0.19.0 → 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 (189) 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 +5 -6
  6. package/dist/generated/internal.d.ts.map +1 -1
  7. package/dist/generated/internal.js +3473 -260
  8. package/dist/generated/internal.js.map +1 -1
  9. package/dist/generated/jsx.d.ts +0 -324
  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 +2 -0
  15. package/dist/host-config.js.map +1 -1
  16. package/dist/jsx.d.ts +42 -105
  17. package/dist/jsx.d.ts.map +1 -1
  18. package/dist/jsx.js +2 -66
  19. package/dist/jsx.js.map +1 -1
  20. package/dist/metadata.d.ts.map +1 -1
  21. package/dist/metadata.js +7 -3
  22. package/dist/metadata.js.map +1 -1
  23. package/dist/node.d.ts +0 -4
  24. package/dist/node.d.ts.map +1 -1
  25. package/dist/node.js +19 -41
  26. package/dist/node.js.map +1 -1
  27. package/dist/nodes/application.d.ts.map +1 -1
  28. package/dist/nodes/application.js +4 -0
  29. package/dist/nodes/application.js.map +1 -1
  30. package/dist/nodes/column-view-column.d.ts +19 -19
  31. package/dist/nodes/column-view-column.d.ts.map +1 -1
  32. package/dist/nodes/column-view-column.js +130 -119
  33. package/dist/nodes/column-view-column.js.map +1 -1
  34. package/dist/nodes/event-controller.d.ts.map +1 -1
  35. package/dist/nodes/event-controller.js +3 -9
  36. package/dist/nodes/event-controller.js.map +1 -1
  37. package/dist/nodes/internal/accessible.d.ts.map +1 -1
  38. package/dist/nodes/internal/accessible.js.map +1 -1
  39. package/dist/nodes/internal/bound-item.d.ts +4 -0
  40. package/dist/nodes/internal/bound-item.d.ts.map +1 -0
  41. package/dist/nodes/internal/bound-item.js +2 -0
  42. package/dist/nodes/internal/bound-item.js.map +1 -0
  43. package/dist/nodes/internal/construct.d.ts +1 -8
  44. package/dist/nodes/internal/construct.d.ts.map +1 -1
  45. package/dist/nodes/internal/construct.js +30 -54
  46. package/dist/nodes/internal/construct.js.map +1 -1
  47. package/dist/nodes/internal/widget.d.ts.map +1 -1
  48. package/dist/nodes/internal/widget.js +4 -1
  49. package/dist/nodes/internal/widget.js.map +1 -1
  50. package/dist/nodes/list-item-node.d.ts +12 -0
  51. package/dist/nodes/list-item-node.d.ts.map +1 -0
  52. package/dist/nodes/list-item-node.js +23 -0
  53. package/dist/nodes/list-item-node.js.map +1 -0
  54. package/dist/nodes/list.d.ts +100 -0
  55. package/dist/nodes/list.d.ts.map +1 -0
  56. package/dist/nodes/list.js +950 -0
  57. package/dist/nodes/list.js.map +1 -0
  58. package/dist/nodes/notebook-page.d.ts.map +1 -1
  59. package/dist/nodes/notebook-page.js +4 -0
  60. package/dist/nodes/notebook-page.js.map +1 -1
  61. package/dist/nodes/widget.d.ts.map +1 -1
  62. package/dist/nodes/widget.js +9 -8
  63. package/dist/nodes/widget.js.map +1 -1
  64. package/dist/nodes/window.d.ts.map +1 -1
  65. package/dist/nodes/window.js +2 -2
  66. package/dist/nodes/window.js.map +1 -1
  67. package/dist/registry.d.ts +0 -2
  68. package/dist/registry.d.ts.map +1 -1
  69. package/dist/registry.js +4 -13
  70. package/dist/registry.js.map +1 -1
  71. package/dist/types.d.ts +2 -2
  72. package/dist/types.d.ts.map +1 -1
  73. package/package.json +5 -4
  74. package/src/components/list.tsx +83 -0
  75. package/src/generated/internal.ts +3479 -258
  76. package/src/generated/jsx.ts +0 -324
  77. package/src/host-config.ts +2 -0
  78. package/src/jsx.ts +49 -141
  79. package/src/metadata.ts +6 -3
  80. package/src/node.ts +23 -39
  81. package/src/nodes/application.ts +5 -0
  82. package/src/nodes/column-view-column.ts +125 -128
  83. package/src/nodes/event-controller.ts +3 -11
  84. package/src/nodes/internal/accessible.ts +0 -1
  85. package/src/nodes/internal/bound-item.ts +4 -0
  86. package/src/nodes/internal/construct.ts +38 -68
  87. package/src/nodes/internal/widget.ts +3 -1
  88. package/src/nodes/list-item-node.ts +29 -0
  89. package/src/nodes/list.ts +1082 -0
  90. package/src/nodes/notebook-page.ts +4 -0
  91. package/src/nodes/widget.ts +8 -13
  92. package/src/nodes/window.ts +2 -2
  93. package/src/registry.ts +11 -19
  94. package/src/types.ts +7 -2
  95. package/dist/fiber-root.d.ts +0 -4
  96. package/dist/fiber-root.d.ts.map +0 -1
  97. package/dist/fiber-root.js +0 -6
  98. package/dist/fiber-root.js.map +0 -1
  99. package/dist/nodes/column-view.d.ts +0 -37
  100. package/dist/nodes/column-view.d.ts.map +0 -1
  101. package/dist/nodes/column-view.js +0 -205
  102. package/dist/nodes/column-view.js.map +0 -1
  103. package/dist/nodes/drop-down.d.ts +0 -37
  104. package/dist/nodes/drop-down.d.ts.map +0 -1
  105. package/dist/nodes/drop-down.js +0 -231
  106. package/dist/nodes/drop-down.js.map +0 -1
  107. package/dist/nodes/grid-view.d.ts +0 -30
  108. package/dist/nodes/grid-view.d.ts.map +0 -1
  109. package/dist/nodes/grid-view.js +0 -90
  110. package/dist/nodes/grid-view.js.map +0 -1
  111. package/dist/nodes/internal/base-item-renderer.d.ts +0 -28
  112. package/dist/nodes/internal/base-item-renderer.d.ts.map +0 -1
  113. package/dist/nodes/internal/base-item-renderer.js +0 -85
  114. package/dist/nodes/internal/base-item-renderer.js.map +0 -1
  115. package/dist/nodes/internal/grid-item-renderer.d.ts +0 -20
  116. package/dist/nodes/internal/grid-item-renderer.d.ts.map +0 -1
  117. package/dist/nodes/internal/grid-item-renderer.js +0 -66
  118. package/dist/nodes/internal/grid-item-renderer.js.map +0 -1
  119. package/dist/nodes/internal/header-item-renderer.d.ts +0 -23
  120. package/dist/nodes/internal/header-item-renderer.d.ts.map +0 -1
  121. package/dist/nodes/internal/header-item-renderer.js +0 -87
  122. package/dist/nodes/internal/header-item-renderer.js.map +0 -1
  123. package/dist/nodes/internal/header-renderer-manager.d.ts +0 -13
  124. package/dist/nodes/internal/header-renderer-manager.d.ts.map +0 -1
  125. package/dist/nodes/internal/header-renderer-manager.js +0 -20
  126. package/dist/nodes/internal/header-renderer-manager.js.map +0 -1
  127. package/dist/nodes/internal/list-item-renderer.d.ts +0 -27
  128. package/dist/nodes/internal/list-item-renderer.d.ts.map +0 -1
  129. package/dist/nodes/internal/list-item-renderer.js +0 -131
  130. package/dist/nodes/internal/list-item-renderer.js.map +0 -1
  131. package/dist/nodes/internal/list-store.d.ts +0 -21
  132. package/dist/nodes/internal/list-store.d.ts.map +0 -1
  133. package/dist/nodes/internal/list-store.js +0 -90
  134. package/dist/nodes/internal/list-store.js.map +0 -1
  135. package/dist/nodes/internal/sectioned-list-store.d.ts +0 -50
  136. package/dist/nodes/internal/sectioned-list-store.d.ts.map +0 -1
  137. package/dist/nodes/internal/sectioned-list-store.js +0 -250
  138. package/dist/nodes/internal/sectioned-list-store.js.map +0 -1
  139. package/dist/nodes/internal/selection-helpers.d.ts +0 -12
  140. package/dist/nodes/internal/selection-helpers.d.ts.map +0 -1
  141. package/dist/nodes/internal/selection-helpers.js +0 -25
  142. package/dist/nodes/internal/selection-helpers.js.map +0 -1
  143. package/dist/nodes/internal/selection-model-controller.d.ts +0 -26
  144. package/dist/nodes/internal/selection-model-controller.d.ts.map +0 -1
  145. package/dist/nodes/internal/selection-model-controller.js +0 -82
  146. package/dist/nodes/internal/selection-model-controller.js.map +0 -1
  147. package/dist/nodes/internal/simple-list-store.d.ts +0 -15
  148. package/dist/nodes/internal/simple-list-store.d.ts.map +0 -1
  149. package/dist/nodes/internal/simple-list-store.js +0 -110
  150. package/dist/nodes/internal/simple-list-store.js.map +0 -1
  151. package/dist/nodes/internal/tree-store.d.ts +0 -37
  152. package/dist/nodes/internal/tree-store.d.ts.map +0 -1
  153. package/dist/nodes/internal/tree-store.js +0 -253
  154. package/dist/nodes/internal/tree-store.js.map +0 -1
  155. package/dist/nodes/list-item.d.ts +0 -24
  156. package/dist/nodes/list-item.d.ts.map +0 -1
  157. package/dist/nodes/list-item.js +0 -83
  158. package/dist/nodes/list-item.js.map +0 -1
  159. package/dist/nodes/list-section.d.ts +0 -27
  160. package/dist/nodes/list-section.d.ts.map +0 -1
  161. package/dist/nodes/list-section.js +0 -43
  162. package/dist/nodes/list-section.js.map +0 -1
  163. package/dist/nodes/list-view.d.ts +0 -32
  164. package/dist/nodes/list-view.d.ts.map +0 -1
  165. package/dist/nodes/list-view.js +0 -123
  166. package/dist/nodes/list-view.js.map +0 -1
  167. package/dist/nodes/models/list.d.ts +0 -39
  168. package/dist/nodes/models/list.d.ts.map +0 -1
  169. package/dist/nodes/models/list.js +0 -207
  170. package/dist/nodes/models/list.js.map +0 -1
  171. package/src/fiber-root.ts +0 -20
  172. package/src/nodes/column-view.ts +0 -262
  173. package/src/nodes/drop-down.ts +0 -284
  174. package/src/nodes/grid-view.ts +0 -119
  175. package/src/nodes/internal/base-item-renderer.ts +0 -107
  176. package/src/nodes/internal/grid-item-renderer.ts +0 -78
  177. package/src/nodes/internal/header-item-renderer.ts +0 -105
  178. package/src/nodes/internal/header-renderer-manager.ts +0 -33
  179. package/src/nodes/internal/list-item-renderer.ts +0 -162
  180. package/src/nodes/internal/list-store.ts +0 -107
  181. package/src/nodes/internal/sectioned-list-store.ts +0 -287
  182. package/src/nodes/internal/selection-helpers.ts +0 -35
  183. package/src/nodes/internal/selection-model-controller.ts +0 -119
  184. package/src/nodes/internal/simple-list-store.ts +0 -116
  185. package/src/nodes/internal/tree-store.ts +0 -289
  186. package/src/nodes/list-item.ts +0 -107
  187. package/src/nodes/list-section.ts +0 -64
  188. package/src/nodes/list-view.ts +0 -164
  189. package/src/nodes/models/list.ts +0 -250
@@ -1,284 +0,0 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import type { ReactNode } from "react";
3
- import type Reconciler from "react-reconciler";
4
- import { createFiberRoot } from "../fiber-root.js";
5
- import type { AdwComboRowProps, GtkDropDownProps } from "../jsx.js";
6
- import type { Node } from "../node.js";
7
- import { reconciler } from "../reconciler.js";
8
- import type { DropDownWidget } from "../registry.js";
9
- import type { Container } from "../types.js";
10
- import { ContainerSlotNode } from "./container-slot.js";
11
- import { EventControllerNode } from "./event-controller.js";
12
- import type { HeaderItemRenderer } from "./internal/header-item-renderer.js";
13
- import { updateHeaderRenderer } from "./internal/header-renderer-manager.js";
14
- import { filterProps, hasChanged } from "./internal/props.js";
15
- import { SimpleListStore } from "./internal/simple-list-store.js";
16
- import { ListItemNode } from "./list-item.js";
17
- import { ListSectionNode } from "./list-section.js";
18
- import { SlotNode } from "./slot.js";
19
- import { WidgetNode } from "./widget.js";
20
-
21
- const OWN_PROPS = ["selectedId", "onSelectionChanged", "renderItem", "renderListItem", "renderHeader"] as const;
22
-
23
- type DropDownProps = Pick<GtkDropDownProps | AdwComboRowProps, (typeof OWN_PROPS)[number]>;
24
-
25
- type DropDownChild = ListItemNode | ListSectionNode | EventControllerNode | SlotNode | ContainerSlotNode;
26
-
27
- type RenderItemFn = (item: string | null) => ReactNode;
28
-
29
- interface FactoryState {
30
- factory: Gtk.SignalListItemFactory;
31
- fiberRoots: Map<object, Reconciler.FiberRoot>;
32
- tornDown: Set<object>;
33
- boundLabels: Map<string, object>;
34
- }
35
-
36
- export class DropDownNode extends WidgetNode<DropDownWidget, DropDownProps, DropDownChild> {
37
- private store = new SimpleListStore();
38
- private initialSelectedId: string | null | undefined;
39
-
40
- private itemState: FactoryState | null = null;
41
- private renderItemFn: RenderItemFn | null = null;
42
-
43
- private listItemState: FactoryState | null = null;
44
- private renderListItemFn: RenderItemFn | null = null;
45
-
46
- private headerRenderer: HeaderItemRenderer | null = null;
47
-
48
- public override isValidChild(child: Node): boolean {
49
- return (
50
- child instanceof ListItemNode ||
51
- child instanceof ListSectionNode ||
52
- child instanceof EventControllerNode ||
53
- child instanceof SlotNode ||
54
- child instanceof ContainerSlotNode
55
- );
56
- }
57
-
58
- constructor(typeName: string, props: DropDownProps, container: DropDownWidget, rootContainer: Container) {
59
- super(typeName, props, container, rootContainer);
60
- this.store.beginBatch();
61
- this.initialSelectedId = props.selectedId;
62
- }
63
-
64
- public override finalizeInitialChildren(props: DropDownProps): boolean {
65
- super.finalizeInitialChildren(props);
66
- this.store.flushBatch();
67
- this.container.setModel(this.store.getModel());
68
- this.reapplyInitialSelectedId();
69
- return false;
70
- }
71
-
72
- private reapplyInitialSelectedId(): void {
73
- if (this.initialSelectedId == null) return;
74
- const index = this.store.getIndexById(this.initialSelectedId);
75
- this.initialSelectedId = undefined;
76
- if (index !== null) {
77
- this.container.setSelected(index);
78
- }
79
- }
80
-
81
- public override appendChild(child: DropDownChild): void {
82
- super.appendChild(child);
83
- if (child instanceof ListSectionNode) {
84
- this.store.addSection(child.props.id, child.props.value);
85
- child.setStore(this.store);
86
- } else if (child instanceof ListItemNode) {
87
- child.setStore(this.store);
88
- this.store.addItem(child.props.id, child.props.value as string);
89
- }
90
- }
91
-
92
- public override insertBefore(child: DropDownChild, before: DropDownChild): void {
93
- super.insertBefore(child, before);
94
- if (child instanceof ListSectionNode) {
95
- this.store.addSection(child.props.id, child.props.value);
96
- child.setStore(this.store);
97
- } else if (child instanceof ListItemNode && before instanceof ListItemNode) {
98
- child.setStore(this.store);
99
- this.store.insertItemBefore(child.props.id, before.props.id, child.props.value as string);
100
- } else if (child instanceof ListItemNode) {
101
- child.setStore(this.store);
102
- this.store.addItem(child.props.id, child.props.value as string);
103
- }
104
- }
105
-
106
- public override removeChild(child: DropDownChild): void {
107
- if (child instanceof ListSectionNode) {
108
- this.store.removeSection(child.props.id);
109
- child.setStore(null);
110
- } else if (child instanceof ListItemNode) {
111
- this.store.removeItem(child.props.id);
112
- child.setStore(null);
113
- }
114
- super.removeChild(child);
115
- }
116
-
117
- public override commitUpdate(oldProps: DropDownProps | null, newProps: DropDownProps): void {
118
- super.commitUpdate(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
119
- this.applyOwnProps(oldProps, newProps);
120
- }
121
-
122
- public override detachDeletedInstance(): void {
123
- this.disposeFactory(this.itemState);
124
- this.itemState = null;
125
- this.disposeFactory(this.listItemState);
126
- this.listItemState = null;
127
- this.headerRenderer?.dispose();
128
- super.detachDeletedInstance();
129
- }
130
-
131
- private applyOwnProps(oldProps: DropDownProps | null, newProps: DropDownProps): void {
132
- if (hasChanged(oldProps, newProps, "onSelectionChanged")) {
133
- const onSelectionChanged = newProps.onSelectionChanged;
134
-
135
- const handleSelectionChange = onSelectionChanged
136
- ? () => {
137
- const selectedIndex = this.container.getSelected();
138
- const id = this.store.getIdAtIndex(selectedIndex);
139
- if (id !== null) {
140
- onSelectionChanged(id);
141
- }
142
- }
143
- : undefined;
144
-
145
- this.signalStore.set(this, this.container, "notify::selected", handleSelectionChange);
146
- }
147
-
148
- if (hasChanged(oldProps, newProps, "selectedId")) {
149
- const index = newProps.selectedId != null ? this.store.getIndexById(newProps.selectedId) : null;
150
-
151
- if (index !== null) {
152
- this.container.setSelected(index);
153
- }
154
- }
155
-
156
- if (hasChanged(oldProps, newProps, "renderItem")) {
157
- if (newProps.renderItem) {
158
- if (!this.itemState) {
159
- this.itemState = this.createFactory(
160
- () => this.renderItemFn,
161
- (f) => this.container.setFactory(f),
162
- );
163
- }
164
- this.renderItemFn = newProps.renderItem;
165
- this.rebindAll(this.itemState, this.renderItemFn);
166
- } else if (this.itemState) {
167
- this.disposeFactory(this.itemState);
168
- this.itemState = null;
169
- this.container.setFactory(null);
170
- this.renderItemFn = null;
171
- }
172
- }
173
-
174
- if (hasChanged(oldProps, newProps, "renderListItem")) {
175
- if (newProps.renderListItem) {
176
- if (!this.listItemState) {
177
- this.listItemState = this.createFactory(
178
- () => this.renderListItemFn,
179
- (f) => this.container.setListFactory(f),
180
- );
181
- }
182
- this.renderListItemFn = newProps.renderListItem;
183
- this.rebindAll(this.listItemState, this.renderListItemFn);
184
- } else if (this.listItemState) {
185
- this.disposeFactory(this.listItemState);
186
- this.listItemState = null;
187
- this.container.setListFactory(null);
188
- this.renderListItemFn = null;
189
- }
190
- }
191
-
192
- if (hasChanged(oldProps, newProps, "renderHeader")) {
193
- this.headerRenderer = updateHeaderRenderer(
194
- this.headerRenderer,
195
- {
196
- signalStore: this.signalStore,
197
- isEnabled: () => this.store.isSectioned(),
198
- resolveItem: (label) => this.store.getHeaderValueByLabel(label),
199
- setFactory: (factory) => this.container.setHeaderFactory(factory),
200
- },
201
- newProps.renderHeader,
202
- );
203
- }
204
- }
205
-
206
- private createFactory(
207
- getRenderFn: () => RenderItemFn | null,
208
- applyFactory: (factory: Gtk.SignalListItemFactory) => void,
209
- ): FactoryState {
210
- const state: FactoryState = {
211
- factory: new Gtk.SignalListItemFactory(),
212
- fiberRoots: new Map(),
213
- tornDown: new Set(),
214
- boundLabels: new Map(),
215
- };
216
-
217
- this.signalStore.set(this, state.factory, "setup", (listItem: Gtk.ListItem) => {
218
- const box = new Gtk.Box(Gtk.Orientation.HORIZONTAL);
219
- box.setValign(Gtk.Align.CENTER);
220
- listItem.setChild(box);
221
- const fiberRoot = createFiberRoot(box);
222
- state.fiberRoots.set(listItem, fiberRoot);
223
- const element = getRenderFn()?.(null);
224
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
225
- });
226
-
227
- this.signalStore.set(this, state.factory, "bind", (listItem: Gtk.ListItem) => {
228
- const fiberRoot = state.fiberRoots.get(listItem);
229
- if (!fiberRoot) return;
230
- const stringObject = listItem.getItem();
231
- const label = stringObject instanceof Gtk.StringObject ? stringObject.getString() : null;
232
- if (label !== null) state.boundLabels.set(label, listItem);
233
- const element = getRenderFn()?.(label);
234
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
235
- });
236
-
237
- this.signalStore.set(this, state.factory, "unbind", (listItem: Gtk.ListItem) => {
238
- const stringObject = listItem.getItem();
239
- if (stringObject instanceof Gtk.StringObject) {
240
- state.boundLabels.delete(stringObject.getString());
241
- }
242
- });
243
-
244
- this.signalStore.set(this, state.factory, "teardown", (listItem: Gtk.ListItem) => {
245
- const fiberRoot = state.fiberRoots.get(listItem);
246
- if (fiberRoot) {
247
- state.tornDown.add(listItem);
248
- reconciler.getInstance().updateContainer(null, fiberRoot, null, () => {});
249
- queueMicrotask(() => {
250
- state.fiberRoots.delete(listItem);
251
- state.tornDown.delete(listItem);
252
- });
253
- }
254
- });
255
-
256
- applyFactory(state.factory);
257
- return state;
258
- }
259
-
260
- private rebindAll(state: FactoryState, renderFn: RenderItemFn): void {
261
- for (const [label, listItem] of state.boundLabels) {
262
- const fiberRoot = state.fiberRoots.get(listItem);
263
- if (!fiberRoot) continue;
264
- const element = renderFn(label);
265
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
266
- }
267
- }
268
-
269
- private disposeFactory(state: FactoryState | null): void {
270
- if (!state) return;
271
- this.signalStore.set(this, state.factory, "setup", undefined);
272
- this.signalStore.set(this, state.factory, "bind", undefined);
273
- this.signalStore.set(this, state.factory, "unbind", undefined);
274
- this.signalStore.set(this, state.factory, "teardown", undefined);
275
- for (const [listItem, fiberRoot] of state.fiberRoots) {
276
- if (!state.tornDown.has(listItem)) {
277
- reconciler.getInstance().updateContainer(null, fiberRoot, null, () => {});
278
- }
279
- }
280
- state.fiberRoots.clear();
281
- state.tornDown.clear();
282
- state.boundLabels.clear();
283
- }
284
- }
@@ -1,119 +0,0 @@
1
- import type * as Gtk from "@gtkx/ffi/gtk";
2
- import type { GtkGridViewProps } from "../jsx.js";
3
- import type { Node } from "../node.js";
4
- import type { Container } from "../types.js";
5
- import { ContainerSlotNode } from "./container-slot.js";
6
- import { EventControllerNode } from "./event-controller.js";
7
- import { GridItemRenderer } from "./internal/grid-item-renderer.js";
8
- import { filterProps, hasChanged } from "./internal/props.js";
9
- import { ListItemNode } from "./list-item.js";
10
- import { ListSectionNode } from "./list-section.js";
11
- import { ListModel, type ListModelProps } from "./models/list.js";
12
- import { SlotNode } from "./slot.js";
13
- import { WidgetNode } from "./widget.js";
14
-
15
- const RENDERER_PROPS = ["renderItem", "estimatedItemHeight"] as const;
16
- const OWN_PROPS = [...RENDERER_PROPS, "selectionMode", "selected", "onSelectionChanged"] as const;
17
-
18
- type GridViewProps = Pick<GtkGridViewProps, (typeof RENDERER_PROPS)[number]> & ListModelProps;
19
-
20
- type GridViewChild = ListItemNode | ListSectionNode | EventControllerNode | SlotNode | ContainerSlotNode;
21
-
22
- export class GridViewNode extends WidgetNode<Gtk.GridView, GridViewProps, GridViewChild> {
23
- private itemRenderer: GridItemRenderer;
24
- private list: ListModel;
25
-
26
- constructor(typeName: string, props: GridViewProps, container: Gtk.GridView, rootContainer: Container) {
27
- super(typeName, props, container, rootContainer);
28
- this.list = new ListModel(
29
- { owner: this, signalStore: this.signalStore },
30
- {
31
- selectionMode: props.selectionMode,
32
- selected: props.selected,
33
- onSelectionChanged: props.onSelectionChanged,
34
- },
35
- true,
36
- );
37
- this.itemRenderer = new GridItemRenderer(this.signalStore);
38
- this.itemRenderer.setStore(this.list.getFlatStore());
39
- this.list.setOnItemUpdated((id) => this.itemRenderer.rebindItem(id));
40
- this.container.setFactory(this.itemRenderer.getFactory());
41
- }
42
-
43
- public override isValidChild(child: Node): boolean {
44
- if (child instanceof EventControllerNode || child instanceof SlotNode || child instanceof ContainerSlotNode)
45
- return true;
46
- if (child instanceof ListSectionNode) return true;
47
- if (!(child instanceof ListItemNode)) return false;
48
- if (child.getChildNodes().length > 0) {
49
- throw new Error("GtkGridView does not support nested ListItems. Use GtkListView for tree lists.");
50
- }
51
- return true;
52
- }
53
-
54
- public override appendChild(child: GridViewChild): void {
55
- super.appendChild(child);
56
- if (child instanceof ListItemNode || child instanceof ListSectionNode) {
57
- this.list.appendChild(child);
58
- }
59
- }
60
-
61
- public override insertBefore(child: GridViewChild, before: GridViewChild): void {
62
- super.insertBefore(child, before);
63
- if (
64
- (child instanceof ListItemNode || child instanceof ListSectionNode) &&
65
- (before instanceof ListItemNode || before instanceof ListSectionNode)
66
- ) {
67
- this.list.insertBefore(child, before);
68
- }
69
- }
70
-
71
- public override removeChild(child: GridViewChild): void {
72
- if (child instanceof ListItemNode || child instanceof ListSectionNode) {
73
- this.list.removeChild(child);
74
- }
75
- super.removeChild(child);
76
- }
77
-
78
- public override finalizeInitialChildren(props: GridViewProps): boolean {
79
- super.finalizeInitialChildren(props);
80
- return true;
81
- }
82
-
83
- public override commitUpdate(oldProps: GridViewProps | null, newProps: GridViewProps): void {
84
- super.commitUpdate(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
85
- this.applyOwnProps(oldProps, newProps);
86
- }
87
-
88
- public override commitMount(): void {
89
- super.commitMount();
90
- this.list.flushBatch();
91
- this.container.setModel(this.list.getSelectionModel());
92
- }
93
-
94
- public override detachDeletedInstance(): void {
95
- this.itemRenderer.dispose();
96
- super.detachDeletedInstance();
97
- }
98
-
99
- private applyOwnProps(oldProps: GridViewProps | null, newProps: GridViewProps): void {
100
- if (hasChanged(oldProps, newProps, "renderItem")) {
101
- this.itemRenderer.setRenderFn(newProps.renderItem ?? null);
102
- }
103
-
104
- if (hasChanged(oldProps, newProps, "estimatedItemHeight")) {
105
- this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight ?? null);
106
- }
107
-
108
- const previousModel = this.list.getSelectionModel();
109
- this.list.updateProps(
110
- oldProps ? filterProps(oldProps, RENDERER_PROPS) : null,
111
- filterProps(newProps, RENDERER_PROPS),
112
- );
113
- const currentModel = this.list.getSelectionModel();
114
-
115
- if (previousModel !== currentModel) {
116
- this.container.setModel(currentModel);
117
- }
118
- }
119
- }
@@ -1,107 +0,0 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import type { ReactNode } from "react";
3
- import type Reconciler from "react-reconciler";
4
- import { createFiberRoot } from "../../fiber-root.js";
5
- import { reconciler } from "../../reconciler.js";
6
- import type { SignalStore } from "./signal-store.js";
7
-
8
- export abstract class BaseItemRenderer<TStore = unknown> {
9
- protected factory: Gtk.SignalListItemFactory;
10
- protected fiberRoots = new Map<Gtk.ListItem, Reconciler.FiberRoot>();
11
- protected tornDown = new Set<Gtk.ListItem>();
12
- protected estimatedItemHeight: number | null = null;
13
- private store: TStore | null = null;
14
- protected signalStore: SignalStore;
15
-
16
- constructor(signalStore: SignalStore) {
17
- this.signalStore = signalStore;
18
- this.factory = new Gtk.SignalListItemFactory();
19
- this.initializeFactory();
20
- }
21
-
22
- public getFactory(): Gtk.SignalListItemFactory {
23
- return this.factory;
24
- }
25
-
26
- public setEstimatedItemHeight(height: number | null): void {
27
- this.estimatedItemHeight = height;
28
- }
29
-
30
- public setStore(store: TStore | null): void {
31
- this.store = store;
32
- }
33
-
34
- protected getStore(): TStore {
35
- if (!this.store) {
36
- throw new Error(`Expected store to be set on ${this.constructor.name}`);
37
- }
38
- return this.store;
39
- }
40
-
41
- public dispose(): void {
42
- this.signalStore.clear(this);
43
- this.fiberRoots.clear();
44
- this.tornDown.clear();
45
- }
46
-
47
- protected abstract renderItem(listItem: Gtk.ListItem): ReactNode;
48
- protected abstract onSetup(listItem: Gtk.ListItem): Gtk.Widget;
49
- protected abstract onBind(listItem: Gtk.ListItem, fiberRoot: Reconciler.FiberRoot): void;
50
- protected abstract onUnbind(listItem: Gtk.ListItem): void;
51
-
52
- protected onSetupComplete(_listItem: Gtk.ListItem): void {}
53
- protected onTeardown(_listItem: Gtk.ListItem): void {}
54
-
55
- protected createBox(): Gtk.Box {
56
- const box = new Gtk.Box(Gtk.Orientation.HORIZONTAL);
57
-
58
- if (this.estimatedItemHeight !== null) {
59
- box.setSizeRequest(-1, this.estimatedItemHeight);
60
- }
61
-
62
- return box;
63
- }
64
-
65
- protected clearBoxSizeRequest(box: Gtk.Widget): void {
66
- if (box instanceof Gtk.Box) {
67
- box.setSizeRequest(-1, -1);
68
- }
69
- }
70
-
71
- private initializeFactory(): void {
72
- this.signalStore.set(this, this.factory, "setup", (listItem: Gtk.ListItem) => {
73
- const container = this.onSetup(listItem);
74
- const fiberRoot = createFiberRoot(container);
75
- this.fiberRoots.set(listItem, fiberRoot);
76
- const element = this.renderItem(listItem);
77
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
78
- if (this.tornDown.has(listItem)) return;
79
- this.onSetupComplete(listItem);
80
- });
81
- });
82
-
83
- this.signalStore.set(this, this.factory, "bind", (listItem: Gtk.ListItem) => {
84
- const fiberRoot = this.fiberRoots.get(listItem);
85
- if (!fiberRoot) return;
86
- this.onBind(listItem, fiberRoot);
87
- });
88
-
89
- this.signalStore.set(this, this.factory, "unbind", (listItem: Gtk.ListItem) => {
90
- this.onUnbind(listItem);
91
- });
92
-
93
- this.signalStore.set(this, this.factory, "teardown", (listItem: Gtk.ListItem) => {
94
- const fiberRoot = this.fiberRoots.get(listItem);
95
-
96
- if (fiberRoot) {
97
- this.tornDown.add(listItem);
98
- this.onTeardown(listItem);
99
- reconciler.getInstance().updateContainer(null, fiberRoot, null, () => {});
100
- queueMicrotask(() => {
101
- this.fiberRoots.delete(listItem);
102
- this.tornDown.delete(listItem);
103
- });
104
- }
105
- });
106
- }
107
- }
@@ -1,78 +0,0 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import type { ReactNode } from "react";
3
- import type Reconciler from "react-reconciler";
4
- import { reconciler } from "../../reconciler.js";
5
- import { BaseItemRenderer } from "./base-item-renderer.js";
6
- import type { ListStore } from "./list-store.js";
7
-
8
- type GridRenderItemFn<T> = (item: T | null) => ReactNode;
9
-
10
- export class GridItemRenderer extends BaseItemRenderer<ListStore> {
11
- private renderFn: GridRenderItemFn<unknown> | null = () => null;
12
- private boundItems = new Map<string, Gtk.ListItem>();
13
-
14
- public setRenderFn(renderFn: GridRenderItemFn<unknown> | null): void {
15
- this.renderFn = renderFn;
16
- this.rebindAllItems();
17
- }
18
-
19
- public rebindAllItems(): void {
20
- for (const id of this.boundItems.keys()) {
21
- this.rebindItem(id);
22
- }
23
- }
24
-
25
- public rebindItem(id: string): void {
26
- const listItem = this.boundItems.get(id);
27
- if (!listItem) return;
28
-
29
- const fiberRoot = this.fiberRoots.get(listItem);
30
- if (!fiberRoot) return;
31
-
32
- const item = this.getStore().getItem(id);
33
- const element = this.renderFn?.(item);
34
-
35
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
36
- }
37
-
38
- protected override renderItem(_listItem: Gtk.ListItem): ReactNode {
39
- return this.renderFn?.(null);
40
- }
41
-
42
- private getItemFromListItem(listItem: Gtk.ListItem): string | null {
43
- const stringObject = listItem.getItem();
44
- if (!(stringObject instanceof Gtk.StringObject)) return null;
45
- return stringObject.getString();
46
- }
47
-
48
- protected override onSetup(listItem: Gtk.ListItem): Gtk.Widget {
49
- const box = this.createBox();
50
- listItem.setChild(box);
51
- return box;
52
- }
53
-
54
- protected override onBind(listItem: Gtk.ListItem, fiberRoot: Reconciler.FiberRoot): void {
55
- const id = this.getItemFromListItem(listItem);
56
- if (id !== null) {
57
- this.boundItems.set(id, listItem);
58
- }
59
-
60
- const item = id !== null ? this.getStore().getItem(id) : null;
61
- const element = this.renderFn?.(item);
62
-
63
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
64
- if (this.tornDown.has(listItem)) return;
65
- if (this.estimatedItemHeight !== null) return;
66
- const currentFiberRoot = this.fiberRoots.get(listItem);
67
- if (!currentFiberRoot) return;
68
- this.clearBoxSizeRequest(currentFiberRoot.containerInfo);
69
- });
70
- }
71
-
72
- protected override onUnbind(listItem: Gtk.ListItem): void {
73
- const id = this.getItemFromListItem(listItem);
74
- if (id !== null) {
75
- this.boundItems.delete(id);
76
- }
77
- }
78
- }
@@ -1,105 +0,0 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import type { ReactNode } from "react";
3
- import type Reconciler from "react-reconciler";
4
- import { createFiberRoot } from "../../fiber-root.js";
5
- import { reconciler } from "../../reconciler.js";
6
- import type { SignalStore } from "./signal-store.js";
7
-
8
- type RenderHeaderFn = (item: unknown) => ReactNode;
9
-
10
- type ItemResolver = (id: string) => unknown;
11
-
12
- export class HeaderItemRenderer {
13
- private factory: Gtk.SignalListItemFactory;
14
- private fiberRoots = new Map<object, Reconciler.FiberRoot>();
15
- private tornDown = new Set<object>();
16
- private renderFn: RenderHeaderFn | null = () => null;
17
- private boundHeaders = new Map<string, object>();
18
- private signalStore: SignalStore;
19
- private resolveItem: ItemResolver | null = null;
20
-
21
- constructor(signalStore: SignalStore) {
22
- this.signalStore = signalStore;
23
- this.factory = new Gtk.SignalListItemFactory();
24
- this.initializeFactory();
25
- }
26
-
27
- public getFactory(): Gtk.SignalListItemFactory {
28
- return this.factory;
29
- }
30
-
31
- public setResolveItem(resolver: ItemResolver | null): void {
32
- this.resolveItem = resolver;
33
- }
34
-
35
- public setRenderFn(renderFn: RenderHeaderFn | null): void {
36
- this.renderFn = renderFn;
37
- this.rebindAllHeaders();
38
- }
39
-
40
- public dispose(): void {
41
- this.signalStore.clear(this);
42
- this.fiberRoots.clear();
43
- this.tornDown.clear();
44
- this.boundHeaders.clear();
45
- }
46
-
47
- private rebindAllHeaders(): void {
48
- for (const [id, header] of this.boundHeaders) {
49
- const fiberRoot = this.fiberRoots.get(header);
50
- if (!fiberRoot) continue;
51
- const item = this.resolveItem?.(id) ?? null;
52
- const element = this.renderFn?.(item);
53
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
54
- }
55
- }
56
-
57
- private initializeFactory(): void {
58
- this.signalStore.set(this, this.factory, "setup", (listHeader: Gtk.ListHeader) => {
59
- const box = new Gtk.Box(Gtk.Orientation.HORIZONTAL);
60
- listHeader.setChild(box);
61
- const fiberRoot = createFiberRoot(box);
62
- this.fiberRoots.set(listHeader, fiberRoot);
63
- const element = this.renderFn?.(null);
64
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
65
- });
66
-
67
- this.signalStore.set(this, this.factory, "bind", (listHeader: Gtk.ListHeader) => {
68
- const fiberRoot = this.fiberRoots.get(listHeader);
69
- if (!fiberRoot) return;
70
-
71
- const stringObject = listHeader.getItem();
72
- let id: string | null = null;
73
- if (stringObject instanceof Gtk.StringObject) {
74
- id = stringObject.getString();
75
- }
76
-
77
- if (id !== null) {
78
- this.boundHeaders.set(id, listHeader);
79
- }
80
-
81
- const item = id !== null && this.resolveItem ? this.resolveItem(id) : null;
82
- const element = this.renderFn?.(item);
83
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
84
- });
85
-
86
- this.signalStore.set(this, this.factory, "unbind", (listHeader: Gtk.ListHeader) => {
87
- const stringObject = listHeader.getItem();
88
- if (stringObject instanceof Gtk.StringObject) {
89
- this.boundHeaders.delete(stringObject.getString());
90
- }
91
- });
92
-
93
- this.signalStore.set(this, this.factory, "teardown", (listHeader: Gtk.ListHeader) => {
94
- const fiberRoot = this.fiberRoots.get(listHeader);
95
- if (fiberRoot) {
96
- this.tornDown.add(listHeader);
97
- reconciler.getInstance().updateContainer(null, fiberRoot, null, () => {});
98
- queueMicrotask(() => {
99
- this.fiberRoots.delete(listHeader);
100
- this.tornDown.delete(listHeader);
101
- });
102
- }
103
- });
104
- }
105
- }