@gtkx/react 0.6.1 → 0.7.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 (84) hide show
  1. package/dist/batch.d.ts +4 -1
  2. package/dist/batch.js +19 -10
  3. package/dist/codegen/jsx-generator.d.ts +4 -4
  4. package/dist/codegen/jsx-generator.js +24 -27
  5. package/dist/container-interfaces.d.ts +19 -6
  6. package/dist/container-interfaces.js +26 -6
  7. package/dist/errors.d.ts +8 -0
  8. package/dist/errors.js +38 -0
  9. package/dist/factory.js +9 -3
  10. package/dist/generated/jsx.d.ts +38 -26
  11. package/dist/generated/jsx.js +12 -2
  12. package/dist/index.js +3 -1
  13. package/dist/node.d.ts +5 -0
  14. package/dist/node.js +62 -6
  15. package/dist/nodes/action-bar.d.ts +2 -6
  16. package/dist/nodes/action-bar.js +3 -12
  17. package/dist/nodes/column-view.d.ts +19 -44
  18. package/dist/nodes/column-view.js +70 -243
  19. package/dist/nodes/combo-row.d.ts +5 -0
  20. package/dist/nodes/combo-row.js +6 -0
  21. package/dist/nodes/drop-down.d.ts +9 -0
  22. package/dist/nodes/drop-down.js +12 -0
  23. package/dist/nodes/flow-box.d.ts +4 -6
  24. package/dist/nodes/flow-box.js +8 -16
  25. package/dist/nodes/grid.d.ts +15 -15
  26. package/dist/nodes/grid.js +21 -64
  27. package/dist/nodes/header-bar.d.ts +34 -11
  28. package/dist/nodes/header-bar.js +52 -24
  29. package/dist/nodes/indexed-child-container.d.ts +16 -0
  30. package/dist/nodes/indexed-child-container.js +22 -0
  31. package/dist/nodes/list-box.d.ts +3 -6
  32. package/dist/nodes/list-box.js +6 -14
  33. package/dist/nodes/list-item-factory.d.ts +19 -0
  34. package/dist/nodes/list-item-factory.js +58 -0
  35. package/dist/nodes/list-view.d.ts +24 -0
  36. package/dist/nodes/list-view.js +46 -0
  37. package/dist/nodes/menu.d.ts +25 -19
  38. package/dist/nodes/menu.js +30 -59
  39. package/dist/nodes/notebook.d.ts +13 -14
  40. package/dist/nodes/notebook.js +18 -56
  41. package/dist/nodes/paged-stack.d.ts +39 -0
  42. package/dist/nodes/paged-stack.js +54 -0
  43. package/dist/nodes/selectable-list.d.ts +41 -0
  44. package/dist/nodes/selectable-list.js +228 -0
  45. package/dist/nodes/stack-page-props.d.ts +11 -0
  46. package/dist/nodes/stack-page-props.js +23 -0
  47. package/dist/nodes/stack.d.ts +14 -28
  48. package/dist/nodes/stack.js +30 -142
  49. package/dist/nodes/string-list-container.d.ts +41 -0
  50. package/dist/nodes/string-list-container.js +90 -0
  51. package/dist/nodes/string-list-item.d.ts +15 -0
  52. package/dist/nodes/string-list-item.js +48 -0
  53. package/dist/nodes/string-list-store.d.ts +13 -0
  54. package/dist/nodes/string-list-store.js +44 -0
  55. package/dist/nodes/text-view.d.ts +1 -1
  56. package/dist/nodes/text-view.js +1 -5
  57. package/dist/nodes/toggle-button.d.ts +1 -1
  58. package/dist/nodes/toggle-button.js +1 -3
  59. package/dist/nodes/view-stack.d.ts +9 -0
  60. package/dist/nodes/view-stack.js +28 -0
  61. package/dist/nodes/virtual-item.d.ts +20 -0
  62. package/dist/nodes/virtual-item.js +57 -0
  63. package/dist/nodes/virtual-slot.d.ts +25 -0
  64. package/dist/nodes/virtual-slot.js +71 -0
  65. package/dist/nodes/widget.d.ts +0 -3
  66. package/dist/nodes/widget.js +0 -28
  67. package/dist/nodes/window.d.ts +1 -1
  68. package/dist/nodes/window.js +9 -15
  69. package/dist/predicates.d.ts +8 -0
  70. package/dist/predicates.js +8 -0
  71. package/dist/props.d.ts +7 -5
  72. package/dist/props.js +11 -9
  73. package/dist/reconciler/host-config.d.ts +19 -0
  74. package/dist/reconciler/host-config.js +89 -0
  75. package/dist/reconciler.d.ts +2 -26
  76. package/dist/reconciler.js +15 -106
  77. package/dist/render.d.ts +1 -2
  78. package/dist/render.js +16 -4
  79. package/dist/types.d.ts +19 -16
  80. package/package.json +4 -4
  81. package/dist/nodes/dropdown.d.ts +0 -39
  82. package/dist/nodes/dropdown.js +0 -103
  83. package/dist/nodes/list.d.ts +0 -43
  84. package/dist/nodes/list.js +0 -153
@@ -2,6 +2,7 @@ import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import { type ChildContainer, type PageContainer } from "../container-interfaces.js";
3
3
  import type { Props } from "../factory.js";
4
4
  import { Node } from "../node.js";
5
+ import { VirtualSlotNode } from "./virtual-slot.js";
5
6
  export declare class NotebookNode extends Node<Gtk.Notebook> implements PageContainer, ChildContainer {
6
7
  static matches(type: string): boolean;
7
8
  addPage(child: Gtk.Widget, label: string): void;
@@ -12,20 +13,18 @@ export declare class NotebookNode extends Node<Gtk.Notebook> implements PageCont
12
13
  insertChildBefore(child: Gtk.Widget, before: Gtk.Widget): void;
13
14
  detachChild(child: Gtk.Widget): void;
14
15
  }
15
- export declare class NotebookPageNode extends Node {
16
+ type NotebookPageProps = {
17
+ label: string;
18
+ };
19
+ export declare class NotebookPageNode extends VirtualSlotNode<PageContainer, NotebookPageProps> {
20
+ static consumedPropNames: string[];
16
21
  static matches(type: string): boolean;
17
- protected isVirtual(): boolean;
18
- private label;
19
- private childWidget;
20
- private parentContainer;
21
- initialize(props: Props): void;
22
- getLabel(): string;
23
- setChildWidget(widget: Gtk.Widget): void;
24
- getChildWidget(): Gtk.Widget | null;
25
- appendChild(child: Node): void;
26
- attachToParent(parent: Node): void;
27
- attachToParentBefore(parent: Node, before: Node): void;
28
- detachFromParent(parent: Node): void;
29
- protected consumedProps(): Set<string>;
22
+ protected isValidContainer(parent: Node): parent is Node & PageContainer;
23
+ protected extractSlotProps(props: Props): NotebookPageProps;
24
+ protected addToContainer(container: PageContainer, child: Gtk.Widget, props: NotebookPageProps): void;
25
+ protected insertBeforeInContainer(container: PageContainer, child: Gtk.Widget, props: NotebookPageProps, before: Gtk.Widget): void;
26
+ protected removeFromContainer(container: PageContainer, child: Gtk.Widget): void;
27
+ protected updateInContainer(container: PageContainer, child: Gtk.Widget, props: NotebookPageProps): void;
30
28
  updateProps(oldProps: Props, newProps: Props): void;
31
29
  }
30
+ export {};
@@ -1,7 +1,7 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import { isPageContainer } from "../container-interfaces.js";
3
3
  import { Node } from "../node.js";
4
- import { getStringProp } from "../props.js";
4
+ import { VirtualSlotNode } from "./virtual-slot.js";
5
5
  export class NotebookNode extends Node {
6
6
  static matches(type) {
7
7
  return type === "Notebook" || type === "Notebook.Root";
@@ -49,71 +49,33 @@ export class NotebookNode extends Node {
49
49
  this.removePage(child);
50
50
  }
51
51
  }
52
- export class NotebookPageNode extends Node {
52
+ export class NotebookPageNode extends VirtualSlotNode {
53
+ static consumedPropNames = ["label"];
53
54
  static matches(type) {
54
55
  return type === "Notebook.Page";
55
56
  }
56
- isVirtual() {
57
- return true;
57
+ isValidContainer(parent) {
58
+ return isPageContainer(parent);
58
59
  }
59
- label = "";
60
- childWidget = null;
61
- parentContainer = null;
62
- initialize(props) {
63
- this.label = getStringProp(props, "label", "");
64
- super.initialize(props);
60
+ extractSlotProps(props) {
61
+ return {
62
+ label: props.label ?? "",
63
+ };
65
64
  }
66
- getLabel() {
67
- return this.label;
65
+ addToContainer(container, child, props) {
66
+ container.addPage(child, props.label);
68
67
  }
69
- setChildWidget(widget) {
70
- this.childWidget = widget;
68
+ insertBeforeInContainer(container, child, props, before) {
69
+ container.insertPageBefore(child, props.label, before);
71
70
  }
72
- getChildWidget() {
73
- return this.childWidget;
71
+ removeFromContainer(container, child) {
72
+ container.removePage(child);
74
73
  }
75
- appendChild(child) {
76
- const childWidget = child.getWidget();
77
- if (childWidget) {
78
- this.childWidget = childWidget;
79
- }
80
- }
81
- attachToParent(parent) {
82
- if (isPageContainer(parent) && this.childWidget) {
83
- this.parentContainer = parent;
84
- parent.addPage(this.childWidget, this.label);
85
- }
86
- }
87
- attachToParentBefore(parent, before) {
88
- if (isPageContainer(parent) && this.childWidget) {
89
- this.parentContainer = parent;
90
- const beforePage = before instanceof NotebookPageNode ? before.getChildWidget() : before.getWidget();
91
- if (beforePage) {
92
- parent.insertPageBefore(this.childWidget, this.label, beforePage);
93
- }
94
- else {
95
- parent.addPage(this.childWidget, this.label);
96
- }
97
- }
98
- }
99
- detachFromParent(parent) {
100
- if (isPageContainer(parent) && this.childWidget) {
101
- parent.removePage(this.childWidget);
102
- this.parentContainer = null;
103
- }
104
- }
105
- consumedProps() {
106
- const consumed = super.consumedProps();
107
- consumed.add("label");
108
- return consumed;
74
+ updateInContainer(container, child, props) {
75
+ container.updatePageLabel(child, props.label);
109
76
  }
110
77
  updateProps(oldProps, newProps) {
111
- if (oldProps.label !== newProps.label) {
112
- this.label = getStringProp(newProps, "label", "");
113
- if (this.parentContainer && this.childWidget) {
114
- this.parentContainer.updatePageLabel(this.childWidget, this.label);
115
- }
116
- }
78
+ this.updateSlotPropsIfChanged(oldProps, newProps, ["label"]);
117
79
  super.updateProps(oldProps, newProps);
118
80
  }
119
81
  }
@@ -0,0 +1,39 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { ChildContainer, StackPageContainer, StackPageProps } from "../container-interfaces.js";
3
+ import type { Props } from "../factory.js";
4
+ import { Node } from "../node.js";
5
+ import { type StackPageLike } from "./stack-page-props.js";
6
+ type StackWidget = Gtk.Widget & {
7
+ getChildByName(name: string): Gtk.Widget | null;
8
+ setVisibleChild(child: Gtk.Widget): void;
9
+ remove(child: Gtk.Widget): void;
10
+ getPage(child: Gtk.Widget): StackPageLike;
11
+ };
12
+ /**
13
+ * Abstract node for paged stack widgets (Gtk.Stack, Adw.ViewStack).
14
+ * Handles visible child deferral and common page operations.
15
+ */
16
+ export declare abstract class PagedStackNode<T extends StackWidget> extends Node<T> implements StackPageContainer, ChildContainer {
17
+ static consumedPropNames: string[];
18
+ private pendingVisibleChildName;
19
+ /**
20
+ * Add a page to the stack widget. Must be implemented by subclasses
21
+ * due to API differences between Gtk.Stack and Adw.ViewStack.
22
+ */
23
+ abstract addStackPage(child: Gtk.Widget, props: StackPageProps): void;
24
+ /**
25
+ * Add a child directly to the stack widget (without page props).
26
+ * Must be implemented by subclasses due to API differences.
27
+ */
28
+ protected abstract addChildToWidget(child: Gtk.Widget): void;
29
+ protected applyPendingVisibleChild(): void;
30
+ insertStackPageBefore(child: Gtk.Widget, props: StackPageProps, _beforeChild: Gtk.Widget): void;
31
+ removeStackPage(child: Gtk.Widget): void;
32
+ updateStackPageProps(child: Gtk.Widget, props: StackPageProps): void;
33
+ attachChild(child: Gtk.Widget): void;
34
+ insertChildBefore(child: Gtk.Widget, _before: Gtk.Widget): void;
35
+ detachChild(child: Gtk.Widget): void;
36
+ private setVisibleChildOrDefer;
37
+ updateProps(oldProps: Props, newProps: Props): void;
38
+ }
39
+ export {};
@@ -0,0 +1,54 @@
1
+ import { Node } from "../node.js";
2
+ import { applyStackPageProps } from "./stack-page-props.js";
3
+ /**
4
+ * Abstract node for paged stack widgets (Gtk.Stack, Adw.ViewStack).
5
+ * Handles visible child deferral and common page operations.
6
+ */
7
+ export class PagedStackNode extends Node {
8
+ static consumedPropNames = ["visibleChildName"];
9
+ pendingVisibleChildName = null;
10
+ applyPendingVisibleChild() {
11
+ if (this.pendingVisibleChildName !== null) {
12
+ const child = this.widget.getChildByName(this.pendingVisibleChildName);
13
+ if (child) {
14
+ this.widget.setVisibleChild(child);
15
+ this.pendingVisibleChildName = null;
16
+ }
17
+ }
18
+ }
19
+ insertStackPageBefore(child, props, _beforeChild) {
20
+ this.addStackPage(child, props);
21
+ }
22
+ removeStackPage(child) {
23
+ this.widget.remove(child);
24
+ }
25
+ updateStackPageProps(child, props) {
26
+ const page = this.widget.getPage(child);
27
+ applyStackPageProps(page, props);
28
+ }
29
+ attachChild(child) {
30
+ this.addChildToWidget(child);
31
+ }
32
+ insertChildBefore(child, _before) {
33
+ this.addChildToWidget(child);
34
+ }
35
+ detachChild(child) {
36
+ this.widget.remove(child);
37
+ }
38
+ setVisibleChildOrDefer(name) {
39
+ const child = this.widget.getChildByName(name);
40
+ if (child) {
41
+ this.widget.setVisibleChild(child);
42
+ this.pendingVisibleChildName = null;
43
+ }
44
+ else {
45
+ this.pendingVisibleChildName = name;
46
+ }
47
+ }
48
+ updateProps(oldProps, newProps) {
49
+ if (newProps.visibleChildName !== undefined) {
50
+ this.setVisibleChildOrDefer(newProps.visibleChildName);
51
+ }
52
+ super.updateProps(oldProps, newProps);
53
+ }
54
+ }
@@ -0,0 +1,41 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { ItemContainer } from "../container-interfaces.js";
3
+ import type { Props } from "../factory.js";
4
+ import { Node } from "../node.js";
5
+ export type SelectableListState = {
6
+ itemsById: Map<string, unknown>;
7
+ itemOrder: string[];
8
+ committedOrder: string[];
9
+ stringList: Gtk.StringList;
10
+ selectionModel: Gtk.SingleSelection | Gtk.MultiSelection;
11
+ selectionMode: Gtk.SelectionMode;
12
+ selected: string[];
13
+ onSelectionChanged?: (ids: string[]) => void;
14
+ selectionHandlerId: number | null;
15
+ needsSync: boolean;
16
+ needsInitialSelection: boolean;
17
+ };
18
+ type SelectableListWidget = Gtk.Widget & {
19
+ setModel(model: Gtk.SelectionModel): void;
20
+ };
21
+ export declare abstract class SelectableListNode<T extends SelectableListWidget, S extends SelectableListState> extends Node<T, S> implements ItemContainer<unknown> {
22
+ protected initializeSelectionState(props: Props): SelectableListState;
23
+ protected applySelectionModel(): void;
24
+ protected cleanupSelection(): void;
25
+ protected updateSelectionProps(oldProps: Props, newProps: Props): void;
26
+ private recreateSelectionModel;
27
+ getItems(): unknown[];
28
+ getItemById(id: string): unknown;
29
+ addItem(id: string, data: unknown): void;
30
+ insertItemBefore(id: string, data: unknown, beforeId: string): void;
31
+ removeItem(id: string): void;
32
+ updateItem(id: string, data: unknown): void;
33
+ protected scheduleSync(): void;
34
+ protected syncModel: () => void;
35
+ protected applyInitialSelection(): void;
36
+ protected applySelection(ids: string[]): void;
37
+ protected getSelectedIds(): string[];
38
+ protected connectSelectionHandler(): void;
39
+ protected disconnectSelectionHandler(): void;
40
+ }
41
+ export {};
@@ -0,0 +1,228 @@
1
+ import { getInterface } from "@gtkx/ffi";
2
+ import * as Gio from "@gtkx/ffi/gio";
3
+ import * as GObject from "@gtkx/ffi/gobject";
4
+ import * as Gtk from "@gtkx/ffi/gtk";
5
+ import { scheduleFlush } from "../batch.js";
6
+ import { Node } from "../node.js";
7
+ import { getCallbackChange } from "../props.js";
8
+ export class SelectableListNode extends Node {
9
+ initializeSelectionState(props) {
10
+ const selectionMode = props.selectionMode ?? Gtk.SelectionMode.SINGLE;
11
+ const stringList = new Gtk.StringList([]);
12
+ const listModel = getInterface(stringList.id, Gio.ListModel);
13
+ const selectionModel = selectionMode === Gtk.SelectionMode.MULTIPLE
14
+ ? new Gtk.MultiSelection(listModel)
15
+ : new Gtk.SingleSelection(listModel);
16
+ if (selectionModel instanceof Gtk.SingleSelection) {
17
+ selectionModel.setAutoselect(false);
18
+ selectionModel.setCanUnselect(true);
19
+ }
20
+ return {
21
+ itemsById: new Map(),
22
+ itemOrder: [],
23
+ committedOrder: [],
24
+ stringList,
25
+ selectionModel,
26
+ selectionMode,
27
+ selected: props.selected ?? [],
28
+ onSelectionChanged: props.onSelectionChanged,
29
+ selectionHandlerId: null,
30
+ needsSync: false,
31
+ needsInitialSelection: true,
32
+ };
33
+ }
34
+ applySelectionModel() {
35
+ this.widget.setModel(this.state.selectionModel);
36
+ }
37
+ cleanupSelection() {
38
+ this.disconnectSelectionHandler();
39
+ }
40
+ updateSelectionProps(oldProps, newProps) {
41
+ const oldSelectionMode = oldProps.selectionMode;
42
+ const newSelectionMode = newProps.selectionMode;
43
+ const effectiveOldMode = oldSelectionMode ?? Gtk.SelectionMode.SINGLE;
44
+ const effectiveNewMode = newSelectionMode ?? Gtk.SelectionMode.SINGLE;
45
+ if (effectiveOldMode !== effectiveNewMode) {
46
+ this.recreateSelectionModel(effectiveNewMode);
47
+ }
48
+ const oldCallback = oldProps.onSelectionChanged;
49
+ const newCallback = newProps.onSelectionChanged;
50
+ const change = getCallbackChange(oldCallback, newCallback);
51
+ if (change.action !== "none") {
52
+ this.state.onSelectionChanged = change.callback;
53
+ if (change.action === "disconnect") {
54
+ this.disconnectSelectionHandler();
55
+ }
56
+ else if (change.action === "connect") {
57
+ this.connectSelectionHandler();
58
+ }
59
+ }
60
+ const oldSelected = oldProps.selected;
61
+ const newSelected = newProps.selected;
62
+ if (oldSelected !== newSelected && newSelected !== undefined) {
63
+ this.state.selected = newSelected;
64
+ this.applySelection(newSelected);
65
+ }
66
+ }
67
+ recreateSelectionModel(newSelectionMode) {
68
+ const currentSelection = this.getSelectedIds();
69
+ const hadHandler = this.state.selectionHandlerId !== null;
70
+ this.disconnectSelectionHandler();
71
+ const listModel = getInterface(this.state.stringList.id, Gio.ListModel);
72
+ const newSelectionModel = newSelectionMode === Gtk.SelectionMode.MULTIPLE
73
+ ? new Gtk.MultiSelection(listModel)
74
+ : new Gtk.SingleSelection(listModel);
75
+ if (newSelectionModel instanceof Gtk.SingleSelection) {
76
+ newSelectionModel.setAutoselect(false);
77
+ newSelectionModel.setCanUnselect(true);
78
+ }
79
+ this.state.selectionModel = newSelectionModel;
80
+ this.state.selectionMode = newSelectionMode;
81
+ this.applySelectionModel();
82
+ this.applySelection(currentSelection);
83
+ if (hadHandler && this.state.onSelectionChanged) {
84
+ this.connectSelectionHandler();
85
+ }
86
+ }
87
+ getItems() {
88
+ return this.state.itemOrder.map((id) => this.state.itemsById.get(id));
89
+ }
90
+ getItemById(id) {
91
+ return this.state.itemsById.get(id);
92
+ }
93
+ addItem(id, data) {
94
+ this.state.itemsById.set(id, data);
95
+ const existingIndex = this.state.itemOrder.indexOf(id);
96
+ if (existingIndex !== -1) {
97
+ this.state.itemOrder.splice(existingIndex, 1);
98
+ }
99
+ this.state.itemOrder.push(id);
100
+ this.scheduleSync();
101
+ }
102
+ insertItemBefore(id, data, beforeId) {
103
+ this.state.itemsById.set(id, data);
104
+ const existingIndex = this.state.itemOrder.indexOf(id);
105
+ if (existingIndex !== -1) {
106
+ this.state.itemOrder.splice(existingIndex, 1);
107
+ }
108
+ const beforeIndex = this.state.itemOrder.indexOf(beforeId);
109
+ if (beforeIndex === -1) {
110
+ this.state.itemOrder.push(id);
111
+ }
112
+ else {
113
+ this.state.itemOrder.splice(beforeIndex, 0, id);
114
+ }
115
+ this.scheduleSync();
116
+ }
117
+ removeItem(id) {
118
+ const index = this.state.itemOrder.indexOf(id);
119
+ if (index !== -1) {
120
+ this.state.itemOrder.splice(index, 1);
121
+ this.state.itemsById.delete(id);
122
+ this.scheduleSync();
123
+ }
124
+ }
125
+ updateItem(id, data) {
126
+ if (this.state.itemsById.has(id)) {
127
+ this.state.itemsById.set(id, data);
128
+ }
129
+ }
130
+ scheduleSync() {
131
+ if (!this.state.needsSync) {
132
+ this.state.needsSync = true;
133
+ scheduleFlush(this.syncModel);
134
+ }
135
+ }
136
+ syncModel = () => {
137
+ if (!this.state.needsSync)
138
+ return;
139
+ this.state.needsSync = false;
140
+ const oldOrder = this.state.committedOrder;
141
+ const newOrder = this.state.itemOrder;
142
+ let firstDiff = 0;
143
+ const minLen = Math.min(oldOrder.length, newOrder.length);
144
+ while (firstDiff < minLen && oldOrder[firstDiff] === newOrder[firstDiff]) {
145
+ firstDiff++;
146
+ }
147
+ let oldEndOffset = 0;
148
+ let newEndOffset = 0;
149
+ while (oldEndOffset < oldOrder.length - firstDiff &&
150
+ newEndOffset < newOrder.length - firstDiff &&
151
+ oldOrder[oldOrder.length - 1 - oldEndOffset] === newOrder[newOrder.length - 1 - newEndOffset]) {
152
+ oldEndOffset++;
153
+ newEndOffset++;
154
+ }
155
+ const removeCount = oldOrder.length - firstDiff - oldEndOffset;
156
+ const addItems = newOrder.slice(firstDiff, newOrder.length - newEndOffset);
157
+ if (removeCount > 0 || addItems.length > 0) {
158
+ this.state.stringList.splice(firstDiff, removeCount, addItems);
159
+ }
160
+ this.state.committedOrder = [...newOrder];
161
+ if (this.state.needsInitialSelection && this.state.itemOrder.length > 0) {
162
+ this.state.needsInitialSelection = false;
163
+ queueMicrotask(() => {
164
+ if (this.hasParent()) {
165
+ this.applyInitialSelection();
166
+ }
167
+ });
168
+ }
169
+ };
170
+ applyInitialSelection() {
171
+ this.applySelection(this.state.selected);
172
+ if (this.state.onSelectionChanged) {
173
+ this.connectSelectionHandler();
174
+ this.state.onSelectionChanged(this.getSelectedIds());
175
+ }
176
+ }
177
+ applySelection(ids) {
178
+ if (this.state.selectionMode === Gtk.SelectionMode.MULTIPLE) {
179
+ const multiSelection = this.state.selectionModel;
180
+ multiSelection.unselectAll();
181
+ for (const id of ids) {
182
+ const index = this.state.itemOrder.indexOf(id);
183
+ if (index !== -1) {
184
+ multiSelection.selectItem(index, false);
185
+ }
186
+ }
187
+ }
188
+ else {
189
+ const singleSelection = this.state.selectionModel;
190
+ const firstId = ids[0];
191
+ if (firstId !== undefined) {
192
+ const index = this.state.itemOrder.indexOf(firstId);
193
+ if (index !== -1) {
194
+ singleSelection.setSelected(index);
195
+ }
196
+ }
197
+ else {
198
+ singleSelection.setSelected(Gtk.INVALID_LIST_POSITION);
199
+ }
200
+ }
201
+ }
202
+ getSelectedIds() {
203
+ const selection = this.state.selectionModel.getSelection();
204
+ const count = Number(selection.getSize());
205
+ const selectedIds = [];
206
+ for (let i = 0; i < count; i++) {
207
+ const position = selection.getNth(i);
208
+ const id = this.state.itemOrder[position];
209
+ if (id !== undefined) {
210
+ selectedIds.push(id);
211
+ }
212
+ }
213
+ return selectedIds;
214
+ }
215
+ connectSelectionHandler() {
216
+ if (this.state.selectionHandlerId !== null)
217
+ return;
218
+ this.state.selectionHandlerId = this.state.selectionModel.connect("selection-changed", () => {
219
+ this.state.onSelectionChanged?.(this.getSelectedIds());
220
+ });
221
+ }
222
+ disconnectSelectionHandler() {
223
+ if (this.state.selectionHandlerId !== null) {
224
+ GObject.signalHandlerDisconnect(this.state.selectionModel, this.state.selectionHandlerId);
225
+ this.state.selectionHandlerId = null;
226
+ }
227
+ }
228
+ }
@@ -0,0 +1,11 @@
1
+ import type { StackPageProps } from "../container-interfaces.js";
2
+ export type StackPageLike = {
3
+ setName(name: string): void;
4
+ setTitle(title: string): void;
5
+ setIconName(iconName: string): void;
6
+ setNeedsAttention(needsAttention: boolean): void;
7
+ setVisible(visible: boolean): void;
8
+ setUseUnderline(useUnderline: boolean): void;
9
+ setBadgeNumber?(badgeNumber: number): void;
10
+ };
11
+ export declare function applyStackPageProps(page: StackPageLike, props: StackPageProps): void;
@@ -0,0 +1,23 @@
1
+ export function applyStackPageProps(page, props) {
2
+ if (props.name !== undefined) {
3
+ page.setName(props.name);
4
+ }
5
+ if (props.title !== undefined) {
6
+ page.setTitle(props.title);
7
+ }
8
+ if (props.iconName !== undefined) {
9
+ page.setIconName(props.iconName);
10
+ }
11
+ if (props.needsAttention !== undefined) {
12
+ page.setNeedsAttention(props.needsAttention);
13
+ }
14
+ if (props.visible !== undefined) {
15
+ page.setVisible(props.visible);
16
+ }
17
+ if (props.useUnderline !== undefined) {
18
+ page.setUseUnderline(props.useUnderline);
19
+ }
20
+ if (props.badgeNumber !== undefined && page.setBadgeNumber) {
21
+ page.setBadgeNumber(props.badgeNumber);
22
+ }
23
+ }
@@ -1,36 +1,22 @@
1
1
  import type * as Gtk from "@gtkx/ffi/gtk";
2
- import { type ChildContainer, type StackPageContainer, type StackPageProps } from "../container-interfaces.js";
2
+ import { type StackPageContainer, type StackPageProps } from "../container-interfaces.js";
3
3
  import type { Props } from "../factory.js";
4
- import { Node } from "../node.js";
5
- export declare class StackNode extends Node<Gtk.Stack> implements StackPageContainer, ChildContainer {
4
+ import type { Node } from "../node.js";
5
+ import { PagedStackNode } from "./paged-stack.js";
6
+ import { VirtualSlotNode } from "./virtual-slot.js";
7
+ export declare class StackNode extends PagedStackNode<Gtk.Stack> {
6
8
  static matches(type: string): boolean;
7
- private pendingVisibleChildName;
8
9
  addStackPage(child: Gtk.Widget, props: StackPageProps): void;
9
- private applyPendingVisibleChild;
10
- insertStackPageBefore(child: Gtk.Widget, props: StackPageProps, _beforeChild: Gtk.Widget): void;
11
- removeStackPage(child: Gtk.Widget): void;
12
- updateStackPageProps(child: Gtk.Widget, props: StackPageProps): void;
13
- private applyStackPageProps;
14
- attachChild(child: Gtk.Widget): void;
15
- insertChildBefore(child: Gtk.Widget, _before: Gtk.Widget): void;
16
- detachChild(child: Gtk.Widget): void;
17
- protected consumedProps(): Set<string>;
18
- private setVisibleChildOrDefer;
19
- updateProps(oldProps: Props, newProps: Props): void;
10
+ protected addChildToWidget(child: Gtk.Widget): void;
20
11
  }
21
- export declare class StackPageNode extends Node {
12
+ export declare class StackPageNode extends VirtualSlotNode<StackPageContainer, StackPageProps> {
13
+ static consumedPropNames: string[];
22
14
  static matches(type: string): boolean;
23
- protected isVirtual(): boolean;
24
- private pageProps;
25
- private childWidget;
26
- private parentContainer;
27
- initialize(props: Props): void;
28
- private extractPageProps;
29
- getChildWidget(): Gtk.Widget | null;
30
- appendChild(child: Node): void;
31
- attachToParent(parent: Node): void;
32
- attachToParentBefore(parent: Node, before: Node): void;
33
- detachFromParent(parent: Node): void;
34
- protected consumedProps(): Set<string>;
15
+ protected isValidContainer(parent: Node): parent is Node & StackPageContainer;
16
+ protected extractSlotProps(props: Props): StackPageProps;
17
+ protected addToContainer(container: StackPageContainer, child: Gtk.Widget, props: StackPageProps): void;
18
+ protected insertBeforeInContainer(container: StackPageContainer, child: Gtk.Widget, props: StackPageProps, before: Gtk.Widget): void;
19
+ protected removeFromContainer(container: StackPageContainer, child: Gtk.Widget): void;
20
+ protected updateInContainer(container: StackPageContainer, child: Gtk.Widget, props: StackPageProps): void;
35
21
  updateProps(oldProps: Props, newProps: Props): void;
36
22
  }