@gtkx/react 0.15.0 → 0.16.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 (186) hide show
  1. package/README.md +1 -0
  2. package/dist/errors.js +3 -0
  3. package/dist/factory.d.ts +3 -2
  4. package/dist/factory.js +1 -1
  5. package/dist/generated/internal.d.ts +28 -1
  6. package/dist/generated/internal.js +93 -18
  7. package/dist/generated/jsx.d.ts +1489 -1300
  8. package/dist/generated/jsx.js +475 -0
  9. package/dist/host-config.d.ts +3 -1
  10. package/dist/host-config.js +31 -11
  11. package/dist/jsx.d.ts +107 -166
  12. package/dist/jsx.js +58 -69
  13. package/dist/node.d.ts +3 -1
  14. package/dist/node.js +5 -3
  15. package/dist/nodes/abstract/positional-child.d.ts +9 -0
  16. package/dist/nodes/abstract/positional-child.js +29 -0
  17. package/dist/nodes/abstract/positional-parent.d.ts +18 -0
  18. package/dist/nodes/abstract/positional-parent.js +48 -0
  19. package/dist/nodes/abstract/virtual-container.d.ts +17 -0
  20. package/dist/nodes/abstract/virtual-container.js +59 -0
  21. package/dist/nodes/abstract/virtual-single-child.d.ts +18 -0
  22. package/dist/nodes/abstract/virtual-single-child.js +54 -0
  23. package/dist/nodes/action-row-child.d.ts +0 -13
  24. package/dist/nodes/action-row-child.js +14 -12
  25. package/dist/nodes/action-row.d.ts +6 -1
  26. package/dist/nodes/action-row.js +4 -37
  27. package/dist/nodes/adjustable.d.ts +23 -0
  28. package/dist/nodes/adjustable.js +62 -0
  29. package/dist/nodes/alert-dialog-response.d.ts +1 -0
  30. package/dist/nodes/alert-dialog-response.js +86 -0
  31. package/dist/nodes/animation/animation-controller.d.ts +17 -0
  32. package/dist/nodes/animation/animation-controller.js +107 -0
  33. package/dist/nodes/animation/animation-factory.d.ts +15 -0
  34. package/dist/nodes/animation/animation-factory.js +25 -0
  35. package/dist/nodes/animation/animation-node.d.ts +9 -0
  36. package/dist/nodes/animation/animation-node.js +126 -0
  37. package/dist/nodes/animation/animation-style-sheet.d.ts +16 -0
  38. package/dist/nodes/animation/animation-style-sheet.js +74 -0
  39. package/dist/nodes/animation/index.d.ts +4 -0
  40. package/dist/nodes/animation/index.js +1 -0
  41. package/dist/nodes/animation/property-mapper.d.ts +11 -0
  42. package/dist/nodes/animation/property-mapper.js +36 -0
  43. package/dist/nodes/animation/transform-state.d.ts +11 -0
  44. package/dist/nodes/animation/transform-state.js +57 -0
  45. package/dist/nodes/animation/widget-registry.d.ts +5 -0
  46. package/dist/nodes/animation/widget-registry.js +42 -0
  47. package/dist/nodes/application.js +17 -7
  48. package/dist/nodes/autowrapped.js +37 -43
  49. package/dist/nodes/calendar.js +17 -43
  50. package/dist/nodes/color-dialog-button.d.ts +1 -0
  51. package/dist/nodes/color-dialog-button.js +70 -0
  52. package/dist/nodes/column-view-column.d.ts +3 -3
  53. package/dist/nodes/column-view-column.js +1 -1
  54. package/dist/nodes/column-view.js +36 -39
  55. package/dist/nodes/dialog.d.ts +11 -0
  56. package/dist/nodes/dialog.js +20 -0
  57. package/dist/nodes/drawing-area.js +24 -7
  58. package/dist/nodes/event-controller.d.ts +1 -0
  59. package/dist/nodes/event-controller.js +96 -0
  60. package/dist/nodes/expander-row-child.d.ts +0 -14
  61. package/dist/nodes/expander-row-child.js +14 -12
  62. package/dist/nodes/expander-row.d.ts +6 -1
  63. package/dist/nodes/expander-row.js +11 -48
  64. package/dist/nodes/fixed-child.js +48 -36
  65. package/dist/nodes/font-dialog-button.d.ts +1 -0
  66. package/dist/nodes/font-dialog-button.js +90 -0
  67. package/dist/nodes/grid-child.js +39 -45
  68. package/dist/nodes/grid.d.ts +1 -0
  69. package/dist/nodes/grid.js +41 -0
  70. package/dist/nodes/index.d.ts +17 -9
  71. package/dist/nodes/index.js +17 -9
  72. package/dist/nodes/internal/base-item-renderer.d.ts +29 -0
  73. package/dist/nodes/internal/base-item-renderer.js +88 -0
  74. package/dist/nodes/internal/base-store.d.ts +9 -0
  75. package/dist/nodes/internal/base-store.js +20 -0
  76. package/dist/nodes/internal/child-attachment.d.ts +26 -0
  77. package/dist/nodes/internal/child-attachment.js +48 -0
  78. package/dist/nodes/internal/deferred-action.d.ts +8 -0
  79. package/dist/nodes/internal/deferred-action.js +19 -0
  80. package/dist/nodes/internal/list-item-renderer.d.ts +14 -15
  81. package/dist/nodes/internal/list-item-renderer.js +51 -77
  82. package/dist/nodes/internal/list-store.d.ts +7 -6
  83. package/dist/nodes/internal/list-store.js +20 -24
  84. package/dist/nodes/internal/predicates.d.ts +25 -2
  85. package/dist/nodes/internal/predicates.js +53 -41
  86. package/dist/nodes/internal/selection-model.d.ts +30 -0
  87. package/dist/nodes/internal/selection-model.js +91 -0
  88. package/dist/nodes/internal/signal-store.d.ts +5 -4
  89. package/dist/nodes/internal/signal-store.js +30 -28
  90. package/dist/nodes/internal/simple-list-store.js +6 -9
  91. package/dist/nodes/internal/text-buffer-controller.d.ts +43 -0
  92. package/dist/nodes/internal/text-buffer-controller.js +287 -0
  93. package/dist/nodes/internal/text-tag-styles.d.ts +43 -0
  94. package/dist/nodes/internal/text-tag-styles.js +52 -0
  95. package/dist/nodes/internal/tree-list-item-renderer.d.ts +15 -14
  96. package/dist/nodes/internal/tree-list-item-renderer.js +85 -96
  97. package/dist/nodes/internal/tree-store.d.ts +10 -9
  98. package/dist/nodes/internal/tree-store.js +31 -35
  99. package/dist/nodes/internal/utils.d.ts +7 -4
  100. package/dist/nodes/internal/utils.js +50 -5
  101. package/dist/nodes/level-bar.js +19 -54
  102. package/dist/nodes/list-item.d.ts +6 -3
  103. package/dist/nodes/list-item.js +7 -4
  104. package/dist/nodes/list-view.js +15 -11
  105. package/dist/nodes/menu.d.ts +3 -3
  106. package/dist/nodes/menu.js +3 -3
  107. package/dist/nodes/models/list.d.ts +11 -13
  108. package/dist/nodes/models/list.js +16 -73
  109. package/dist/nodes/models/menu.d.ts +8 -7
  110. package/dist/nodes/models/menu.js +43 -50
  111. package/dist/nodes/models/tree-list.d.ts +6 -12
  112. package/dist/nodes/models/tree-list.js +30 -93
  113. package/dist/nodes/navigation-page.d.ts +1 -0
  114. package/dist/nodes/navigation-page.js +27 -32
  115. package/dist/nodes/navigation-view.js +17 -28
  116. package/dist/nodes/notebook-page-tab.d.ts +3 -3
  117. package/dist/nodes/notebook-page-tab.js +11 -14
  118. package/dist/nodes/notebook-page.d.ts +7 -5
  119. package/dist/nodes/notebook-page.js +45 -25
  120. package/dist/nodes/notebook.js +2 -2
  121. package/dist/nodes/overlay-child.js +90 -30
  122. package/dist/nodes/pack-child.d.ts +0 -13
  123. package/dist/nodes/pack-child.js +14 -12
  124. package/dist/nodes/pack.d.ts +6 -1
  125. package/dist/nodes/pack.js +4 -37
  126. package/dist/nodes/popover-menu.js +2 -2
  127. package/dist/nodes/scale.js +15 -45
  128. package/dist/nodes/scrolled-window.js +7 -6
  129. package/dist/nodes/search-bar.d.ts +1 -0
  130. package/dist/nodes/search-bar.js +40 -0
  131. package/dist/nodes/shortcut-controller.d.ts +1 -37
  132. package/dist/nodes/shortcut-controller.js +24 -8
  133. package/dist/nodes/shortcut.d.ts +5 -4
  134. package/dist/nodes/shortcut.js +11 -5
  135. package/dist/nodes/simple-list-view.js +2 -3
  136. package/dist/nodes/slot.d.ts +6 -9
  137. package/dist/nodes/slot.js +27 -42
  138. package/dist/nodes/source-view.js +80 -29
  139. package/dist/nodes/stack-page.js +20 -22
  140. package/dist/nodes/stack.js +19 -5
  141. package/dist/nodes/text-anchor.d.ts +41 -0
  142. package/dist/nodes/text-anchor.js +59 -0
  143. package/dist/nodes/text-content.d.ts +10 -0
  144. package/dist/nodes/text-content.js +1 -0
  145. package/dist/nodes/text-paintable.d.ts +17 -0
  146. package/dist/nodes/text-paintable.js +34 -0
  147. package/dist/nodes/text-segment.d.ts +15 -0
  148. package/dist/nodes/text-segment.js +29 -0
  149. package/dist/nodes/text-tag.d.ts +136 -0
  150. package/dist/nodes/text-tag.js +202 -0
  151. package/dist/nodes/text-view.d.ts +30 -0
  152. package/dist/nodes/text-view.js +49 -21
  153. package/dist/nodes/toggle-group.js +24 -32
  154. package/dist/nodes/toggle.d.ts +1 -15
  155. package/dist/nodes/toggle.js +40 -32
  156. package/dist/nodes/toolbar-child.js +14 -16
  157. package/dist/nodes/tree-list-item.d.ts +7 -5
  158. package/dist/nodes/tree-list-item.js +24 -36
  159. package/dist/nodes/tree-list-view.js +7 -6
  160. package/dist/nodes/virtual.d.ts +1 -1
  161. package/dist/nodes/widget.d.ts +2 -16
  162. package/dist/nodes/widget.js +105 -294
  163. package/dist/nodes/window.d.ts +9 -3
  164. package/dist/nodes/window.js +29 -15
  165. package/dist/registry.d.ts +1 -1
  166. package/dist/render.js +8 -6
  167. package/dist/scheduler.d.ts +11 -1
  168. package/dist/scheduler.js +16 -4
  169. package/dist/types.d.ts +2 -136
  170. package/package.json +3 -3
  171. package/dist/nodes/adjustment.d.ts +0 -48
  172. package/dist/nodes/adjustment.js +0 -70
  173. package/dist/nodes/calendar-mark.d.ts +0 -15
  174. package/dist/nodes/calendar-mark.js +0 -29
  175. package/dist/nodes/internal/constants.d.ts +0 -1
  176. package/dist/nodes/internal/constants.js +0 -24
  177. package/dist/nodes/level-bar-offset.d.ts +0 -13
  178. package/dist/nodes/level-bar-offset.js +0 -35
  179. package/dist/nodes/scale-mark.d.ts +0 -17
  180. package/dist/nodes/scale-mark.js +0 -38
  181. package/dist/nodes/source-buffer.d.ts +0 -73
  182. package/dist/nodes/source-buffer.js +0 -149
  183. package/dist/nodes/text-buffer.d.ts +0 -43
  184. package/dist/nodes/text-buffer.js +0 -81
  185. package/dist/nodes/virtual-child.d.ts +0 -18
  186. package/dist/nodes/virtual-child.js +0 -62
@@ -1,122 +1,109 @@
1
- import { getNativeId } from "@gtkx/ffi";
2
1
  import * as Gtk from "@gtkx/ffi/gtk";
3
- import { createFiberRoot } from "../../fiber-root.js";
4
2
  import { reconciler } from "../../reconciler.js";
5
- import { signalStore } from "./signal-store.js";
6
- export class TreeListItemRenderer {
7
- factory;
8
- store;
9
- fiberRoots = new Map();
3
+ import { BaseItemRenderer } from "./base-item-renderer.js";
4
+ export class TreeListItemRenderer extends BaseItemRenderer {
10
5
  expanders = new Map();
11
6
  setupComplete = new Set();
12
7
  pendingBinds = new Map();
13
- tornDown = new Set();
14
8
  renderFn = () => null;
15
- estimatedItemHeight;
16
- constructor() {
17
- this.factory = new Gtk.SignalListItemFactory();
18
- this.initialize();
19
- }
20
- getFactory() {
21
- return this.factory;
22
- }
9
+ boundItems = new Map();
23
10
  setRenderFn(renderFn) {
24
11
  this.renderFn = renderFn;
25
12
  }
26
- setStore(store) {
27
- this.store = store;
28
- }
29
- setEstimatedItemHeight(height) {
30
- this.estimatedItemHeight = height;
13
+ rebindItem(id) {
14
+ const binding = this.boundItems.get(id);
15
+ if (!binding)
16
+ return;
17
+ const fiberRoot = this.fiberRoots.get(binding.ptr);
18
+ if (!fiberRoot)
19
+ return;
20
+ const expander = this.expanders.get(binding.ptr);
21
+ if (!expander)
22
+ return;
23
+ this.renderBind(binding.ptr, expander, binding.treeListRow, id, fiberRoot);
31
24
  }
32
- getStore() {
33
- if (!this.store) {
34
- throw new Error("Expected tree store to be set on TreeListItemRenderer");
35
- }
36
- return this.store;
25
+ getStoreTypeName() {
26
+ return "tree store";
37
27
  }
38
28
  dispose() {
39
- signalStore.clear(this);
40
- this.fiberRoots.clear();
29
+ super.dispose();
41
30
  this.expanders.clear();
42
31
  this.setupComplete.clear();
43
32
  this.pendingBinds.clear();
44
- this.tornDown.clear();
33
+ this.boundItems.clear();
45
34
  }
46
- initialize() {
47
- signalStore.set(this, this.factory, "setup", (_self, listItem) => {
48
- const ptr = getNativeId(listItem.handle);
49
- const expander = new Gtk.TreeExpander();
50
- const box = new Gtk.Box(Gtk.Orientation.VERTICAL);
51
- if (this.estimatedItemHeight !== undefined) {
52
- box.setSizeRequest(-1, this.estimatedItemHeight);
53
- }
54
- expander.setChild(box);
55
- listItem.setChild(expander);
56
- const fiberRoot = createFiberRoot(box);
57
- this.fiberRoots.set(ptr, fiberRoot);
58
- this.expanders.set(ptr, expander);
59
- const element = this.renderFn?.(null, null);
60
- reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
61
- if (this.tornDown.has(ptr))
62
- return;
63
- this.setupComplete.add(ptr);
64
- this.processPendingBind(ptr);
65
- });
66
- });
67
- signalStore.set(this, this.factory, "bind", (_self, listItem) => {
68
- const ptr = getNativeId(listItem.handle);
69
- const fiberRoot = this.fiberRoots.get(ptr);
70
- const expander = this.expanders.get(ptr);
71
- if (!fiberRoot || !expander)
72
- return;
35
+ renderItem(_ptr) {
36
+ return this.renderFn?.(null, null);
37
+ }
38
+ getItemFromListItem(listItem) {
39
+ const treeListRow = listItem.getItem();
40
+ if (!(treeListRow instanceof Gtk.TreeListRow))
41
+ return null;
42
+ const stringObject = treeListRow.getItem();
43
+ if (!(stringObject instanceof Gtk.StringObject))
44
+ return null;
45
+ return this.getStore().getItem(stringObject.getString());
46
+ }
47
+ onSetup(listItem, ptr) {
48
+ const expander = new Gtk.TreeExpander();
49
+ const box = this.createBox();
50
+ expander.setChild(box);
51
+ listItem.setChild(expander);
52
+ this.expanders.set(ptr, expander);
53
+ return box;
54
+ }
55
+ onSetupComplete(ptr) {
56
+ this.setupComplete.add(ptr);
57
+ this.processPendingBind(ptr);
58
+ }
59
+ onBind(listItem, ptr, fiberRoot) {
60
+ const expander = this.expanders.get(ptr);
61
+ if (!expander)
62
+ return;
63
+ const treeListRow = listItem.getItem();
64
+ if (!(treeListRow instanceof Gtk.TreeListRow))
65
+ return;
66
+ expander.setListRow(treeListRow);
67
+ const stringObject = treeListRow.getItem();
68
+ if (!(stringObject instanceof Gtk.StringObject))
69
+ return;
70
+ const id = stringObject.getString();
71
+ this.boundItems.set(id, { ptr, treeListRow });
72
+ if (!this.setupComplete.has(ptr)) {
73
+ this.pendingBinds.set(ptr, { treeListRow, expander, id });
74
+ return;
75
+ }
76
+ this.renderBind(ptr, expander, treeListRow, id, fiberRoot);
77
+ }
78
+ onUnbind(listItem) {
79
+ const expander = listItem.getChild();
80
+ if (expander instanceof Gtk.TreeExpander) {
81
+ expander.setListRow(null);
73
82
  const treeListRow = listItem.getItem();
74
- if (!(treeListRow instanceof Gtk.TreeListRow))
75
- return;
76
- expander.setListRow(treeListRow);
77
- const stringObject = treeListRow.getItem();
78
- if (!(stringObject instanceof Gtk.StringObject))
79
- return;
80
- const id = stringObject.getString();
81
- if (!this.setupComplete.has(ptr)) {
82
- this.pendingBinds.set(ptr, { treeListRow, expander, id });
83
- return;
83
+ if (treeListRow instanceof Gtk.TreeListRow) {
84
+ const stringObject = treeListRow.getItem();
85
+ if (stringObject instanceof Gtk.StringObject) {
86
+ this.boundItems.delete(stringObject.getString());
87
+ }
84
88
  }
85
- this.renderBind(ptr, expander, treeListRow, id);
86
- });
87
- signalStore.set(this, this.factory, "unbind", (_self, listItem) => {
88
- const expander = listItem.getChild();
89
- if (expander instanceof Gtk.TreeExpander) {
90
- expander.setListRow(null);
91
- }
92
- });
93
- signalStore.set(this, this.factory, "teardown", (_self, listItem) => {
94
- const ptr = getNativeId(listItem.handle);
95
- const fiberRoot = this.fiberRoots.get(ptr);
96
- if (fiberRoot) {
97
- this.tornDown.add(ptr);
98
- reconciler.getInstance().updateContainer(null, fiberRoot, null, () => { });
99
- queueMicrotask(() => {
100
- this.fiberRoots.delete(ptr);
101
- this.expanders.delete(ptr);
102
- this.setupComplete.delete(ptr);
103
- this.pendingBinds.delete(ptr);
104
- this.tornDown.delete(ptr);
105
- });
106
- }
107
- });
89
+ }
90
+ }
91
+ onTeardown(_listItem, ptr) {
92
+ this.expanders.delete(ptr);
93
+ this.setupComplete.delete(ptr);
94
+ this.pendingBinds.delete(ptr);
108
95
  }
109
96
  processPendingBind(ptr) {
110
97
  const pending = this.pendingBinds.get(ptr);
111
98
  if (!pending)
112
99
  return;
113
100
  this.pendingBinds.delete(ptr);
114
- this.renderBind(ptr, pending.expander, pending.treeListRow, pending.id);
115
- }
116
- renderBind(ptr, expander, treeListRow, id) {
117
101
  const fiberRoot = this.fiberRoots.get(ptr);
118
- if (!fiberRoot)
119
- return;
102
+ if (fiberRoot) {
103
+ this.renderBind(ptr, pending.expander, pending.treeListRow, pending.id, fiberRoot);
104
+ }
105
+ }
106
+ renderBind(ptr, expander, treeListRow, id, fiberRoot) {
120
107
  const itemData = this.getStore().getItem(id);
121
108
  if (itemData) {
122
109
  if (itemData.indentForDepth !== undefined) {
@@ -133,12 +120,14 @@ export class TreeListItemRenderer {
133
120
  reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
134
121
  if (this.tornDown.has(ptr))
135
122
  return;
123
+ if (this.estimatedItemHeight !== null)
124
+ return;
136
125
  const currentExpander = this.expanders.get(ptr);
137
126
  if (!currentExpander)
138
127
  return;
139
128
  const box = currentExpander.getChild();
140
- if (box instanceof Gtk.Box) {
141
- box.setSizeRequest(-1, -1);
129
+ if (box) {
130
+ this.clearBoxSizeRequest(box);
142
131
  }
143
132
  });
144
133
  }
@@ -1,28 +1,29 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
- export interface TreeItemData<T = unknown> {
2
+ import { BaseStore } from "./base-store.js";
3
+ export type TreeItemUpdatedCallback = (id: string) => void;
4
+ export type TreeItemData<T = unknown> = {
3
5
  value: T;
4
6
  indentForDepth?: boolean;
5
7
  indentForIcon?: boolean;
6
8
  hideExpander?: boolean;
7
- }
8
- export declare class TreeStore {
9
- private items;
9
+ };
10
+ export declare class TreeStore extends BaseStore<TreeItemData> {
10
11
  private rootIds;
11
12
  private newRootIds;
12
13
  private children;
13
14
  private newChildren;
14
15
  private rootModel;
15
16
  private childModels;
16
- private shouldSync;
17
+ private onItemUpdated;
17
18
  constructor();
19
+ setOnItemUpdated(callback: TreeItemUpdatedCallback | null): void;
20
+ updateItem(id: string, item: TreeItemData): void;
18
21
  addItem(id: string, data: TreeItemData, parentId?: string): void;
19
22
  removeItem(id: string, parentId?: string): void;
20
23
  insertItemBefore(id: string, beforeId: string, data: TreeItemData, parentId?: string): void;
21
- updateItem(id: string, data: TreeItemData): void;
22
- getItem(id: string): TreeItemData | null;
24
+ getItem(id: string): TreeItemData | undefined;
23
25
  getRootModel(): Gtk.StringList;
24
26
  getChildrenModel(parentId: string): Gtk.StringList | null;
25
27
  hasChildren(parentId: string): boolean;
26
- private scheduleSync;
27
- private sync;
28
+ protected sync(): void;
28
29
  }
@@ -1,18 +1,29 @@
1
- import { batch } from "@gtkx/ffi";
2
1
  import * as Gtk from "@gtkx/ffi/gtk";
3
- import { CommitPriority, scheduleAfterCommit } from "../../scheduler.js";
4
- export class TreeStore {
5
- items = new Map();
2
+ import { BaseStore } from "./base-store.js";
3
+ export class TreeStore extends BaseStore {
6
4
  rootIds = [];
7
5
  newRootIds = [];
8
6
  children = new Map();
9
7
  newChildren = new Map();
10
8
  rootModel;
11
9
  childModels = new Map();
12
- shouldSync = false;
10
+ onItemUpdated = null;
13
11
  constructor() {
12
+ super();
14
13
  this.rootModel = new Gtk.StringList();
15
14
  }
15
+ setOnItemUpdated(callback) {
16
+ this.onItemUpdated = callback;
17
+ }
18
+ updateItem(id, item) {
19
+ if (this.items.has(id)) {
20
+ this.items.set(id, item);
21
+ this.onItemUpdated?.(id);
22
+ }
23
+ else {
24
+ this.addItem(id, item);
25
+ }
26
+ }
16
27
  addItem(id, data, parentId) {
17
28
  this.items.set(id, data);
18
29
  if (parentId === undefined) {
@@ -94,13 +105,8 @@ export class TreeStore {
94
105
  }
95
106
  this.scheduleSync();
96
107
  }
97
- updateItem(id, data) {
98
- if (this.items.has(id)) {
99
- this.items.set(id, data);
100
- }
101
- }
102
108
  getItem(id) {
103
- return this.items.get(id) ?? null;
109
+ return this.items.get(id);
104
110
  }
105
111
  getRootModel() {
106
112
  return this.rootModel;
@@ -121,32 +127,22 @@ export class TreeStore {
121
127
  const childIds = this.children.get(parentId);
122
128
  return childIds !== undefined && childIds.length > 0;
123
129
  }
124
- scheduleSync() {
125
- if (this.shouldSync) {
126
- return;
127
- }
128
- this.shouldSync = true;
129
- scheduleAfterCommit(() => this.sync(), CommitPriority.LOW);
130
- }
131
130
  sync() {
132
- this.shouldSync = false;
133
- batch(() => {
134
- const oldRootLength = this.rootIds.length;
135
- this.rootModel.splice(0, oldRootLength, this.newRootIds.length > 0 ? this.newRootIds : undefined);
136
- this.rootIds = [...this.newRootIds];
137
- for (const [parentId, newChildIds] of this.newChildren) {
138
- const model = this.childModels.get(parentId);
139
- if (model) {
140
- const oldLength = model.getNItems();
141
- model.splice(0, oldLength, newChildIds.length > 0 ? newChildIds : undefined);
142
- }
131
+ const oldRootLength = this.rootIds.length;
132
+ this.rootModel.splice(0, oldRootLength, this.newRootIds.length > 0 ? this.newRootIds : undefined);
133
+ this.rootIds = [...this.newRootIds];
134
+ for (const [parentId, newChildIds] of this.newChildren) {
135
+ const model = this.childModels.get(parentId);
136
+ if (model) {
137
+ const oldLength = model.getNItems();
138
+ model.splice(0, oldLength, newChildIds.length > 0 ? newChildIds : undefined);
143
139
  }
144
- for (const [parentId] of this.children) {
145
- if (!this.newChildren.has(parentId)) {
146
- this.childModels.delete(parentId);
147
- }
140
+ }
141
+ for (const [parentId] of this.children) {
142
+ if (!this.newChildren.has(parentId)) {
143
+ this.childModels.delete(parentId);
148
144
  }
149
- this.children = new Map(this.newChildren);
150
- });
145
+ }
146
+ this.children = new Map(this.newChildren);
151
147
  }
152
148
  }
@@ -1,9 +1,12 @@
1
1
  import type { Container, ContainerClass, Props } from "../../types.js";
2
2
  type AnyClass = new (...args: any[]) => any;
3
3
  export declare const matchesAnyClass: (classes: readonly AnyClass[], containerOrClass?: Container | ContainerClass | null) => boolean;
4
- export declare const isContainerType: (cls: AnyClass, containerOrClass?: Container | ContainerClass | null) => boolean;
5
4
  export declare const matchesInterface: (methods: readonly string[], containerOrClass?: Container | ContainerClass | null) => boolean;
6
- export declare const filterProps: (props: Props, excludeKeys: string[]) => Props;
7
- export declare const resolvePropMeta: (container: Container, key: string) => [string | null, string] | null;
8
- export declare const resolveSignal: (container: Container, signalName: string) => boolean;
5
+ export declare const filterProps: (props: Props, excludeKeys: readonly string[]) => Props;
6
+ export declare const resolvePropMeta: (instance: Container, key: string) => [string | null, string] | null;
7
+ export declare const resolveSignal: (instance: Container, signalName: string) => boolean;
8
+ export declare const propNameToSignalName: (propName: string) => string;
9
+ export declare const hasChanged: <T>(oldProps: T | null, newProps: T, key: keyof T) => boolean;
10
+ export declare const shallowArrayEqual: <T extends Record<string, unknown>>(a: T[], b: T[]) => boolean;
11
+ export declare const primitiveArrayEqual: <T extends string | number | boolean>(a: T[] | null | undefined, b: T[] | null | undefined) => boolean;
9
12
  export {};
@@ -7,7 +7,6 @@ export const matchesAnyClass = (classes, containerOrClass) => {
7
7
  containerOrClass === cls ||
8
8
  Object.prototype.isPrototypeOf.call(cls, containerOrClass));
9
9
  };
10
- export const isContainerType = (cls, containerOrClass) => matchesAnyClass([cls], containerOrClass);
11
10
  export const matchesInterface = (methods, containerOrClass) => {
12
11
  if (!containerOrClass) {
13
12
  return false;
@@ -24,9 +23,9 @@ export const filterProps = (props, excludeKeys) => {
24
23
  }
25
24
  return result;
26
25
  };
27
- const walkPrototypeChain = (container, lookup) => {
26
+ const walkPrototypeChain = (instance, lookup) => {
28
27
  // biome-ignore lint/complexity/noBannedTypes: Walking prototype chain requires Function type
29
- let current = container.constructor;
28
+ let current = instance.constructor;
30
29
  while (current) {
31
30
  const typeName = current.glibTypeName;
32
31
  if (typeName) {
@@ -43,5 +42,51 @@ const walkPrototypeChain = (container, lookup) => {
43
42
  }
44
43
  return null;
45
44
  };
46
- export const resolvePropMeta = (container, key) => walkPrototypeChain(container, (typeName) => PROPS[typeName]?.[key] ?? null);
47
- export const resolveSignal = (container, signalName) => walkPrototypeChain(container, (typeName) => (SIGNALS[typeName]?.has(signalName) ? true : null)) ?? false;
45
+ export const resolvePropMeta = (instance, key) => walkPrototypeChain(instance, (typeName) => PROPS[typeName]?.[key] ?? null);
46
+ export const resolveSignal = (instance, signalName) => {
47
+ if (signalName === "notify")
48
+ return true;
49
+ return walkPrototypeChain(instance, (typeName) => (SIGNALS[typeName]?.has(signalName) ? true : null)) ?? false;
50
+ };
51
+ export const propNameToSignalName = (propName) => {
52
+ if (!propName.startsWith("on"))
53
+ return propName;
54
+ return propName
55
+ .slice(2)
56
+ .replace(/([A-Z])/g, "-$1")
57
+ .toLowerCase()
58
+ .replace(/^-/, "");
59
+ };
60
+ export const hasChanged = (oldProps, newProps, key) => !oldProps || oldProps[key] !== newProps[key];
61
+ export const shallowArrayEqual = (a, b) => {
62
+ if (a.length !== b.length)
63
+ return false;
64
+ for (let i = 0; i < a.length; i++) {
65
+ const itemA = a[i];
66
+ const itemB = b[i];
67
+ if (!itemA || !itemB)
68
+ return false;
69
+ const keysA = Object.keys(itemA);
70
+ const keysB = Object.keys(itemB);
71
+ if (keysA.length !== keysB.length)
72
+ return false;
73
+ for (const key of keysA) {
74
+ if (itemA[key] !== itemB[key])
75
+ return false;
76
+ }
77
+ }
78
+ return true;
79
+ };
80
+ export const primitiveArrayEqual = (a, b) => {
81
+ if (a === b)
82
+ return true;
83
+ if (!a || !b)
84
+ return false;
85
+ if (a.length !== b.length)
86
+ return false;
87
+ for (let i = 0; i < a.length; i++) {
88
+ if (a[i] !== b[i])
89
+ return false;
90
+ }
91
+ return true;
92
+ };
@@ -1,69 +1,34 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import { registerNodeClass } from "../registry.js";
3
- import { CommitPriority, scheduleAfterCommit } from "../scheduler.js";
4
- import { isContainerType } from "./internal/utils.js";
5
- import { LevelBarOffsetNode } from "./level-bar-offset.js";
3
+ import { filterProps, matchesAnyClass, shallowArrayEqual } from "./internal/utils.js";
6
4
  import { WidgetNode } from "./widget.js";
5
+ const OWN_PROPS = ["offsets"];
7
6
  class LevelBarNode extends WidgetNode {
8
7
  static priority = 1;
9
- offsetChildren = [];
10
8
  appliedOffsetIds = new Set();
11
9
  static matches(_type, containerOrClass) {
12
- return isContainerType(Gtk.LevelBar, containerOrClass);
10
+ return matchesAnyClass([Gtk.LevelBar], containerOrClass);
13
11
  }
14
- appendChild(child) {
15
- if (child instanceof LevelBarOffsetNode) {
16
- child.setLevelBar(this.container, () => this.scheduleRebuildAllOffsets());
17
- this.offsetChildren.push(child);
18
- scheduleAfterCommit(() => {
19
- const id = child.addOffset();
20
- if (id) {
21
- this.appliedOffsetIds.add(id);
22
- }
23
- });
24
- return;
25
- }
26
- super.appendChild(child);
12
+ updateProps(oldProps, newProps) {
13
+ super.updateProps(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
14
+ this.applyOwnProps(oldProps, newProps);
27
15
  }
28
- insertBefore(child, before) {
29
- if (child instanceof LevelBarOffsetNode) {
30
- child.setLevelBar(this.container, () => this.scheduleRebuildAllOffsets());
31
- const beforeIndex = this.offsetChildren.indexOf(before);
32
- if (beforeIndex >= 0) {
33
- this.offsetChildren.splice(beforeIndex, 0, child);
34
- }
35
- else {
36
- this.offsetChildren.push(child);
37
- }
38
- this.scheduleRebuildAllOffsets();
39
- return;
40
- }
41
- super.insertBefore(child, before);
16
+ applyOwnProps(oldProps, newProps) {
17
+ this.applyOffsets(oldProps, newProps);
42
18
  }
43
- removeChild(child) {
44
- if (child instanceof LevelBarOffsetNode) {
45
- const index = this.offsetChildren.indexOf(child);
46
- if (index >= 0) {
47
- this.offsetChildren.splice(index, 1);
48
- }
49
- this.scheduleRebuildAllOffsets(CommitPriority.HIGH);
19
+ applyOffsets(oldProps, newProps) {
20
+ const newOffsets = newProps.offsets ?? [];
21
+ if (shallowArrayEqual(oldProps?.offsets ?? [], newOffsets)) {
50
22
  return;
51
23
  }
52
- super.removeChild(child);
53
- }
54
- scheduleRebuildAllOffsets(priority = CommitPriority.NORMAL) {
55
- scheduleAfterCommit(() => {
56
- for (const id of this.appliedOffsetIds) {
57
- this.container.removeOffsetValue(id);
58
- }
59
- this.appliedOffsetIds.clear();
60
- for (const offset of this.offsetChildren) {
61
- const id = offset.addOffset();
62
- if (id) {
63
- this.appliedOffsetIds.add(id);
64
- }
65
- }
66
- }, priority);
24
+ for (const id of this.appliedOffsetIds) {
25
+ this.container.removeOffsetValue(id);
26
+ }
27
+ this.appliedOffsetIds.clear();
28
+ for (const offset of newOffsets) {
29
+ this.container.addOffsetValue(offset.id, offset.value);
30
+ this.appliedOffsetIds.add(offset.id);
31
+ }
67
32
  }
68
33
  }
69
34
  registerNodeClass(LevelBarNode);
@@ -2,11 +2,14 @@ import type { ListItemProps } from "../jsx.js";
2
2
  import type { ListStore } from "./internal/list-store.js";
3
3
  import { VirtualNode } from "./virtual.js";
4
4
  type Props = Partial<ListItemProps>;
5
- export declare class ListItemNode<T extends Omit<ListStore, "items" | "model"> = ListStore, P extends Props = Props> extends VirtualNode<P> {
5
+ export declare class ListItemNode<T extends {
6
+ updateItem(id: string, value: unknown): void;
7
+ } = ListStore, P extends Props = Props> extends VirtualNode<P> {
6
8
  static priority: number;
7
- private store?;
9
+ private store;
8
10
  static matches(type: string): boolean;
9
- setStore(store?: T | null): void;
11
+ setStore(store: T | null): void;
10
12
  updateProps(oldProps: P | null, newProps: P): void;
13
+ protected applyOwnProps(oldProps: P | null, newProps: P): void;
11
14
  }
12
15
  export {};
@@ -1,8 +1,9 @@
1
1
  import { registerNodeClass } from "../registry.js";
2
+ import { hasChanged } from "./internal/utils.js";
2
3
  import { VirtualNode } from "./virtual.js";
3
4
  export class ListItemNode extends VirtualNode {
4
5
  static priority = 1;
5
- store;
6
+ store = null;
6
7
  static matches(type) {
7
8
  return type === "ListItem";
8
9
  }
@@ -11,10 +12,12 @@ export class ListItemNode extends VirtualNode {
11
12
  }
12
13
  updateProps(oldProps, newProps) {
13
14
  super.updateProps(oldProps, newProps);
14
- if (!this.store) {
15
+ this.applyOwnProps(oldProps, newProps);
16
+ }
17
+ applyOwnProps(oldProps, newProps) {
18
+ if (!this.store)
15
19
  return;
16
- }
17
- if (!oldProps || oldProps.id !== newProps.id || oldProps.value !== newProps.value) {
20
+ if (hasChanged(oldProps, newProps, "id") || hasChanged(oldProps, newProps, "value")) {
18
21
  if (newProps.id !== undefined) {
19
22
  this.store.updateItem(newProps.id, newProps.value);
20
23
  }
@@ -1,11 +1,11 @@
1
1
  import { LIST_WIDGET_CLASSES } from "../generated/internal.js";
2
2
  import { registerNodeClass } from "../registry.js";
3
3
  import { ListItemRenderer } from "./internal/list-item-renderer.js";
4
- import { filterProps, matchesAnyClass } from "./internal/utils.js";
4
+ import { filterProps, hasChanged, matchesAnyClass } from "./internal/utils.js";
5
5
  import { ListItemNode } from "./list-item.js";
6
- import { List } from "./models/list.js";
6
+ import { ListModel } from "./models/list.js";
7
7
  import { WidgetNode } from "./widget.js";
8
- const PROP_NAMES = ["renderItem", "estimatedItemHeight"];
8
+ const OWN_PROPS = ["renderItem", "estimatedItemHeight"];
9
9
  class ListViewNode extends WidgetNode {
10
10
  static priority = 1;
11
11
  itemRenderer;
@@ -15,13 +15,14 @@ class ListViewNode extends WidgetNode {
15
15
  }
16
16
  constructor(typeName, props, container, rootContainer) {
17
17
  super(typeName, props, container, rootContainer);
18
- this.list = new List({
18
+ this.list = new ListModel({ owner: this, signalStore: this.signalStore }, {
19
19
  selectionMode: props.selectionMode,
20
20
  selected: props.selected,
21
21
  onSelectionChanged: props.onSelectionChanged,
22
22
  });
23
- this.itemRenderer = new ListItemRenderer();
23
+ this.itemRenderer = new ListItemRenderer(this.signalStore);
24
24
  this.itemRenderer.setStore(this.list.getStore());
25
+ this.list.getStore().setOnItemUpdated((id) => this.itemRenderer.rebindItem(id));
25
26
  this.container.setFactory(this.itemRenderer.getFactory());
26
27
  }
27
28
  mount() {
@@ -51,19 +52,22 @@ class ListViewNode extends WidgetNode {
51
52
  this.list.removeChild(child);
52
53
  }
53
54
  updateProps(oldProps, newProps) {
54
- if (!oldProps || oldProps.renderItem !== newProps.renderItem) {
55
- this.itemRenderer.setRenderFn(newProps.renderItem);
55
+ super.updateProps(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
56
+ this.applyOwnProps(oldProps, newProps);
57
+ }
58
+ applyOwnProps(oldProps, newProps) {
59
+ if (hasChanged(oldProps, newProps, "renderItem")) {
60
+ this.itemRenderer.setRenderFn(newProps.renderItem ?? null);
56
61
  }
57
- if (!oldProps || oldProps.estimatedItemHeight !== newProps.estimatedItemHeight) {
58
- this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight);
62
+ if (hasChanged(oldProps, newProps, "estimatedItemHeight")) {
63
+ this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight ?? null);
59
64
  }
60
65
  const previousModel = this.list.getSelectionModel();
61
- this.list.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
66
+ this.list.updateProps(oldProps, newProps);
62
67
  const currentModel = this.list.getSelectionModel();
63
68
  if (previousModel !== currentModel) {
64
69
  this.container.setModel(currentModel);
65
70
  }
66
- super.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
67
71
  }
68
72
  }
69
73
  registerNodeClass(ListViewNode);
@@ -1,8 +1,8 @@
1
1
  import type { Container } from "../types.js";
2
- import { Menu, type MenuProps } from "./models/menu.js";
3
- export declare class MenuNode extends Menu {
2
+ import { MenuModel, type MenuProps } from "./models/menu.js";
3
+ export declare class MenuNode extends MenuModel {
4
4
  static priority: number;
5
5
  static matches(type: string): boolean;
6
6
  private static getType;
7
- constructor(typeName: string, props: MenuProps, _container: undefined, rootContainer?: Container);
7
+ constructor(typeName: string, props: MenuProps, _container: undefined, rootContainer: Container);
8
8
  }