@gtkx/react 0.18.9 → 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.
- package/dist/generated/internal.d.ts +6 -0
- package/dist/generated/internal.d.ts.map +1 -1
- package/dist/generated/internal.js +331 -44
- package/dist/generated/internal.js.map +1 -1
- package/dist/generated/jsx.d.ts +178 -2
- package/dist/generated/jsx.d.ts.map +1 -1
- package/dist/generated/jsx.js.map +1 -1
- package/dist/host-config.d.ts.map +1 -1
- package/dist/host-config.js +46 -10
- package/dist/host-config.js.map +1 -1
- package/dist/jsx.d.ts +133 -13
- package/dist/jsx.d.ts.map +1 -1
- package/dist/jsx.js +41 -2
- package/dist/jsx.js.map +1 -1
- package/dist/metadata.d.ts +1 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +3 -1
- package/dist/metadata.js.map +1 -1
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +22 -6
- package/dist/node.js.map +1 -1
- package/dist/nodes/column-view-column.d.ts +4 -1
- package/dist/nodes/column-view-column.d.ts.map +1 -1
- package/dist/nodes/column-view-column.js +29 -8
- package/dist/nodes/column-view-column.js.map +1 -1
- package/dist/nodes/column-view.d.ts +4 -3
- package/dist/nodes/column-view.d.ts.map +1 -1
- package/dist/nodes/column-view.js +44 -14
- package/dist/nodes/column-view.js.map +1 -1
- package/dist/nodes/drop-down.d.ts +12 -2
- package/dist/nodes/drop-down.d.ts.map +1 -1
- package/dist/nodes/drop-down.js +151 -5
- package/dist/nodes/drop-down.js.map +1 -1
- package/dist/nodes/event-controller.d.ts +1 -0
- package/dist/nodes/event-controller.d.ts.map +1 -1
- package/dist/nodes/event-controller.js +11 -3
- package/dist/nodes/event-controller.js.map +1 -1
- package/dist/nodes/fixed-child.d.ts +1 -2
- package/dist/nodes/fixed-child.d.ts.map +1 -1
- package/dist/nodes/fixed-child.js +21 -21
- package/dist/nodes/fixed-child.js.map +1 -1
- package/dist/nodes/font-dialog-button.d.ts +1 -1
- package/dist/nodes/font-dialog-button.d.ts.map +1 -1
- package/dist/nodes/font-dialog-button.js +8 -0
- package/dist/nodes/font-dialog-button.js.map +1 -1
- package/dist/nodes/grid-view.d.ts +6 -5
- package/dist/nodes/grid-view.d.ts.map +1 -1
- package/dist/nodes/grid-view.js +23 -18
- package/dist/nodes/grid-view.js.map +1 -1
- package/dist/nodes/internal/accessible.d.ts +5 -0
- package/dist/nodes/internal/accessible.d.ts.map +1 -0
- package/dist/nodes/internal/accessible.js +119 -0
- package/dist/nodes/internal/accessible.js.map +1 -0
- package/dist/nodes/internal/base-item-renderer.d.ts.map +1 -1
- package/dist/nodes/internal/base-item-renderer.js +0 -1
- package/dist/nodes/internal/base-item-renderer.js.map +1 -1
- package/dist/nodes/internal/construct.d.ts +10 -0
- package/dist/nodes/internal/construct.d.ts.map +1 -0
- package/dist/nodes/internal/construct.js +68 -0
- package/dist/nodes/internal/construct.js.map +1 -0
- package/dist/nodes/internal/header-item-renderer.d.ts +23 -0
- package/dist/nodes/internal/header-item-renderer.d.ts.map +1 -0
- package/dist/nodes/internal/header-item-renderer.js +87 -0
- package/dist/nodes/internal/header-item-renderer.js.map +1 -0
- package/dist/nodes/internal/header-renderer-manager.d.ts +13 -0
- package/dist/nodes/internal/header-renderer-manager.d.ts.map +1 -0
- package/dist/nodes/internal/header-renderer-manager.js +20 -0
- package/dist/nodes/internal/header-renderer-manager.js.map +1 -0
- package/dist/nodes/internal/list-store.d.ts +10 -11
- package/dist/nodes/internal/list-store.d.ts.map +1 -1
- package/dist/nodes/internal/list-store.js +28 -29
- package/dist/nodes/internal/list-store.js.map +1 -1
- package/dist/nodes/internal/sectioned-list-store.d.ts +50 -0
- package/dist/nodes/internal/sectioned-list-store.d.ts.map +1 -0
- package/dist/nodes/internal/sectioned-list-store.js +250 -0
- package/dist/nodes/internal/sectioned-list-store.js.map +1 -0
- package/dist/nodes/internal/selection-helpers.d.ts +12 -0
- package/dist/nodes/internal/selection-helpers.d.ts.map +1 -0
- package/dist/nodes/internal/selection-helpers.js +25 -0
- package/dist/nodes/internal/selection-helpers.js.map +1 -0
- package/dist/nodes/internal/selection-model-controller.d.ts.map +1 -1
- package/dist/nodes/internal/selection-model-controller.js +3 -0
- package/dist/nodes/internal/selection-model-controller.js.map +1 -1
- package/dist/nodes/internal/simple-list-store.d.ts +7 -12
- package/dist/nodes/internal/simple-list-store.d.ts.map +1 -1
- package/dist/nodes/internal/simple-list-store.js +58 -35
- package/dist/nodes/internal/simple-list-store.js.map +1 -1
- package/dist/nodes/internal/text-buffer-controller.d.ts +4 -0
- package/dist/nodes/internal/text-buffer-controller.d.ts.map +1 -1
- package/dist/nodes/internal/text-buffer-controller.js +49 -9
- package/dist/nodes/internal/text-buffer-controller.js.map +1 -1
- package/dist/nodes/internal/tree-store.d.ts +3 -0
- package/dist/nodes/internal/tree-store.d.ts.map +1 -1
- package/dist/nodes/internal/tree-store.js +55 -10
- package/dist/nodes/internal/tree-store.js.map +1 -1
- package/dist/nodes/list-section.d.ts +27 -0
- package/dist/nodes/list-section.d.ts.map +1 -0
- package/dist/nodes/list-section.js +43 -0
- package/dist/nodes/list-section.js.map +1 -0
- package/dist/nodes/list-view.d.ts +6 -3
- package/dist/nodes/list-view.d.ts.map +1 -1
- package/dist/nodes/list-view.js +54 -14
- package/dist/nodes/list-view.js.map +1 -1
- package/dist/nodes/models/list.d.ts +13 -5
- package/dist/nodes/models/list.d.ts.map +1 -1
- package/dist/nodes/models/list.js +135 -21
- package/dist/nodes/models/list.js.map +1 -1
- package/dist/nodes/shortcut.d.ts +3 -2
- package/dist/nodes/shortcut.d.ts.map +1 -1
- package/dist/nodes/shortcut.js +19 -4
- package/dist/nodes/shortcut.js.map +1 -1
- package/dist/nodes/text-anchor.d.ts.map +1 -1
- package/dist/nodes/text-anchor.js +7 -1
- package/dist/nodes/text-anchor.js.map +1 -1
- package/dist/nodes/text-tag.d.ts.map +1 -1
- package/dist/nodes/text-tag.js +5 -1
- package/dist/nodes/text-tag.js.map +1 -1
- package/dist/nodes/text-view.d.ts +1 -0
- package/dist/nodes/text-view.d.ts.map +1 -1
- package/dist/nodes/text-view.js +4 -0
- package/dist/nodes/text-view.js.map +1 -1
- package/dist/nodes/widget.d.ts +0 -2
- package/dist/nodes/widget.d.ts.map +1 -1
- package/dist/nodes/widget.js +44 -61
- package/dist/nodes/widget.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +2 -2
- package/dist/registry.js.map +1 -1
- package/package.json +3 -3
- package/src/generated/internal.ts +333 -44
- package/src/generated/jsx.ts +178 -2
- package/src/host-config.ts +41 -10
- package/src/jsx.ts +166 -15
- package/src/metadata.ts +5 -1
- package/src/node.ts +20 -6
- package/src/nodes/column-view-column.ts +32 -8
- package/src/nodes/column-view.ts +59 -14
- package/src/nodes/drop-down.ts +182 -6
- package/src/nodes/event-controller.ts +11 -3
- package/src/nodes/fixed-child.ts +24 -23
- package/src/nodes/font-dialog-button.ts +10 -0
- package/src/nodes/grid-view.ts +29 -19
- package/src/nodes/internal/accessible.ts +156 -0
- package/src/nodes/internal/base-item-renderer.ts +0 -1
- package/src/nodes/internal/construct.ts +90 -0
- package/src/nodes/internal/header-item-renderer.ts +105 -0
- package/src/nodes/internal/header-renderer-manager.ts +33 -0
- package/src/nodes/internal/list-store.ts +32 -30
- package/src/nodes/internal/sectioned-list-store.ts +287 -0
- package/src/nodes/internal/selection-helpers.ts +35 -0
- package/src/nodes/internal/selection-model-controller.ts +4 -0
- package/src/nodes/internal/simple-list-store.ts +60 -43
- package/src/nodes/internal/text-buffer-controller.ts +51 -8
- package/src/nodes/internal/tree-store.ts +61 -9
- package/src/nodes/list-section.ts +64 -0
- package/src/nodes/list-view.ts +65 -14
- package/src/nodes/models/list.ts +147 -37
- package/src/nodes/shortcut.ts +22 -5
- package/src/nodes/text-anchor.ts +6 -1
- package/src/nodes/text-tag.ts +7 -1
- package/src/nodes/text-view.ts +5 -0
- package/src/nodes/widget.ts +45 -62
- package/src/registry.ts +4 -2
- package/dist/nodes/models/grid.d.ts +0 -28
- package/dist/nodes/models/grid.d.ts.map +0 -1
- package/dist/nodes/models/grid.js +0 -69
- package/dist/nodes/models/grid.js.map +0 -1
- package/dist/nodes/shortcut-controller.d.ts +0 -10
- package/dist/nodes/shortcut-controller.d.ts.map +0 -1
- package/dist/nodes/shortcut-controller.js +0 -23
- package/dist/nodes/shortcut-controller.js.map +0 -1
- package/src/nodes/models/grid.ts +0 -105
- package/src/nodes/shortcut-controller.ts +0 -27
|
@@ -1,24 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { SectionedListStore } from "./sectioned-list-store.js";
|
|
2
2
|
|
|
3
|
-
export class SimpleListStore {
|
|
4
|
-
private
|
|
5
|
-
private idToIndex = new Map<string, number>();
|
|
6
|
-
private model = new Gtk.StringList();
|
|
7
|
-
private pendingBatch: string[] | null = null;
|
|
3
|
+
export class SimpleListStore extends SectionedListStore {
|
|
4
|
+
private batchMode = false;
|
|
8
5
|
|
|
9
|
-
public beginBatch(): void {
|
|
10
|
-
this.
|
|
6
|
+
public override beginBatch(): void {
|
|
7
|
+
this.batchMode = true;
|
|
8
|
+
super.beginBatch();
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
public flushBatch(): void {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
public override flushBatch(): void {
|
|
12
|
+
this.batchMode = false;
|
|
13
|
+
super.flushBatch();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected override getInitialPendingBatch(): string[] | null {
|
|
17
|
+
return this.batchMode ? [] : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected override getModelString(_itemId: string, item: unknown): string {
|
|
21
|
+
return item as string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public getHeaderValueByLabel(label: string): unknown {
|
|
25
|
+
for (const section of this.sections) {
|
|
26
|
+
if (section.model.getNItems() > 0) {
|
|
27
|
+
const firstLabel = section.model.getString(0);
|
|
28
|
+
if (firstLabel === label) {
|
|
29
|
+
return this.headerValues.get(section.id);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
}
|
|
33
|
+
return undefined;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
public addItem(id: string, label: string): void {
|
|
37
|
+
this.flushRemovals();
|
|
38
|
+
if (this.sectioned) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
22
42
|
this.idToIndex.set(id, this.ids.length);
|
|
23
43
|
this.ids.push(id);
|
|
24
44
|
|
|
@@ -30,6 +50,7 @@ export class SimpleListStore {
|
|
|
30
50
|
}
|
|
31
51
|
|
|
32
52
|
public appendItem(id: string, label: string): void {
|
|
53
|
+
this.flushRemovals();
|
|
33
54
|
const existingIndex = this.idToIndex.get(id);
|
|
34
55
|
|
|
35
56
|
if (existingIndex !== undefined) {
|
|
@@ -43,17 +64,8 @@ export class SimpleListStore {
|
|
|
43
64
|
this.model.append(label);
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
public removeItem(id: string): void {
|
|
47
|
-
const index = this.idToIndex.get(id);
|
|
48
|
-
if (index === undefined) return;
|
|
49
|
-
|
|
50
|
-
this.model.remove(index);
|
|
51
|
-
this.ids.splice(index, 1);
|
|
52
|
-
this.idToIndex.delete(id);
|
|
53
|
-
this.rebuildIndices(index);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
67
|
public insertItemBefore(id: string, beforeId: string, label: string): void {
|
|
68
|
+
this.flushRemovals();
|
|
57
69
|
const beforeIndex = this.idToIndex.get(beforeId);
|
|
58
70
|
if (beforeIndex === undefined) {
|
|
59
71
|
this.addItem(id, label);
|
|
@@ -64,7 +76,20 @@ export class SimpleListStore {
|
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
public updateItem(id: string,
|
|
79
|
+
public updateItem(id: string, item: unknown): void {
|
|
80
|
+
this.flushRemovals();
|
|
81
|
+
const label = item as string;
|
|
82
|
+
if (this.sectioned) {
|
|
83
|
+
const sectionId = this.itemToSection.get(id);
|
|
84
|
+
if (!sectionId) return;
|
|
85
|
+
const section = this.sectionById.get(sectionId);
|
|
86
|
+
if (!section) return;
|
|
87
|
+
const indexInSection = section.itemIds.indexOf(id);
|
|
88
|
+
if (indexInSection >= 0) {
|
|
89
|
+
section.model.splice(indexInSection, 1, [label]);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
68
93
|
const index = this.idToIndex.get(id);
|
|
69
94
|
if (index === undefined) {
|
|
70
95
|
this.addItem(id, label);
|
|
@@ -73,27 +98,19 @@ export class SimpleListStore {
|
|
|
73
98
|
this.model.splice(index, 1, [label]);
|
|
74
99
|
}
|
|
75
100
|
|
|
76
|
-
public getItem(id: string) {
|
|
101
|
+
public getItem(id: string): string | null {
|
|
102
|
+
this.flushRemovals();
|
|
103
|
+
if (this.sectioned) {
|
|
104
|
+
const sectionId = this.itemToSection.get(id);
|
|
105
|
+
if (!sectionId) return null;
|
|
106
|
+
const section = this.sectionById.get(sectionId);
|
|
107
|
+
if (!section) return null;
|
|
108
|
+
const indexInSection = section.itemIds.indexOf(id);
|
|
109
|
+
if (indexInSection < 0) return null;
|
|
110
|
+
return section.model.getString(indexInSection);
|
|
111
|
+
}
|
|
77
112
|
const index = this.idToIndex.get(id);
|
|
78
113
|
if (index === undefined) return null;
|
|
79
114
|
return this.model.getString(index);
|
|
80
115
|
}
|
|
81
|
-
|
|
82
|
-
public getIdAtIndex(index: number): string | null {
|
|
83
|
-
return this.ids[index] ?? null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
public getIndexById(id: string): number | null {
|
|
87
|
-
return this.idToIndex.get(id) ?? null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public getModel(): Gtk.StringList {
|
|
91
|
-
return this.model;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private rebuildIndices(fromIndex: number): void {
|
|
95
|
-
for (let i = fromIndex; i < this.ids.length; i++) {
|
|
96
|
-
this.idToIndex.set(this.ids[i] as string, i);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
116
|
}
|
|
@@ -18,6 +18,8 @@ type BufferProps = Pick<GtkTextViewProps, "enableUndo"> & BufferCallbackProps;
|
|
|
18
18
|
export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffer> {
|
|
19
19
|
private buffer: TBuffer | null = null;
|
|
20
20
|
private textChildren: TextContentChild[] = [];
|
|
21
|
+
private initialMount = true;
|
|
22
|
+
private irreversibleStarted = false;
|
|
21
23
|
|
|
22
24
|
constructor(
|
|
23
25
|
private readonly owner: Node & TextContentParent,
|
|
@@ -41,6 +43,14 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
41
43
|
return this.buffer;
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
finalizeInitialMount(): void {
|
|
47
|
+
if (this.irreversibleStarted) {
|
|
48
|
+
this.buffer?.endIrreversibleAction();
|
|
49
|
+
this.irreversibleStarted = false;
|
|
50
|
+
}
|
|
51
|
+
this.initialMount = false;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
applyOwnProps(oldProps: BufferProps | null, newProps: BufferProps): void {
|
|
45
55
|
const hasBufferProps =
|
|
46
56
|
newProps.enableUndo !== undefined ||
|
|
@@ -130,6 +140,11 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
130
140
|
appendChild(child: TextContentChild): void {
|
|
131
141
|
const buffer = this.ensureBuffer();
|
|
132
142
|
|
|
143
|
+
if (this.initialMount && !this.irreversibleStarted) {
|
|
144
|
+
buffer.beginIrreversibleAction();
|
|
145
|
+
this.irreversibleStarted = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
133
148
|
const wasMoved = this.textChildren.indexOf(child) !== -1;
|
|
134
149
|
if (wasMoved) {
|
|
135
150
|
const existingIndex = this.textChildren.indexOf(child);
|
|
@@ -158,6 +173,7 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
158
173
|
if (!child.hasBuffer()) {
|
|
159
174
|
child.setBuffer(buffer);
|
|
160
175
|
}
|
|
176
|
+
this.setupEmbeddedObjects(child);
|
|
161
177
|
} else if (child instanceof TextAnchorNode) {
|
|
162
178
|
child.setTextViewAndBuffer(this.container, buffer);
|
|
163
179
|
} else if (child instanceof TextPaintableNode) {
|
|
@@ -173,6 +189,11 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
173
189
|
insertBefore(child: TextContentChild, before: TextContentChild): void {
|
|
174
190
|
const buffer = this.ensureBuffer();
|
|
175
191
|
|
|
192
|
+
if (this.initialMount && !this.irreversibleStarted) {
|
|
193
|
+
buffer.beginIrreversibleAction();
|
|
194
|
+
this.irreversibleStarted = true;
|
|
195
|
+
}
|
|
196
|
+
|
|
176
197
|
const existingIndex = this.textChildren.indexOf(child);
|
|
177
198
|
if (existingIndex !== -1) {
|
|
178
199
|
const oldOffset = child.getBufferOffset();
|
|
@@ -207,6 +228,7 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
207
228
|
if (!child.hasBuffer()) {
|
|
208
229
|
child.setBuffer(buffer);
|
|
209
230
|
}
|
|
231
|
+
this.setupEmbeddedObjects(child);
|
|
210
232
|
} else if (child instanceof TextAnchorNode) {
|
|
211
233
|
child.setTextViewAndBuffer(this.container, buffer);
|
|
212
234
|
} else if (child instanceof TextPaintableNode) {
|
|
@@ -234,6 +256,21 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
234
256
|
this.reapplyTagsFromOffset(offset);
|
|
235
257
|
}
|
|
236
258
|
|
|
259
|
+
private setupEmbeddedObjects(tag: TextTagNode): void {
|
|
260
|
+
if (!this.buffer) return;
|
|
261
|
+
|
|
262
|
+
for (const child of tag.children) {
|
|
263
|
+
if (child instanceof TextPaintableNode || child instanceof TextAnchorNode) {
|
|
264
|
+
this.deleteTextAtRange(child.getBufferOffset(), child.getBufferOffset() + 1);
|
|
265
|
+
child.setTextViewAndBuffer(this.container, this.buffer);
|
|
266
|
+
} else if (child instanceof TextTagNode) {
|
|
267
|
+
this.setupEmbeddedObjects(child);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
tag.reapplyTag();
|
|
272
|
+
}
|
|
273
|
+
|
|
237
274
|
private getTotalLength(): number {
|
|
238
275
|
let length = 0;
|
|
239
276
|
for (const child of this.textChildren) {
|
|
@@ -248,7 +285,7 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
248
285
|
|
|
249
286
|
const iter = new Gtk.TextIter();
|
|
250
287
|
buffer.getIterAtOffset(iter, offset);
|
|
251
|
-
buffer.insert(iter, text,
|
|
288
|
+
buffer.insert(iter, text, -1);
|
|
252
289
|
}
|
|
253
290
|
|
|
254
291
|
private deleteTextAtRange(start: number, end: number): void {
|
|
@@ -292,10 +329,7 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
292
329
|
private reapplyTagsFromOffset(fromOffset: number): void {
|
|
293
330
|
for (const child of this.textChildren) {
|
|
294
331
|
if (child instanceof TextTagNode) {
|
|
295
|
-
if (child.getBufferOffset()
|
|
296
|
-
child.reapplyTag();
|
|
297
|
-
this.reapplyAllTagsRecursive(child.children);
|
|
298
|
-
} else if (child.getBufferOffset() + child.getLength() > fromOffset) {
|
|
332
|
+
if (child.getBufferOffset() + child.getLength() > fromOffset) {
|
|
299
333
|
child.reapplyTag();
|
|
300
334
|
this.reapplyAllTagsRecursive(child.children);
|
|
301
335
|
}
|
|
@@ -320,9 +354,18 @@ export class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffe
|
|
|
320
354
|
onChildInserted(child: TextContentChild): void {
|
|
321
355
|
if (!this.buffer) return;
|
|
322
356
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
357
|
+
if (child instanceof TextPaintableNode) {
|
|
358
|
+
child.setTextViewAndBuffer(this.container, this.buffer);
|
|
359
|
+
} else if (child instanceof TextAnchorNode) {
|
|
360
|
+
child.setTextViewAndBuffer(this.container, this.buffer);
|
|
361
|
+
} else {
|
|
362
|
+
const text = child.getText();
|
|
363
|
+
if (text.length > 0) {
|
|
364
|
+
this.insertTextAtOffset(text, child.getBufferOffset());
|
|
365
|
+
}
|
|
366
|
+
if (child instanceof TextTagNode) {
|
|
367
|
+
this.setupEmbeddedObjects(child);
|
|
368
|
+
}
|
|
326
369
|
}
|
|
327
370
|
|
|
328
371
|
const containingIndex = this.findDirectChildContaining(child.getBufferOffset());
|
|
@@ -19,6 +19,8 @@ export class TreeStore {
|
|
|
19
19
|
private items = new Map<string, TreeItemData>();
|
|
20
20
|
private onItemUpdated: TreeItemUpdatedCallback | null = null;
|
|
21
21
|
private pendingBatch: string[] | null = null;
|
|
22
|
+
private pendingRemovals: Set<string> | null = null;
|
|
23
|
+
private flushScheduled = false;
|
|
22
24
|
|
|
23
25
|
public setOnItemUpdated(callback: TreeItemUpdatedCallback | null): void {
|
|
24
26
|
this.onItemUpdated = callback;
|
|
@@ -46,6 +48,7 @@ export class TreeStore {
|
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
public addItem(id: string, data: TreeItemData, parentId?: string): void {
|
|
51
|
+
if (parentId === undefined) this.flushRemovals();
|
|
49
52
|
this.items.set(id, data);
|
|
50
53
|
|
|
51
54
|
if (parentId === undefined) {
|
|
@@ -104,15 +107,7 @@ export class TreeStore {
|
|
|
104
107
|
this.childIdToIndex.delete(id);
|
|
105
108
|
this.childModels.delete(id);
|
|
106
109
|
|
|
107
|
-
if (parentId
|
|
108
|
-
const index = this.rootIdToIndex.get(id);
|
|
109
|
-
if (index !== undefined) {
|
|
110
|
-
this.rootIds.splice(index, 1);
|
|
111
|
-
this.rootIdToIndex.delete(id);
|
|
112
|
-
this.rebuildRootIndices(index);
|
|
113
|
-
this.rootModel.remove(index);
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
110
|
+
if (parentId !== undefined) {
|
|
116
111
|
const siblings = this.children.get(parentId);
|
|
117
112
|
const indexMap = this.childIdToIndex.get(parentId);
|
|
118
113
|
if (siblings && indexMap) {
|
|
@@ -131,10 +126,67 @@ export class TreeStore {
|
|
|
131
126
|
this.childIdToIndex.delete(parentId);
|
|
132
127
|
}
|
|
133
128
|
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!this.rootIdToIndex.has(id)) return;
|
|
133
|
+
|
|
134
|
+
if (!this.pendingRemovals) {
|
|
135
|
+
this.pendingRemovals = new Set();
|
|
136
|
+
if (!this.flushScheduled) {
|
|
137
|
+
this.flushScheduled = true;
|
|
138
|
+
queueMicrotask(() => this.flushRemovals());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
this.pendingRemovals.add(id);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public flushRemovals(): void {
|
|
145
|
+
this.flushScheduled = false;
|
|
146
|
+
const removals = this.pendingRemovals;
|
|
147
|
+
if (!removals || removals.size === 0) {
|
|
148
|
+
this.pendingRemovals = null;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.pendingRemovals = null;
|
|
152
|
+
|
|
153
|
+
const indices: number[] = [];
|
|
154
|
+
for (const id of removals) {
|
|
155
|
+
const index = this.rootIdToIndex.get(id);
|
|
156
|
+
if (index !== undefined) {
|
|
157
|
+
indices.push(index);
|
|
158
|
+
this.rootIdToIndex.delete(id);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (indices.length === 0) return;
|
|
163
|
+
|
|
164
|
+
indices.sort((a, b) => a - b);
|
|
165
|
+
|
|
166
|
+
let i = indices.length - 1;
|
|
167
|
+
while (i >= 0) {
|
|
168
|
+
let rangeStart = indices[i] ?? 0;
|
|
169
|
+
const rangeEnd = rangeStart;
|
|
170
|
+
|
|
171
|
+
while (i > 0) {
|
|
172
|
+
const prev = indices[i - 1];
|
|
173
|
+
if (prev !== rangeStart - 1) break;
|
|
174
|
+
i--;
|
|
175
|
+
rangeStart = prev ?? 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const count = rangeEnd - rangeStart + 1;
|
|
179
|
+
this.rootModel.splice(rangeStart, count);
|
|
180
|
+
this.rootIds.splice(rangeStart, count);
|
|
181
|
+
|
|
182
|
+
i--;
|
|
134
183
|
}
|
|
184
|
+
|
|
185
|
+
this.rebuildRootIndices(0);
|
|
135
186
|
}
|
|
136
187
|
|
|
137
188
|
public insertItemBefore(id: string, beforeId: string, data: TreeItemData, parentId?: string): void {
|
|
189
|
+
if (parentId === undefined) this.flushRemovals();
|
|
138
190
|
this.items.set(id, data);
|
|
139
191
|
|
|
140
192
|
if (parentId === undefined) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ListSectionProps } from "../jsx.js";
|
|
2
|
+
import type { Node } from "../node.js";
|
|
3
|
+
import { hasChanged } from "./internal/props.js";
|
|
4
|
+
import { ListItemNode } from "./list-item.js";
|
|
5
|
+
import { VirtualNode } from "./virtual.js";
|
|
6
|
+
|
|
7
|
+
type SectionStore = {
|
|
8
|
+
addItemToSection(sectionId: string, itemId: string, item: unknown): void;
|
|
9
|
+
addItemsToSection(sectionId: string, items: { itemId: string; item: unknown }[]): void;
|
|
10
|
+
removeItemFromSection(itemId: string): void;
|
|
11
|
+
updateHeaderValue(sectionId: string, value: unknown): void;
|
|
12
|
+
insertItemToSectionBefore(sectionId: string, itemId: string, beforeId: string, item: unknown): void;
|
|
13
|
+
updateItem(id: string, item: unknown): void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class ListSectionNode extends VirtualNode<ListSectionProps, Node, ListItemNode> {
|
|
17
|
+
private store: SectionStore | null = null;
|
|
18
|
+
|
|
19
|
+
public override isValidChild(child: Node): boolean {
|
|
20
|
+
return child instanceof ListItemNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public override appendChild(child: ListItemNode): void {
|
|
24
|
+
super.appendChild(child);
|
|
25
|
+
if (this.store) {
|
|
26
|
+
this.store.addItemToSection(this.props.id, child.props.id, child.props.value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public override insertBefore(child: ListItemNode, before: ListItemNode): void {
|
|
31
|
+
super.insertBefore(child, before);
|
|
32
|
+
if (this.store) {
|
|
33
|
+
this.store.insertItemToSectionBefore(this.props.id, child.props.id, before.props.id, child.props.value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public override removeChild(child: ListItemNode): void {
|
|
38
|
+
if (this.store) {
|
|
39
|
+
this.store.removeItemFromSection(child.props.id);
|
|
40
|
+
}
|
|
41
|
+
super.removeChild(child);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public override commitUpdate(oldProps: ListSectionProps | null, newProps: ListSectionProps): void {
|
|
45
|
+
super.commitUpdate(oldProps, newProps);
|
|
46
|
+
if (this.store && hasChanged(oldProps, newProps, "value")) {
|
|
47
|
+
this.store.updateHeaderValue(newProps.id, newProps.value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public setStore(store: SectionStore | null): void {
|
|
52
|
+
this.store = store;
|
|
53
|
+
if (store && this.children.length > 0) {
|
|
54
|
+
store.addItemsToSection(
|
|
55
|
+
this.props.id,
|
|
56
|
+
this.children.map((child) => ({ itemId: child.props.id, item: child.props.value })),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public getChildNodes(): readonly ListItemNode[] {
|
|
62
|
+
return this.children;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/nodes/list-view.ts
CHANGED
|
@@ -4,27 +4,34 @@ import type { Node } from "../node.js";
|
|
|
4
4
|
import type { Container } from "../types.js";
|
|
5
5
|
import { ContainerSlotNode } from "./container-slot.js";
|
|
6
6
|
import { EventControllerNode } from "./event-controller.js";
|
|
7
|
+
import { GridItemRenderer } from "./internal/grid-item-renderer.js";
|
|
8
|
+
import type { HeaderItemRenderer } from "./internal/header-item-renderer.js";
|
|
9
|
+
import { updateHeaderRenderer } from "./internal/header-renderer-manager.js";
|
|
7
10
|
import { ListItemRenderer } from "./internal/list-item-renderer.js";
|
|
8
11
|
import { filterProps, hasChanged } from "./internal/props.js";
|
|
9
12
|
import { ListItemNode } from "./list-item.js";
|
|
13
|
+
import { ListSectionNode } from "./list-section.js";
|
|
10
14
|
import { ListModel, type ListModelProps } from "./models/list.js";
|
|
11
15
|
import { SlotNode } from "./slot.js";
|
|
12
16
|
import { WidgetNode } from "./widget.js";
|
|
13
17
|
|
|
14
|
-
const RENDERER_PROPS = ["renderItem", "estimatedItemHeight"] as const;
|
|
18
|
+
const RENDERER_PROPS = ["renderItem", "renderHeader", "estimatedItemHeight"] as const;
|
|
15
19
|
const OWN_PROPS = [...RENDERER_PROPS, "autoexpand", "selectionMode", "selected", "onSelectionChanged"] as const;
|
|
16
20
|
|
|
17
21
|
type ListViewProps = Pick<GtkListViewProps, (typeof RENDERER_PROPS)[number]> & ListModelProps;
|
|
18
22
|
|
|
19
|
-
type ListViewChild = ListItemNode | EventControllerNode | SlotNode | ContainerSlotNode;
|
|
23
|
+
type ListViewChild = ListItemNode | ListSectionNode | EventControllerNode | SlotNode | ContainerSlotNode;
|
|
20
24
|
|
|
21
25
|
export class ListViewNode extends WidgetNode<Gtk.ListView, ListViewProps, ListViewChild> {
|
|
22
|
-
private
|
|
26
|
+
private treeRenderer: ListItemRenderer | null = null;
|
|
27
|
+
private flatRenderer: GridItemRenderer | null = null;
|
|
28
|
+
private headerRenderer: HeaderItemRenderer | null = null;
|
|
23
29
|
private list: ListModel;
|
|
24
30
|
|
|
25
31
|
public override isValidChild(child: Node): boolean {
|
|
26
32
|
return (
|
|
27
33
|
child instanceof ListItemNode ||
|
|
34
|
+
child instanceof ListSectionNode ||
|
|
28
35
|
child instanceof EventControllerNode ||
|
|
29
36
|
child instanceof SlotNode ||
|
|
30
37
|
child instanceof ContainerSlotNode
|
|
@@ -33,37 +40,54 @@ export class ListViewNode extends WidgetNode<Gtk.ListView, ListViewProps, ListVi
|
|
|
33
40
|
|
|
34
41
|
constructor(typeName: string, props: ListViewProps, container: Gtk.ListView, rootContainer: Container) {
|
|
35
42
|
super(typeName, props, container, rootContainer);
|
|
43
|
+
|
|
44
|
+
const flat = props.renderHeader != null;
|
|
45
|
+
|
|
36
46
|
this.list = new ListModel(
|
|
37
47
|
{ owner: this, signalStore: this.signalStore },
|
|
38
48
|
{
|
|
39
|
-
autoexpand: props.autoexpand,
|
|
49
|
+
autoexpand: flat ? undefined : props.autoexpand,
|
|
40
50
|
selectionMode: props.selectionMode,
|
|
41
51
|
selected: props.selected,
|
|
42
52
|
onSelectionChanged: props.onSelectionChanged,
|
|
43
53
|
},
|
|
54
|
+
flat,
|
|
44
55
|
);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
|
|
57
|
+
if (flat) {
|
|
58
|
+
const store = this.list.getFlatStore();
|
|
59
|
+
this.flatRenderer = new GridItemRenderer(this.signalStore);
|
|
60
|
+
this.flatRenderer.setStore(store);
|
|
61
|
+
store.setOnItemUpdated((id) => this.flatRenderer?.rebindItem(id));
|
|
62
|
+
this.container.setFactory(this.flatRenderer.getFactory());
|
|
63
|
+
} else {
|
|
64
|
+
const store = this.list.getStore();
|
|
65
|
+
this.treeRenderer = new ListItemRenderer(this.signalStore);
|
|
66
|
+
this.treeRenderer.setStore(store);
|
|
67
|
+
store.setOnItemUpdated((id) => this.treeRenderer?.rebindItem(id));
|
|
68
|
+
this.container.setFactory(this.treeRenderer.getFactory());
|
|
69
|
+
}
|
|
49
70
|
}
|
|
50
71
|
|
|
51
72
|
public override appendChild(child: ListViewChild): void {
|
|
52
73
|
super.appendChild(child);
|
|
53
|
-
if (child instanceof ListItemNode) {
|
|
74
|
+
if (child instanceof ListItemNode || child instanceof ListSectionNode) {
|
|
54
75
|
this.list.appendChild(child);
|
|
55
76
|
}
|
|
56
77
|
}
|
|
57
78
|
|
|
58
79
|
public override insertBefore(child: ListViewChild, before: ListViewChild): void {
|
|
59
80
|
super.insertBefore(child, before);
|
|
60
|
-
if (
|
|
81
|
+
if (
|
|
82
|
+
(child instanceof ListItemNode || child instanceof ListSectionNode) &&
|
|
83
|
+
(before instanceof ListItemNode || before instanceof ListSectionNode)
|
|
84
|
+
) {
|
|
61
85
|
this.list.insertBefore(child, before);
|
|
62
86
|
}
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
public override removeChild(child: ListViewChild): void {
|
|
66
|
-
if (child instanceof ListItemNode) {
|
|
90
|
+
if (child instanceof ListItemNode || child instanceof ListSectionNode) {
|
|
67
91
|
this.list.removeChild(child);
|
|
68
92
|
}
|
|
69
93
|
super.removeChild(child);
|
|
@@ -86,17 +110,44 @@ export class ListViewNode extends WidgetNode<Gtk.ListView, ListViewProps, ListVi
|
|
|
86
110
|
}
|
|
87
111
|
|
|
88
112
|
public override detachDeletedInstance(): void {
|
|
89
|
-
this.
|
|
113
|
+
this.treeRenderer?.dispose();
|
|
114
|
+
this.flatRenderer?.dispose();
|
|
115
|
+
this.headerRenderer?.dispose();
|
|
90
116
|
super.detachDeletedInstance();
|
|
91
117
|
}
|
|
92
118
|
|
|
93
119
|
private applyOwnProps(oldProps: ListViewProps | null, newProps: ListViewProps): void {
|
|
94
120
|
if (hasChanged(oldProps, newProps, "renderItem")) {
|
|
95
|
-
this.
|
|
121
|
+
if (this.treeRenderer) {
|
|
122
|
+
this.treeRenderer.setRenderFn(newProps.renderItem ?? null);
|
|
123
|
+
}
|
|
124
|
+
if (this.flatRenderer) {
|
|
125
|
+
const renderItem = newProps.renderItem;
|
|
126
|
+
this.flatRenderer.setRenderFn(renderItem ? (item: unknown) => renderItem(item, null) : null);
|
|
127
|
+
}
|
|
96
128
|
}
|
|
97
129
|
|
|
98
130
|
if (hasChanged(oldProps, newProps, "estimatedItemHeight")) {
|
|
99
|
-
|
|
131
|
+
const height = newProps.estimatedItemHeight ?? null;
|
|
132
|
+
if (this.treeRenderer) {
|
|
133
|
+
this.treeRenderer.setEstimatedItemHeight(height);
|
|
134
|
+
}
|
|
135
|
+
if (this.flatRenderer) {
|
|
136
|
+
this.flatRenderer.setEstimatedItemHeight(height);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (hasChanged(oldProps, newProps, "renderHeader")) {
|
|
141
|
+
this.headerRenderer = updateHeaderRenderer(
|
|
142
|
+
this.headerRenderer,
|
|
143
|
+
{
|
|
144
|
+
signalStore: this.signalStore,
|
|
145
|
+
isEnabled: () => this.list.isFlatMode(),
|
|
146
|
+
resolveItem: (id) => this.list.getFlatStore().getHeaderValue(id),
|
|
147
|
+
setFactory: (factory) => this.container.setHeaderFactory(factory),
|
|
148
|
+
},
|
|
149
|
+
newProps.renderHeader,
|
|
150
|
+
);
|
|
100
151
|
}
|
|
101
152
|
|
|
102
153
|
const previousModel = this.list.getSelectionModel();
|