@gtkx/react 0.17.2 → 0.18.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 (194) hide show
  1. package/README.md +7 -7
  2. package/dist/factory.d.ts +0 -1
  3. package/dist/factory.js +21 -8
  4. package/dist/generated/internal.d.ts +4 -51
  5. package/dist/generated/internal.js +626 -412
  6. package/dist/generated/jsx.d.ts +453 -958
  7. package/dist/host-config.d.ts +1 -1
  8. package/dist/host-config.js +18 -23
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/jsx.d.ts +579 -302
  12. package/dist/jsx.js +37 -179
  13. package/dist/metadata.d.ts +3 -0
  14. package/dist/metadata.js +26 -0
  15. package/dist/node.d.ts +20 -12
  16. package/dist/node.js +72 -17
  17. package/dist/nodes/adjustable.d.ts +3 -16
  18. package/dist/nodes/adjustable.js +5 -22
  19. package/dist/nodes/alert-dialog-response.d.ts +14 -1
  20. package/dist/nodes/alert-dialog-response.js +36 -62
  21. package/dist/nodes/animation.d.ts +37 -1
  22. package/dist/nodes/animation.js +162 -105
  23. package/dist/nodes/application.d.ts +11 -1
  24. package/dist/nodes/application.js +17 -38
  25. package/dist/nodes/calendar.d.ts +13 -0
  26. package/dist/nodes/calendar.js +10 -16
  27. package/dist/nodes/color-dialog-button.d.ts +13 -0
  28. package/dist/nodes/color-dialog-button.js +10 -38
  29. package/dist/nodes/column-view-column.d.ts +13 -11
  30. package/dist/nodes/column-view-column.js +27 -23
  31. package/dist/nodes/column-view.d.ts +31 -0
  32. package/dist/nodes/column-view.js +44 -44
  33. package/dist/nodes/container-slot.d.ts +15 -0
  34. package/dist/nodes/container-slot.js +68 -0
  35. package/dist/nodes/dialog.d.ts +6 -8
  36. package/dist/nodes/dialog.js +12 -13
  37. package/dist/nodes/drawing-area.d.ts +12 -0
  38. package/dist/nodes/drawing-area.js +24 -24
  39. package/dist/nodes/drop-down.d.ts +22 -0
  40. package/dist/nodes/drop-down.js +72 -0
  41. package/dist/nodes/event-controller.d.ts +8 -17
  42. package/dist/nodes/event-controller.js +20 -42
  43. package/dist/nodes/fixed-child.d.ts +18 -1
  44. package/dist/nodes/fixed-child.js +52 -36
  45. package/dist/nodes/font-dialog-button.d.ts +13 -0
  46. package/dist/nodes/font-dialog-button.js +12 -35
  47. package/dist/nodes/grid-child.d.ts +17 -1
  48. package/dist/nodes/grid-child.js +57 -37
  49. package/dist/nodes/grid-view.d.ts +24 -0
  50. package/dist/nodes/grid-view.js +73 -0
  51. package/dist/nodes/internal/base-item-renderer.d.ts +7 -9
  52. package/dist/nodes/internal/base-item-renderer.js +15 -18
  53. package/dist/nodes/internal/grid-item-renderer.d.ts +17 -0
  54. package/dist/nodes/internal/grid-item-renderer.js +59 -0
  55. package/dist/nodes/internal/list-item-renderer.d.ts +14 -9
  56. package/dist/nodes/internal/list-item-renderer.js +96 -35
  57. package/dist/nodes/internal/list-store.d.ts +5 -0
  58. package/dist/nodes/internal/list-store.js +39 -9
  59. package/dist/nodes/internal/predicates.d.ts +4 -19
  60. package/dist/nodes/internal/predicates.js +1 -20
  61. package/dist/nodes/internal/props.d.ts +5 -0
  62. package/dist/nodes/internal/props.js +42 -0
  63. package/dist/nodes/internal/{selection-model.d.ts → selection-model-controller.d.ts} +4 -9
  64. package/dist/nodes/internal/{selection-model.js → selection-model-controller.js} +6 -15
  65. package/dist/nodes/internal/signal-store.js +12 -5
  66. package/dist/nodes/internal/simple-list-store.d.ts +5 -0
  67. package/dist/nodes/internal/simple-list-store.js +42 -13
  68. package/dist/nodes/internal/text-buffer-controller.d.ts +4 -12
  69. package/dist/nodes/internal/text-buffer-controller.js +32 -33
  70. package/dist/nodes/internal/tree-store.d.ts +7 -0
  71. package/dist/nodes/internal/tree-store.js +75 -18
  72. package/dist/nodes/internal/widget.d.ts +7 -0
  73. package/dist/nodes/internal/widget.js +68 -0
  74. package/dist/nodes/level-bar.d.ts +10 -0
  75. package/dist/nodes/level-bar.js +11 -22
  76. package/dist/nodes/list-item.d.ts +17 -9
  77. package/dist/nodes/list-item.js +67 -12
  78. package/dist/nodes/list-view.d.ts +23 -0
  79. package/dist/nodes/list-view.js +27 -31
  80. package/dist/nodes/menu.d.ts +2 -4
  81. package/dist/nodes/menu.js +0 -6
  82. package/dist/nodes/models/grid.d.ts +27 -0
  83. package/dist/nodes/models/grid.js +68 -0
  84. package/dist/nodes/models/list.d.ts +15 -13
  85. package/dist/nodes/models/list.js +48 -26
  86. package/dist/nodes/models/menu.d.ts +15 -16
  87. package/dist/nodes/models/menu.js +63 -93
  88. package/dist/nodes/navigation-page.d.ts +16 -10
  89. package/dist/nodes/navigation-page.js +108 -31
  90. package/dist/nodes/navigation-view.d.ts +15 -0
  91. package/dist/nodes/navigation-view.js +15 -65
  92. package/dist/nodes/notebook-page-tab.d.ts +10 -12
  93. package/dist/nodes/notebook-page-tab.js +24 -27
  94. package/dist/nodes/notebook-page.d.ts +19 -16
  95. package/dist/nodes/notebook-page.js +75 -56
  96. package/dist/nodes/notebook.d.ts +10 -1
  97. package/dist/nodes/notebook.js +10 -22
  98. package/dist/nodes/overlay-child.d.ts +17 -1
  99. package/dist/nodes/overlay-child.js +53 -75
  100. package/dist/nodes/popover-menu.d.ts +15 -0
  101. package/dist/nodes/popover-menu.js +13 -26
  102. package/dist/nodes/scale.d.ts +8 -0
  103. package/dist/nodes/scale.js +2 -11
  104. package/dist/nodes/scrolled-window.d.ts +9 -0
  105. package/dist/nodes/scrolled-window.js +5 -11
  106. package/dist/nodes/search-bar.d.ts +9 -0
  107. package/dist/nodes/search-bar.js +8 -33
  108. package/dist/nodes/shortcut-controller.d.ts +9 -1
  109. package/dist/nodes/shortcut-controller.js +12 -25
  110. package/dist/nodes/shortcut.d.ts +11 -33
  111. package/dist/nodes/shortcut.js +19 -15
  112. package/dist/nodes/slot.d.ts +16 -15
  113. package/dist/nodes/slot.js +63 -57
  114. package/dist/nodes/source-view.d.ts +16 -0
  115. package/dist/nodes/source-view.js +44 -44
  116. package/dist/nodes/stack-page.d.ts +21 -1
  117. package/dist/nodes/stack-page.js +68 -17
  118. package/dist/nodes/stack.d.ts +11 -0
  119. package/dist/nodes/stack.js +8 -26
  120. package/dist/nodes/text-anchor.d.ts +11 -30
  121. package/dist/nodes/text-anchor.js +20 -22
  122. package/dist/nodes/text-content.d.ts +1 -0
  123. package/dist/nodes/text-content.js +1 -1
  124. package/dist/nodes/text-paintable.d.ts +10 -15
  125. package/dist/nodes/text-paintable.js +16 -9
  126. package/dist/nodes/text-segment.d.ts +12 -10
  127. package/dist/nodes/text-segment.js +19 -11
  128. package/dist/nodes/text-tag.d.ts +20 -119
  129. package/dist/nodes/text-tag.js +153 -119
  130. package/dist/nodes/text-view.d.ts +13 -18
  131. package/dist/nodes/text-view.js +17 -17
  132. package/dist/nodes/toggle-group.d.ts +9 -0
  133. package/dist/nodes/toggle-group.js +8 -33
  134. package/dist/nodes/toggle.d.ts +15 -1
  135. package/dist/nodes/toggle.js +34 -52
  136. package/dist/nodes/virtual.d.ts +3 -10
  137. package/dist/nodes/virtual.js +1 -14
  138. package/dist/nodes/web-view.d.ts +9 -0
  139. package/dist/nodes/web-view.js +10 -24
  140. package/dist/nodes/widget.d.ts +17 -13
  141. package/dist/nodes/widget.js +185 -112
  142. package/dist/nodes/window.d.ts +20 -21
  143. package/dist/nodes/window.js +54 -35
  144. package/dist/registry.d.ts +17 -6
  145. package/dist/registry.js +104 -5
  146. package/dist/render.d.ts +1 -10
  147. package/dist/render.js +1 -13
  148. package/package.json +6 -6
  149. package/dist/animation/css-builder.d.ts +0 -3
  150. package/dist/animation/css-builder.js +0 -53
  151. package/dist/animation/types.d.ts +0 -120
  152. package/dist/animation/types.js +0 -1
  153. package/dist/nodes/abstract/positional-child.d.ts +0 -9
  154. package/dist/nodes/abstract/positional-child.js +0 -29
  155. package/dist/nodes/abstract/virtual-container.d.ts +0 -21
  156. package/dist/nodes/abstract/virtual-container.js +0 -68
  157. package/dist/nodes/abstract/virtual-single-child.d.ts +0 -18
  158. package/dist/nodes/abstract/virtual-single-child.js +0 -55
  159. package/dist/nodes/action-row-child.d.ts +0 -1
  160. package/dist/nodes/action-row-child.js +0 -30
  161. package/dist/nodes/autowrapped.d.ts +0 -1
  162. package/dist/nodes/autowrapped.js +0 -115
  163. package/dist/nodes/expander-row-child.d.ts +0 -1
  164. package/dist/nodes/expander-row-child.js +0 -30
  165. package/dist/nodes/grid.d.ts +0 -1
  166. package/dist/nodes/grid.js +0 -41
  167. package/dist/nodes/index.d.ts +0 -56
  168. package/dist/nodes/index.js +0 -56
  169. package/dist/nodes/internal/child-attachment.d.ts +0 -26
  170. package/dist/nodes/internal/child-attachment.js +0 -48
  171. package/dist/nodes/internal/deferred-action.d.ts +0 -9
  172. package/dist/nodes/internal/deferred-action.js +0 -22
  173. package/dist/nodes/internal/text-tag-styles.d.ts +0 -43
  174. package/dist/nodes/internal/text-tag-styles.js +0 -52
  175. package/dist/nodes/internal/tree-list-item-renderer.d.ts +0 -26
  176. package/dist/nodes/internal/tree-list-item-renderer.js +0 -134
  177. package/dist/nodes/internal/utils.d.ts +0 -12
  178. package/dist/nodes/internal/utils.js +0 -92
  179. package/dist/nodes/models/tree-list.d.ts +0 -28
  180. package/dist/nodes/models/tree-list.js +0 -113
  181. package/dist/nodes/pack-child.d.ts +0 -1
  182. package/dist/nodes/pack-child.js +0 -30
  183. package/dist/nodes/simple-list-item.d.ts +0 -9
  184. package/dist/nodes/simple-list-item.js +0 -9
  185. package/dist/nodes/simple-list-view.d.ts +0 -1
  186. package/dist/nodes/simple-list-view.js +0 -74
  187. package/dist/nodes/toolbar-child.d.ts +0 -1
  188. package/dist/nodes/toolbar-child.js +0 -30
  189. package/dist/nodes/tree-list-item.d.ts +0 -22
  190. package/dist/nodes/tree-list-item.js +0 -90
  191. package/dist/nodes/tree-list-view.d.ts +0 -1
  192. package/dist/nodes/tree-list-view.js +0 -77
  193. package/dist/scheduler.d.ts +0 -26
  194. package/dist/scheduler.js +0 -42
@@ -1,4 +1,3 @@
1
- import { getNativeId } from "@gtkx/ffi";
2
1
  import * as GObject from "@gtkx/ffi/gobject";
3
2
  const LIFECYCLE_SIGNALS = new Set([
4
3
  "realize",
@@ -15,6 +14,16 @@ const LIFECYCLE_SIGNALS = new Set([
15
14
  "unbind",
16
15
  "teardown",
17
16
  ]);
17
+ const signalKeyCache = new WeakMap();
18
+ let nextSignalObjId = 0;
19
+ function getSignalKey(obj, signal) {
20
+ let objKey = signalKeyCache.get(obj);
21
+ if (!objKey) {
22
+ objKey = String(nextSignalObjId++);
23
+ signalKeyCache.set(obj, objKey);
24
+ }
25
+ return `${objKey}:${signal}`;
26
+ }
18
27
  export class SignalStore {
19
28
  ownerHandlers = new Map();
20
29
  blockDepth = 0;
@@ -36,8 +45,7 @@ export class SignalStore {
36
45
  };
37
46
  }
38
47
  disconnect(owner, obj, signal) {
39
- const objectId = getNativeId(obj.handle);
40
- const key = `${objectId}:${signal}`;
48
+ const key = getSignalKey(obj, signal);
41
49
  const ownerMap = this.ownerHandlers.get(owner);
42
50
  const existing = ownerMap?.get(key);
43
51
  if (existing) {
@@ -46,8 +54,7 @@ export class SignalStore {
46
54
  }
47
55
  }
48
56
  connect(owner, obj, signal, handler, blockable) {
49
- const objectId = getNativeId(obj.handle);
50
- const key = `${objectId}:${signal}`;
57
+ const key = getSignalKey(obj, signal);
51
58
  const wrappedHandler = this.wrapHandler(handler, signal, blockable);
52
59
  const handlerId = obj.connect(signal, wrappedHandler);
53
60
  this.getOwnerMap(owner).set(key, { obj, handlerId });
@@ -1,7 +1,11 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  export declare class SimpleListStore {
3
3
  private ids;
4
+ private idToIndex;
4
5
  private model;
6
+ private pendingBatch;
7
+ beginBatch(): void;
8
+ flushBatch(): void;
5
9
  addItem(id: string, label: string): void;
6
10
  appendItem(id: string, label: string): void;
7
11
  removeItem(id: string): void;
@@ -11,4 +15,5 @@ export declare class SimpleListStore {
11
15
  getIdAtIndex(index: number): string | null;
12
16
  getIndexById(id: string): number | null;
13
17
  getModel(): Gtk.StringList;
18
+ private rebuildIndices;
14
19
  }
@@ -1,57 +1,86 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  export class SimpleListStore {
3
3
  ids = [];
4
+ idToIndex = new Map();
4
5
  model = new Gtk.StringList();
6
+ pendingBatch = null;
7
+ beginBatch() {
8
+ this.pendingBatch = [];
9
+ }
10
+ flushBatch() {
11
+ const batch = this.pendingBatch;
12
+ this.pendingBatch = null;
13
+ if (batch && batch.length > 0) {
14
+ this.model.splice(0, 0, batch);
15
+ }
16
+ }
5
17
  addItem(id, label) {
18
+ this.idToIndex.set(id, this.ids.length);
6
19
  this.ids.push(id);
7
- this.model.append(label);
20
+ if (this.pendingBatch) {
21
+ this.pendingBatch.push(label);
22
+ }
23
+ else {
24
+ this.model.append(label);
25
+ }
8
26
  }
9
27
  appendItem(id, label) {
10
- const existingIndex = this.ids.indexOf(id);
11
- if (existingIndex >= 0) {
28
+ const existingIndex = this.idToIndex.get(id);
29
+ if (existingIndex !== undefined) {
12
30
  this.model.remove(existingIndex);
13
31
  this.ids.splice(existingIndex, 1);
32
+ this.rebuildIndices(existingIndex);
14
33
  }
34
+ this.idToIndex.set(id, this.ids.length);
15
35
  this.ids.push(id);
16
36
  this.model.append(label);
17
37
  }
18
38
  removeItem(id) {
19
- const index = this.ids.indexOf(id);
20
- if (index < 0) {
39
+ const index = this.idToIndex.get(id);
40
+ if (index === undefined)
21
41
  return;
22
- }
23
42
  this.model.remove(index);
24
43
  this.ids.splice(index, 1);
44
+ this.idToIndex.delete(id);
45
+ this.rebuildIndices(index);
25
46
  }
26
47
  insertItemBefore(id, beforeId, label) {
27
- const beforeIndex = this.ids.indexOf(beforeId);
28
- if (beforeIndex < 0) {
48
+ const beforeIndex = this.idToIndex.get(beforeId);
49
+ if (beforeIndex === undefined) {
29
50
  this.addItem(id, label);
30
51
  }
31
52
  else {
32
53
  this.ids.splice(beforeIndex, 0, id);
54
+ this.rebuildIndices(beforeIndex);
33
55
  this.model.splice(beforeIndex, 0, [label]);
34
56
  }
35
57
  }
36
58
  updateItem(id, label) {
37
- const index = this.ids.indexOf(id);
38
- if (index < 0) {
59
+ const index = this.idToIndex.get(id);
60
+ if (index === undefined) {
39
61
  this.addItem(id, label);
40
62
  return;
41
63
  }
42
64
  this.model.splice(index, 1, [label]);
43
65
  }
44
66
  getItem(id) {
45
- return this.model.getString(this.ids.indexOf(id));
67
+ const index = this.idToIndex.get(id);
68
+ if (index === undefined)
69
+ return null;
70
+ return this.model.getString(index);
46
71
  }
47
72
  getIdAtIndex(index) {
48
73
  return this.ids[index] ?? null;
49
74
  }
50
75
  getIndexById(id) {
51
- const index = this.ids.indexOf(id);
52
- return index >= 0 ? index : null;
76
+ return this.idToIndex.get(id) ?? null;
53
77
  }
54
78
  getModel() {
55
79
  return this.model;
56
80
  }
81
+ rebuildIndices(fromIndex) {
82
+ for (let i = fromIndex; i < this.ids.length; i++) {
83
+ this.idToIndex.set(this.ids[i], i);
84
+ }
85
+ }
57
86
  }
@@ -1,17 +1,10 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { GtkTextViewProps } from "../../jsx.js";
2
3
  import type { Node } from "../../node.js";
3
4
  import type { TextContentChild, TextContentParent } from "../text-content.js";
4
5
  import { TextSegmentNode } from "../text-segment.js";
5
- type BufferCallbackProps = {
6
- onBufferChanged?: ((buffer: Gtk.TextBuffer) => void) | null;
7
- onTextInserted?: ((buffer: Gtk.TextBuffer, offset: number, text: string) => void) | null;
8
- onTextDeleted?: ((buffer: Gtk.TextBuffer, startOffset: number, endOffset: number) => void) | null;
9
- onCanUndoChanged?: ((canUndo: boolean) => void) | null;
10
- onCanRedoChanged?: ((canRedo: boolean) => void) | null;
11
- };
12
- type BufferProps = {
13
- enableUndo?: boolean;
14
- } & BufferCallbackProps;
6
+ type BufferCallbackProps = Pick<GtkTextViewProps, "onBufferChanged" | "onTextInserted" | "onTextDeleted" | "onCanUndoChanged" | "onCanRedoChanged">;
7
+ type BufferProps = Pick<GtkTextViewProps, "enableUndo"> & BufferCallbackProps;
15
8
  export declare class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.TextBuffer> {
16
9
  private readonly owner;
17
10
  private readonly container;
@@ -23,12 +16,11 @@ export declare class TextBufferController<TBuffer extends Gtk.TextBuffer = Gtk.T
23
16
  hasTextChildren(): boolean;
24
17
  ensureBuffer(): TBuffer;
25
18
  applyOwnProps(oldProps: BufferProps | null, newProps: BufferProps): void;
26
- private updateSignalHandlers;
19
+ private setSignalHandlersChanged;
27
20
  isTextContentChild(child: Node): child is TextContentChild;
28
21
  appendChild(child: TextContentChild): void;
29
22
  insertBefore(child: TextContentChild, before: TextContentChild): void;
30
23
  removeChild(child: TextContentChild): void;
31
- private setChildParent;
32
24
  private getTotalLength;
33
25
  private insertTextAtOffset;
34
26
  private deleteTextAtRange;
@@ -3,7 +3,7 @@ import { TextAnchorNode } from "../text-anchor.js";
3
3
  import { TextPaintableNode } from "../text-paintable.js";
4
4
  import { TextSegmentNode } from "../text-segment.js";
5
5
  import { TextTagNode } from "../text-tag.js";
6
- import { hasChanged } from "./utils.js";
6
+ import { hasChanged } from "./props.js";
7
7
  export class TextBufferController {
8
8
  owner;
9
9
  container;
@@ -48,10 +48,10 @@ export class TextBufferController {
48
48
  hasChanged(oldProps, newProps, "onCanUndoChanged") ||
49
49
  hasChanged(oldProps, newProps, "onCanRedoChanged");
50
50
  if (signalHandlersChanged) {
51
- this.updateSignalHandlers(newProps);
51
+ this.setSignalHandlersChanged(newProps);
52
52
  }
53
53
  }
54
- updateSignalHandlers(callbacks) {
54
+ setSignalHandlersChanged(callbacks) {
55
55
  if (!this.buffer)
56
56
  return;
57
57
  const buffer = this.buffer;
@@ -77,7 +77,7 @@ export class TextBufferController {
77
77
  const wasMoved = this.textChildren.indexOf(child) !== -1;
78
78
  if (wasMoved) {
79
79
  const existingIndex = this.textChildren.indexOf(child);
80
- const oldOffset = child.bufferOffset;
80
+ const oldOffset = child.getBufferOffset();
81
81
  const oldLength = child.getLength();
82
82
  this.textChildren.splice(existingIndex, 1);
83
83
  if (oldLength > 0) {
@@ -87,8 +87,7 @@ export class TextBufferController {
87
87
  }
88
88
  const offset = this.getTotalLength();
89
89
  this.textChildren.push(child);
90
- child.bufferOffset = offset;
91
- this.setChildParent(child);
90
+ child.setBufferOffset(offset);
92
91
  if (child instanceof TextSegmentNode) {
93
92
  this.insertTextAtOffset(child.getText(), offset);
94
93
  }
@@ -114,7 +113,7 @@ export class TextBufferController {
114
113
  const buffer = this.ensureBuffer();
115
114
  const existingIndex = this.textChildren.indexOf(child);
116
115
  if (existingIndex !== -1) {
117
- const oldOffset = child.bufferOffset;
116
+ const oldOffset = child.getBufferOffset();
118
117
  const oldLength = child.getLength();
119
118
  this.textChildren.splice(existingIndex, 1);
120
119
  if (oldLength > 0) {
@@ -131,8 +130,7 @@ export class TextBufferController {
131
130
  offset += c.getLength();
132
131
  }
133
132
  this.textChildren.splice(insertIndex, 0, child);
134
- child.bufferOffset = offset;
135
- this.setChildParent(child);
133
+ child.setBufferOffset(offset);
136
134
  if (child instanceof TextSegmentNode) {
137
135
  this.insertTextAtOffset(child.getText(), offset);
138
136
  }
@@ -156,7 +154,7 @@ export class TextBufferController {
156
154
  const index = this.textChildren.indexOf(child);
157
155
  if (index === -1)
158
156
  return;
159
- const offset = child.bufferOffset;
157
+ const offset = child.getBufferOffset();
160
158
  const length = child.getLength();
161
159
  this.textChildren.splice(index, 1);
162
160
  if (this.buffer && length > 0) {
@@ -165,11 +163,6 @@ export class TextBufferController {
165
163
  this.updateChildOffsets(index);
166
164
  this.reapplyTagsFromOffset(offset);
167
165
  }
168
- setChildParent(child) {
169
- if (child instanceof TextSegmentNode || child instanceof TextTagNode) {
170
- child.setParent(this.owner);
171
- }
172
- }
173
166
  getTotalLength() {
174
167
  let length = 0;
175
168
  for (const child of this.textChildren) {
@@ -205,7 +198,7 @@ export class TextBufferController {
205
198
  for (let i = startIndex; i < this.textChildren.length; i++) {
206
199
  const child = this.textChildren[i];
207
200
  if (child) {
208
- child.bufferOffset = offset;
201
+ child.setBufferOffset(offset);
209
202
  offset += child.getLength();
210
203
  }
211
204
  }
@@ -214,20 +207,20 @@ export class TextBufferController {
214
207
  for (const child of children) {
215
208
  if (child instanceof TextTagNode) {
216
209
  child.reapplyTag();
217
- this.reapplyAllTagsRecursive(child.getChildren());
210
+ this.reapplyAllTagsRecursive(child.children);
218
211
  }
219
212
  }
220
213
  }
221
214
  reapplyTagsFromOffset(fromOffset) {
222
215
  for (const child of this.textChildren) {
223
216
  if (child instanceof TextTagNode) {
224
- if (child.bufferOffset >= fromOffset) {
217
+ if (child.getBufferOffset() >= fromOffset) {
225
218
  child.reapplyTag();
226
- this.reapplyAllTagsRecursive(child.getChildren());
219
+ this.reapplyAllTagsRecursive(child.children);
227
220
  }
228
- else if (child.bufferOffset + child.getLength() > fromOffset) {
221
+ else if (child.getBufferOffset() + child.getLength() > fromOffset) {
229
222
  child.reapplyTag();
230
- this.reapplyAllTagsRecursive(child.getChildren());
223
+ this.reapplyAllTagsRecursive(child.children);
231
224
  }
232
225
  }
233
226
  }
@@ -236,7 +229,7 @@ export class TextBufferController {
236
229
  for (let i = 0; i < this.textChildren.length; i++) {
237
230
  const child = this.textChildren[i];
238
231
  if (child) {
239
- const start = child.bufferOffset;
232
+ const start = child.getBufferOffset();
240
233
  const end = start + child.getLength();
241
234
  if (offset >= start && offset <= end) {
242
235
  return i;
@@ -250,18 +243,18 @@ export class TextBufferController {
250
243
  return;
251
244
  const text = child.getText();
252
245
  if (text.length > 0) {
253
- this.insertTextAtOffset(text, child.bufferOffset);
246
+ this.insertTextAtOffset(text, child.getBufferOffset());
254
247
  }
255
- const containingIndex = this.findDirectChildContaining(child.bufferOffset);
248
+ const containingIndex = this.findDirectChildContaining(child.getBufferOffset());
256
249
  if (containingIndex !== -1) {
257
250
  this.updateChildOffsets(containingIndex + 1);
258
251
  }
259
- this.reapplyTagsFromOffset(child.bufferOffset);
252
+ this.reapplyTagsFromOffset(child.getBufferOffset());
260
253
  }
261
254
  onChildRemoved(child) {
262
255
  if (!this.buffer)
263
256
  return;
264
- const offset = child.bufferOffset;
257
+ const offset = child.getBufferOffset();
265
258
  const length = child.getLength();
266
259
  if (length > 0) {
267
260
  this.deleteTextAtRange(offset, offset + length);
@@ -275,13 +268,19 @@ export class TextBufferController {
275
268
  onChildTextChanged(child, oldLength, _newLength) {
276
269
  if (!this.buffer)
277
270
  return;
278
- const offset = child.bufferOffset;
279
- this.deleteTextAtRange(offset, offset + oldLength);
280
- this.insertTextAtOffset(child.getText(), offset);
281
- const containingIndex = this.findDirectChildContaining(offset);
282
- if (containingIndex !== -1) {
283
- this.updateChildOffsets(containingIndex + 1);
271
+ this.owner.signalStore.blockAll();
272
+ try {
273
+ const offset = child.getBufferOffset();
274
+ this.deleteTextAtRange(offset, offset + oldLength);
275
+ this.insertTextAtOffset(child.getText(), offset);
276
+ const containingIndex = this.findDirectChildContaining(offset);
277
+ if (containingIndex !== -1) {
278
+ this.updateChildOffsets(containingIndex + 1);
279
+ }
280
+ this.reapplyTagsFromOffset(offset);
281
+ }
282
+ finally {
283
+ this.owner.signalStore.unblockAll();
284
284
  }
285
- this.reapplyTagsFromOffset(offset);
286
285
  }
287
286
  }
@@ -8,12 +8,17 @@ export type TreeItemData<T = unknown> = {
8
8
  };
9
9
  export declare class TreeStore {
10
10
  private rootIds;
11
+ private rootIdToIndex;
11
12
  private children;
13
+ private childIdToIndex;
12
14
  private rootModel;
13
15
  private childModels;
14
16
  private items;
15
17
  private onItemUpdated;
18
+ private pendingBatch;
16
19
  setOnItemUpdated(callback: TreeItemUpdatedCallback | null): void;
20
+ beginBatch(): void;
21
+ flushBatch(): void;
17
22
  updateItem(id: string, item: TreeItemData): void;
18
23
  addItem(id: string, data: TreeItemData, parentId?: string): void;
19
24
  removeItem(id: string, parentId?: string): void;
@@ -22,4 +27,6 @@ export declare class TreeStore {
22
27
  getRootModel(): Gtk.StringList;
23
28
  getChildrenModel(parentId: string): Gtk.StringList | null;
24
29
  hasChildren(parentId: string): boolean;
30
+ private rebuildRootIndices;
31
+ private rebuildChildIndices;
25
32
  }
@@ -1,14 +1,27 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  export class TreeStore {
3
3
  rootIds = [];
4
+ rootIdToIndex = new Map();
4
5
  children = new Map();
6
+ childIdToIndex = new Map();
5
7
  rootModel = new Gtk.StringList();
6
8
  childModels = new Map();
7
9
  items = new Map();
8
10
  onItemUpdated = null;
11
+ pendingBatch = null;
9
12
  setOnItemUpdated(callback) {
10
13
  this.onItemUpdated = callback;
11
14
  }
15
+ beginBatch() {
16
+ this.pendingBatch = [];
17
+ }
18
+ flushBatch() {
19
+ const batch = this.pendingBatch;
20
+ this.pendingBatch = null;
21
+ if (batch && batch.length > 0) {
22
+ this.rootModel.splice(0, 0, batch);
23
+ }
24
+ }
12
25
  updateItem(id, item) {
13
26
  if (this.items.has(id)) {
14
27
  this.items.set(id, item);
@@ -21,28 +34,42 @@ export class TreeStore {
21
34
  addItem(id, data, parentId) {
22
35
  this.items.set(id, data);
23
36
  if (parentId === undefined) {
24
- const existingIndex = this.rootIds.indexOf(id);
25
- if (existingIndex >= 0) {
37
+ const existingIndex = this.rootIdToIndex.get(id);
38
+ if (existingIndex !== undefined) {
26
39
  this.rootModel.remove(existingIndex);
27
40
  this.rootIds.splice(existingIndex, 1);
41
+ this.rebuildRootIndices(existingIndex);
28
42
  }
43
+ this.rootIdToIndex.set(id, this.rootIds.length);
29
44
  this.rootIds.push(id);
30
- this.rootModel.append(id);
45
+ if (this.pendingBatch) {
46
+ this.pendingBatch.push(id);
47
+ }
48
+ else {
49
+ this.rootModel.append(id);
50
+ }
31
51
  }
32
52
  else {
33
53
  let siblings = this.children.get(parentId);
54
+ let indexMap = this.childIdToIndex.get(parentId);
34
55
  if (!siblings) {
35
56
  siblings = [];
36
57
  this.children.set(parentId, siblings);
37
58
  }
38
- const existingIndex = siblings.indexOf(id);
39
- if (existingIndex >= 0) {
59
+ if (!indexMap) {
60
+ indexMap = new Map();
61
+ this.childIdToIndex.set(parentId, indexMap);
62
+ }
63
+ const existingIndex = indexMap.get(id);
64
+ if (existingIndex !== undefined) {
40
65
  const model = this.childModels.get(parentId);
41
66
  if (model) {
42
67
  model.remove(existingIndex);
43
68
  }
44
69
  siblings.splice(existingIndex, 1);
70
+ this.rebuildChildIndices(siblings, indexMap, existingIndex);
45
71
  }
72
+ indexMap.set(id, siblings.length);
46
73
  siblings.push(id);
47
74
  let model = this.childModels.get(parentId);
48
75
  if (!model) {
@@ -55,20 +82,26 @@ export class TreeStore {
55
82
  removeItem(id, parentId) {
56
83
  this.items.delete(id);
57
84
  this.children.delete(id);
85
+ this.childIdToIndex.delete(id);
58
86
  this.childModels.delete(id);
59
87
  if (parentId === undefined) {
60
- const index = this.rootIds.indexOf(id);
61
- if (index >= 0) {
88
+ const index = this.rootIdToIndex.get(id);
89
+ if (index !== undefined) {
62
90
  this.rootIds.splice(index, 1);
91
+ this.rootIdToIndex.delete(id);
92
+ this.rebuildRootIndices(index);
63
93
  this.rootModel.remove(index);
64
94
  }
65
95
  }
66
96
  else {
67
97
  const siblings = this.children.get(parentId);
68
- if (siblings) {
69
- const index = siblings.indexOf(id);
70
- if (index >= 0) {
98
+ const indexMap = this.childIdToIndex.get(parentId);
99
+ if (siblings && indexMap) {
100
+ const index = indexMap.get(id);
101
+ if (index !== undefined) {
71
102
  siblings.splice(index, 1);
103
+ indexMap.delete(id);
104
+ this.rebuildChildIndices(siblings, indexMap, index);
72
105
  const model = this.childModels.get(parentId);
73
106
  if (model) {
74
107
  model.remove(index);
@@ -76,6 +109,7 @@ export class TreeStore {
76
109
  }
77
110
  if (siblings.length === 0) {
78
111
  this.children.delete(parentId);
112
+ this.childIdToIndex.delete(parentId);
79
113
  }
80
114
  }
81
115
  }
@@ -83,44 +117,57 @@ export class TreeStore {
83
117
  insertItemBefore(id, beforeId, data, parentId) {
84
118
  this.items.set(id, data);
85
119
  if (parentId === undefined) {
86
- const existingIndex = this.rootIds.indexOf(id);
87
- if (existingIndex >= 0) {
120
+ const existingIndex = this.rootIdToIndex.get(id);
121
+ if (existingIndex !== undefined) {
88
122
  this.rootModel.remove(existingIndex);
89
123
  this.rootIds.splice(existingIndex, 1);
124
+ this.rootIdToIndex.delete(id);
125
+ this.rebuildRootIndices(existingIndex);
90
126
  }
91
- const beforeIndex = this.rootIds.indexOf(beforeId);
92
- if (beforeIndex < 0) {
127
+ const beforeIndex = this.rootIdToIndex.get(beforeId);
128
+ if (beforeIndex === undefined) {
129
+ this.rootIdToIndex.set(id, this.rootIds.length);
93
130
  this.rootIds.push(id);
94
131
  this.rootModel.append(id);
95
132
  }
96
133
  else {
97
134
  this.rootIds.splice(beforeIndex, 0, id);
135
+ this.rebuildRootIndices(beforeIndex);
98
136
  this.rootModel.splice(beforeIndex, 0, [id]);
99
137
  }
100
138
  }
101
139
  else {
102
140
  let siblings = this.children.get(parentId);
141
+ let indexMap = this.childIdToIndex.get(parentId);
103
142
  if (!siblings) {
104
143
  siblings = [];
105
144
  this.children.set(parentId, siblings);
106
145
  }
146
+ if (!indexMap) {
147
+ indexMap = new Map();
148
+ this.childIdToIndex.set(parentId, indexMap);
149
+ }
107
150
  let model = this.childModels.get(parentId);
108
151
  if (!model) {
109
152
  model = new Gtk.StringList();
110
153
  this.childModels.set(parentId, model);
111
154
  }
112
- const existingIndex = siblings.indexOf(id);
113
- if (existingIndex >= 0) {
155
+ const existingIndex = indexMap.get(id);
156
+ if (existingIndex !== undefined) {
114
157
  model.remove(existingIndex);
115
158
  siblings.splice(existingIndex, 1);
159
+ indexMap.delete(id);
160
+ this.rebuildChildIndices(siblings, indexMap, existingIndex);
116
161
  }
117
- const beforeIndex = siblings.indexOf(beforeId);
118
- if (beforeIndex < 0) {
162
+ const beforeIndex = indexMap.get(beforeId);
163
+ if (beforeIndex === undefined) {
164
+ indexMap.set(id, siblings.length);
119
165
  siblings.push(id);
120
166
  model.append(id);
121
167
  }
122
168
  else {
123
169
  siblings.splice(beforeIndex, 0, id);
170
+ this.rebuildChildIndices(siblings, indexMap, beforeIndex);
124
171
  model.splice(beforeIndex, 0, [id]);
125
172
  }
126
173
  }
@@ -147,4 +194,14 @@ export class TreeStore {
147
194
  const childIds = this.children.get(parentId);
148
195
  return childIds !== undefined && childIds.length > 0;
149
196
  }
197
+ rebuildRootIndices(fromIndex) {
198
+ for (let i = fromIndex; i < this.rootIds.length; i++) {
199
+ this.rootIdToIndex.set(this.rootIds[i], i);
200
+ }
201
+ }
202
+ rebuildChildIndices(siblings, indexMap, fromIndex) {
203
+ for (let i = fromIndex; i < siblings.length; i++) {
204
+ indexMap.set(siblings[i], i);
205
+ }
206
+ }
150
207
  }
@@ -0,0 +1,7 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ export declare function detachChild(child: Gtk.Widget, container: Gtk.Widget): void;
3
+ export declare function attachChild(child: Gtk.Widget, container: Gtk.Widget): void;
4
+ export declare function isAttachedTo(child: Gtk.Widget | null, parent: Gtk.Widget | null): boolean;
5
+ export declare function getFocusWidget(widget: Gtk.Widget): Gtk.Widget | null;
6
+ export declare function isDescendantOf(widget: Gtk.Widget, ancestor: Gtk.Widget): boolean;
7
+ export declare function resolvePropertySetter(parentWidget: Gtk.Widget, propId: string): ((child: Gtk.Widget | null) => void) | null;
@@ -0,0 +1,68 @@
1
+ import { resolvePropMeta } from "../../metadata.js";
2
+ import { isAddable, isAppendable, isContentWidget, isRemovable, isSingleChild } from "./predicates.js";
3
+ export function detachChild(child, container) {
4
+ if (isAppendable(container) || isAddable(container)) {
5
+ if (isRemovable(container)) {
6
+ container.remove(child);
7
+ }
8
+ }
9
+ else if (isContentWidget(container)) {
10
+ container.setContent(null);
11
+ }
12
+ else if (isSingleChild(container)) {
13
+ container.setChild(null);
14
+ }
15
+ else if (isRemovable(container)) {
16
+ container.remove(child);
17
+ }
18
+ }
19
+ export function attachChild(child, container) {
20
+ if (isAppendable(container)) {
21
+ container.append(child);
22
+ }
23
+ else if (isAddable(container)) {
24
+ container.add(child);
25
+ }
26
+ else if (isContentWidget(container)) {
27
+ container.setContent(child);
28
+ }
29
+ else if (isSingleChild(container)) {
30
+ container.setChild(child);
31
+ }
32
+ else {
33
+ throw new Error(`Cannot attach child to '${container.constructor.name}': container does not support children`);
34
+ }
35
+ }
36
+ export function isAttachedTo(child, parent) {
37
+ if (!child || !parent)
38
+ return false;
39
+ const childParent = child.getParent();
40
+ return childParent !== null && childParent === parent;
41
+ }
42
+ export function getFocusWidget(widget) {
43
+ const root = widget.getRoot();
44
+ return root?.getFocus?.() ?? null;
45
+ }
46
+ export function isDescendantOf(widget, ancestor) {
47
+ let current = widget;
48
+ while (current) {
49
+ if (current === ancestor) {
50
+ return true;
51
+ }
52
+ current = current.getParent();
53
+ }
54
+ return false;
55
+ }
56
+ export function resolvePropertySetter(parentWidget, propId) {
57
+ const propMeta = resolvePropMeta(parentWidget, propId);
58
+ if (!propMeta) {
59
+ return null;
60
+ }
61
+ const [, setterName] = propMeta;
62
+ const setter = parentWidget[setterName];
63
+ if (typeof setter !== "function") {
64
+ const parentType = parentWidget.constructor.glibTypeName;
65
+ throw new Error(`Expected setter function for property '${propId}' on type '${parentType}'`);
66
+ }
67
+ return setter.bind(parentWidget);
68
+ }
@@ -1 +1,11 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { GtkLevelBarProps } from "../jsx.js";
3
+ import { WidgetNode } from "./widget.js";
4
+ declare const OWN_PROPS: readonly ["offsets"];
5
+ type LevelBarProps = Pick<GtkLevelBarProps, (typeof OWN_PROPS)[number]>;
6
+ export declare class LevelBarNode extends WidgetNode<Gtk.LevelBar, LevelBarProps> {
7
+ private appliedOffsets;
8
+ commitUpdate(oldProps: LevelBarProps | null, newProps: LevelBarProps): void;
9
+ private applyOwnProps;
10
+ }
1
11
  export {};