@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,123 +1,81 @@
1
- import { batch, isObjectEqual, NativeObject } from "@gtkx/ffi";
2
- import * as Gdk from "@gtkx/ffi/gdk";
1
+ import { getNativeObject, isObjectEqual } from "@gtkx/ffi";
2
+ import { ObjectClass, ParamSpecString, Type, TypeInstance, typeClassRef, typeFromName, typeFundamental, typeNameFromInstance, } from "@gtkx/ffi/gobject";
3
3
  import * as Gtk from "@gtkx/ffi/gtk";
4
4
  import { CONSTRUCTOR_PROPS } from "../generated/internal.js";
5
5
  import { Node } from "../node.js";
6
6
  import { registerNodeClass } from "../registry.js";
7
- import { AdjustmentNode } from "./adjustment.js";
8
- import { EVENT_CONTROLLER_PROPS } from "./internal/constants.js";
9
- import { hasSingleContent, isAddable, isAdjustable, isAppendable, isEditable, isInsertable, isRemovable, isReorderable, isSingleChild, } from "./internal/predicates.js";
10
- import { signalStore } from "./internal/signal-store.js";
11
- import { filterProps, isContainerType, resolvePropMeta, resolveSignal } from "./internal/utils.js";
12
- import { ShortcutControllerNode } from "./shortcut-controller.js";
13
- import { SlotNode } from "./slot.js";
14
- const PROPS = ["children", "widthRequest", "heightRequest", "grabFocus"];
7
+ import { getAttachmentStrategy, attachChild as performAttachment, detachChild as performDetachment, } from "./internal/child-attachment.js";
8
+ import { isAttachable, isEditable, isInsertable, isRemovable, isReorderable, } from "./internal/predicates.js";
9
+ import { filterProps, matchesAnyClass, propNameToSignalName, resolvePropMeta, resolveSignal, } from "./internal/utils.js";
10
+ const EXCLUDED_PROPS = ["children", "widthRequest", "heightRequest", "grabFocus"];
11
+ function findProperty(obj, key) {
12
+ if (!obj.handle)
13
+ return null;
14
+ const propertyName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
15
+ const typeInstance = getNativeObject(obj.handle, TypeInstance);
16
+ const typeName = typeNameFromInstance(typeInstance);
17
+ const gtype = typeFromName(typeName);
18
+ const typeClass = typeClassRef(gtype);
19
+ const objectClass = getNativeObject(typeClass.handle, ObjectClass);
20
+ return objectClass.findProperty(propertyName) ?? null;
21
+ }
15
22
  export class WidgetNode extends Node {
16
23
  static priority = 3;
17
- motionController;
18
- clickController;
19
- keyController;
20
- scrollController;
21
- dragSourceController;
22
- dropTargetController;
23
- gestureDragController;
24
- adjustmentChild;
25
24
  static matches(_type, containerOrClass) {
26
- return isContainerType(Gtk.Widget, containerOrClass);
25
+ return matchesAnyClass([Gtk.Widget], containerOrClass);
27
26
  }
28
- static createContainer(props, containerClass) {
27
+ static createContainer(props, containerClass, _rootContainer) {
29
28
  const WidgetClass = containerClass;
30
29
  const typeName = WidgetClass.glibTypeName;
31
30
  const args = (CONSTRUCTOR_PROPS[typeName] ?? []).map((name) => props[name]);
32
31
  return new WidgetClass(...args);
33
32
  }
34
33
  appendChild(child) {
35
- if (child instanceof ShortcutControllerNode) {
36
- child.setParent(this.container);
37
- return;
38
- }
39
- if (child instanceof SlotNode) {
40
- child.setParent(this.container);
41
- return;
42
- }
43
- if (child instanceof AdjustmentNode) {
44
- if (!isAdjustable(this.container)) {
45
- throw new Error(`Cannot add Adjustment to '${this.typeName}': widget does not support adjustments`);
46
- }
47
- if (this.adjustmentChild) {
48
- throw new Error(`${this.typeName} can only have one Adjustment child`);
49
- }
50
- this.adjustmentChild = child;
51
- child.setWidget(this.container);
34
+ if (isAttachable(child) && child.canBeChildOf(this)) {
35
+ child.attachTo(this);
52
36
  return;
53
37
  }
54
38
  if (!(child instanceof WidgetNode)) {
55
- throw new Error(`Cannot append '${child.typeName}' to 'Widget': expected WidgetNode child`);
39
+ throw new Error(`Cannot append '${child.typeName}' to 'Widget': expected Widget`);
56
40
  }
57
41
  if (child.container instanceof Gtk.Window) {
58
42
  throw new Error(`Cannot append 'Window' to '${this.typeName}': windows must be top-level containers`);
59
43
  }
60
- batch(() => this.attachChild(child));
44
+ this.attachChild(child);
61
45
  }
62
46
  removeChild(child) {
63
- if (child instanceof ShortcutControllerNode) {
64
- child.setParent(undefined);
65
- return;
66
- }
67
- if (child instanceof SlotNode) {
68
- return;
69
- }
70
- if (child instanceof AdjustmentNode) {
71
- if (this.adjustmentChild === child) {
72
- this.adjustmentChild = undefined;
73
- }
47
+ if (isAttachable(child) && child.canBeChildOf(this)) {
48
+ child.detachFrom(this);
74
49
  return;
75
50
  }
76
51
  if (!(child instanceof WidgetNode)) {
77
- throw new Error(`Cannot remove '${child.typeName}' from 'Widget': expected WidgetNode child`);
52
+ throw new Error(`Cannot remove '${child.typeName}' from 'Widget': expected Widget`);
78
53
  }
79
54
  if (child.container instanceof Gtk.Window) {
80
55
  throw new Error(`Cannot remove 'Window' from '${this.typeName}': windows must be top-level containers`);
81
56
  }
82
- batch(() => this.detachChild(child));
57
+ this.detachChild(child);
83
58
  }
84
59
  insertBefore(child, before) {
85
- if (child instanceof ShortcutControllerNode) {
86
- child.setParent(this.container);
87
- return;
88
- }
89
- if (child instanceof SlotNode) {
90
- child.setParent(this.container);
91
- return;
92
- }
93
- if (child instanceof AdjustmentNode) {
94
- if (!isAdjustable(this.container)) {
95
- throw new Error(`Cannot add Adjustment to '${this.typeName}': widget does not support adjustments`);
96
- }
97
- if (this.adjustmentChild) {
98
- throw new Error(`${this.typeName} can only have one Adjustment child`);
99
- }
100
- this.adjustmentChild = child;
101
- child.setWidget(this.container);
60
+ if (isAttachable(child) && child.canBeChildOf(this)) {
61
+ child.attachTo(this);
102
62
  return;
103
63
  }
104
64
  if (!(child instanceof WidgetNode) || !(before instanceof WidgetNode)) {
105
- throw new Error(`Cannot insert '${child.typeName}' before '${before.typeName}': expected WidgetNode children`);
65
+ throw new Error(`Cannot insert '${child.typeName}' into '${this.typeName}': expected Widget`);
106
66
  }
107
67
  if (child.container instanceof Gtk.Window) {
108
68
  throw new Error(`Cannot insert 'Window' into '${this.typeName}': windows must be top-level containers`);
109
69
  }
110
- batch(() => {
111
- if (isReorderable(this.container)) {
112
- this.insertBeforeReorderable(this.container, child, before);
113
- }
114
- else if (isInsertable(this.container)) {
115
- this.insertBeforeInsertable(this.container, child, before);
116
- }
117
- else {
118
- this.appendChild(child);
119
- }
120
- });
70
+ if (isReorderable(this.container)) {
71
+ this.insertBeforeReorderable(this.container, child, before);
72
+ }
73
+ else if (isInsertable(this.container)) {
74
+ this.insertBeforeInsertable(this.container, child, before);
75
+ }
76
+ else {
77
+ this.appendChild(child);
78
+ }
121
79
  }
122
80
  insertBeforeReorderable(container, child, before) {
123
81
  const previousSibling = this.findPreviousSibling(before);
@@ -137,11 +95,14 @@ export class WidgetNode extends Node {
137
95
  container.insert(child.container, position);
138
96
  }
139
97
  updateProps(oldProps, newProps) {
98
+ if (!this.container) {
99
+ throw new Error(`WidgetNode.updateProps: container is undefined for ${this.typeName}`);
100
+ }
140
101
  this.updateSizeRequest(oldProps, newProps);
141
102
  this.updateGrabFocus(oldProps, newProps);
142
103
  const propNames = new Set([
143
- ...Object.keys(filterProps(oldProps ?? {}, PROPS)),
144
- ...Object.keys(filterProps(newProps ?? {}, PROPS)),
104
+ ...Object.keys(filterProps(oldProps ?? {}, EXCLUDED_PROPS)),
105
+ ...Object.keys(filterProps(newProps ?? {}, EXCLUDED_PROPS)),
145
106
  ]);
146
107
  const pendingSignals = [];
147
108
  const pendingProperties = [];
@@ -150,40 +111,28 @@ export class WidgetNode extends Node {
150
111
  const newValue = newProps[name];
151
112
  if (oldValue === newValue)
152
113
  continue;
153
- if (EVENT_CONTROLLER_PROPS.has(name)) {
154
- pendingSignals.push({ name, newValue });
155
- continue;
156
- }
157
- if (name === "onNotify") {
158
- pendingSignals.push({ name, newValue });
159
- continue;
160
- }
161
- const signalName = this.propNameToSignalName(name);
114
+ const signalName = propNameToSignalName(name);
162
115
  if (resolveSignal(this.container, signalName)) {
163
116
  pendingSignals.push({ name, newValue });
164
117
  }
165
118
  else if (newValue !== undefined) {
166
119
  pendingProperties.push({ name, oldValue, newValue });
167
120
  }
121
+ else if (oldValue !== undefined) {
122
+ const defaultValue = this.getPropertyDefaultValue(name);
123
+ if (defaultValue !== undefined) {
124
+ pendingProperties.push({ name, oldValue, newValue: defaultValue });
125
+ }
126
+ }
168
127
  }
169
128
  for (const { name, newValue } of pendingSignals) {
170
- if (EVENT_CONTROLLER_PROPS.has(name)) {
171
- this.updateEventControllerProp(name, newValue ?? null);
172
- }
173
- else if (name === "onNotify") {
174
- this.updateNotifyHandler(newValue ?? null);
175
- }
176
- else {
177
- const signalName = this.propNameToSignalName(name);
178
- const handler = typeof newValue === "function" ? newValue : undefined;
179
- signalStore.set(this, this.container, signalName, handler);
180
- }
129
+ const signalName = propNameToSignalName(name);
130
+ const handler = typeof newValue === "function" ? newValue : undefined;
131
+ this.signalStore.set(this, this.container, signalName, handler);
181
132
  }
182
133
  for (const { name, oldValue, newValue } of pendingProperties) {
183
- const isEditableText = name === "text" && isEditable(this.container);
184
- if (isEditableText && oldValue !== undefined) {
185
- const currentValue = this.getProperty(name);
186
- if (oldValue !== currentValue) {
134
+ if (name === "text" && oldValue !== undefined && isEditable(this.container)) {
135
+ if (oldValue !== this.container.getText()) {
187
136
  continue;
188
137
  }
189
138
  }
@@ -206,157 +155,39 @@ export class WidgetNode extends Node {
206
155
  this.container.grabFocus();
207
156
  }
208
157
  }
209
- updateEventControllerProp(propName, handlerOrValue) {
210
- const wrappedHandler = typeof handlerOrValue === "function"
211
- ? (_self, ...args) => handlerOrValue(...args)
212
- : undefined;
213
- switch (propName) {
214
- case "onEnter":
215
- case "onLeave":
216
- case "onMotion": {
217
- if (!this.motionController) {
218
- this.motionController = new Gtk.EventControllerMotion();
219
- this.container.addController(this.motionController);
220
- }
221
- const signalName = propName === "onEnter" ? "enter" : propName === "onLeave" ? "leave" : "motion";
222
- signalStore.set(this, this.motionController, signalName, wrappedHandler);
223
- break;
224
- }
225
- case "onPressed":
226
- case "onReleased": {
227
- if (!this.clickController) {
228
- this.clickController = new Gtk.GestureClick();
229
- this.container.addController(this.clickController);
230
- }
231
- const signalName = propName === "onPressed" ? "pressed" : "released";
232
- signalStore.set(this, this.clickController, signalName, wrappedHandler);
233
- break;
234
- }
235
- case "onKeyPressed":
236
- case "onKeyReleased": {
237
- if (!this.keyController) {
238
- this.keyController = new Gtk.EventControllerKey();
239
- this.container.addController(this.keyController);
240
- }
241
- const signalName = propName === "onKeyPressed" ? "key-pressed" : "key-released";
242
- signalStore.set(this, this.keyController, signalName, wrappedHandler);
243
- break;
244
- }
245
- case "onScroll": {
246
- if (!this.scrollController) {
247
- this.scrollController = new Gtk.EventControllerScroll(Gtk.EventControllerScrollFlags.BOTH_AXES);
248
- this.container.addController(this.scrollController);
249
- }
250
- signalStore.set(this, this.scrollController, "scroll", wrappedHandler);
251
- break;
252
- }
253
- case "onDragPrepare":
254
- case "onDragBegin":
255
- case "onDragEnd":
256
- case "onDragCancel":
257
- case "dragActions": {
258
- const dragSource = this.ensureDragSource();
259
- if (propName === "dragActions") {
260
- dragSource.setActions(handlerOrValue ?? Gdk.DragAction.COPY);
261
- }
262
- else {
263
- const signalName = propName === "onDragPrepare"
264
- ? "prepare"
265
- : propName === "onDragBegin"
266
- ? "drag-begin"
267
- : propName === "onDragEnd"
268
- ? "drag-end"
269
- : "drag-cancel";
270
- signalStore.set(this, dragSource, signalName, wrappedHandler);
271
- }
272
- break;
273
- }
274
- case "onDrop":
275
- case "onDropEnter":
276
- case "onDropLeave":
277
- case "onDropMotion":
278
- case "dropActions":
279
- case "dropTypes": {
280
- const dropTarget = this.ensureDropTarget();
281
- if (propName === "dropActions") {
282
- dropTarget.setActions(handlerOrValue ?? Gdk.DragAction.COPY);
283
- }
284
- else if (propName === "dropTypes") {
285
- const types = handlerOrValue ?? [];
286
- dropTarget.setGtypes(types.length, types);
287
- }
288
- else {
289
- const signalName = propName === "onDrop"
290
- ? "drop"
291
- : propName === "onDropEnter"
292
- ? "enter"
293
- : propName === "onDropLeave"
294
- ? "leave"
295
- : "motion";
296
- signalStore.set(this, dropTarget, signalName, wrappedHandler);
297
- }
298
- break;
299
- }
300
- case "onGestureDragBegin":
301
- case "onGestureDragUpdate":
302
- case "onGestureDragEnd": {
303
- const gestureDrag = this.ensureGestureDrag();
304
- const signalName = propName === "onGestureDragBegin"
305
- ? "drag-begin"
306
- : propName === "onGestureDragUpdate"
307
- ? "drag-update"
308
- : "drag-end";
309
- signalStore.set(this, gestureDrag, signalName, wrappedHandler);
310
- break;
311
- }
312
- }
313
- }
314
- ensureDragSource() {
315
- if (!this.dragSourceController) {
316
- this.dragSourceController = new Gtk.DragSource();
317
- this.dragSourceController.setActions(Gdk.DragAction.COPY);
318
- this.container.addController(this.dragSourceController);
319
- }
320
- return this.dragSourceController;
321
- }
322
- ensureDropTarget() {
323
- if (!this.dropTargetController) {
324
- this.dropTargetController = new Gtk.DropTarget(0, Gdk.DragAction.COPY);
325
- this.container.addController(this.dropTargetController);
326
- }
327
- return this.dropTargetController;
328
- }
329
- ensureGestureDrag() {
330
- if (!this.gestureDragController) {
331
- this.gestureDragController = new Gtk.GestureDrag();
332
- this.container.addController(this.gestureDragController);
333
- }
334
- return this.gestureDragController;
335
- }
336
- updateNotifyHandler(handler) {
337
- const wrappedHandler = handler
338
- ? (obj, pspec) => {
339
- handler(obj, pspec.getName());
340
- }
341
- : undefined;
342
- signalStore.set(this, this.container, "notify", wrappedHandler);
343
- }
344
- propNameToSignalName(name) {
345
- return name
346
- .slice(2)
347
- .replace(/([A-Z])/g, "-$1")
348
- .toLowerCase()
349
- .replace(/^-/, "");
350
- }
351
- getProperty(key) {
352
- const propMeta = resolvePropMeta(this.container, key);
353
- if (!propMeta)
158
+ getPropertyDefaultValue(key) {
159
+ if (!resolvePropMeta(this.container, key))
354
160
  return undefined;
355
- const [getterName] = propMeta;
356
- const getter = getterName ? this.container[getterName] : undefined;
357
- if (getter && typeof getter === "function") {
358
- return getter.call(this.container);
359
- }
161
+ const pspec = findProperty(this.container, key);
162
+ if (!pspec)
163
+ return undefined;
164
+ const value = pspec.getDefaultValue();
165
+ const gtype = value.getType();
166
+ const fundamental = typeFundamental(gtype);
167
+ if (fundamental === Type.BOOLEAN)
168
+ return value.getBoolean();
169
+ if (fundamental === Type.INT)
170
+ return value.getInt();
171
+ if (fundamental === Type.UINT)
172
+ return value.getUint();
173
+ if (fundamental === Type.LONG)
174
+ return value.getLong();
175
+ if (fundamental === Type.ULONG)
176
+ return value.getUlong();
177
+ if (fundamental === Type.INT64)
178
+ return value.getInt64();
179
+ if (fundamental === Type.UINT64)
180
+ return value.getUint64();
181
+ if (fundamental === Type.FLOAT)
182
+ return value.getFloat();
183
+ if (fundamental === Type.DOUBLE)
184
+ return value.getDouble();
185
+ if (fundamental === Type.STRING)
186
+ return value.getString();
187
+ if (fundamental === Type.ENUM)
188
+ return value.getEnum();
189
+ if (fundamental === Type.FLAGS)
190
+ return value.getFlags();
360
191
  return undefined;
361
192
  }
362
193
  setProperty(key, value) {
@@ -365,21 +196,17 @@ export class WidgetNode extends Node {
365
196
  return;
366
197
  const [getterName, setterName] = propMeta;
367
198
  const setter = this.container[setterName];
368
- const getter = getterName ? this.container[getterName] : undefined;
369
- if (getter && typeof getter === "function") {
370
- const currentValue = getter.call(this.container);
371
- if (currentValue === value) {
372
- return;
373
- }
374
- if (currentValue instanceof NativeObject &&
375
- value instanceof NativeObject &&
376
- isObjectEqual(currentValue, value)) {
377
- return;
199
+ if (!setter || typeof setter !== "function")
200
+ return;
201
+ if (getterName && findProperty(this.container, key) instanceof ParamSpecString) {
202
+ const getter = this.container[getterName];
203
+ if (getter && typeof getter === "function") {
204
+ const currentValue = getter.call(this.container);
205
+ if (currentValue === value)
206
+ return;
378
207
  }
379
208
  }
380
- if (setter && typeof setter === "function") {
381
- setter.call(this.container, value);
382
- }
209
+ setter.call(this.container, value);
383
210
  }
384
211
  detachChildFromParent(child) {
385
212
  const currentParent = child.container.getParent();
@@ -388,37 +215,21 @@ export class WidgetNode extends Node {
388
215
  }
389
216
  }
390
217
  attachChild(child) {
391
- if (isAppendable(this.container)) {
392
- this.detachChildFromParent(child);
393
- this.container.append(child.container);
218
+ const strategy = getAttachmentStrategy(this.container);
219
+ if (!strategy) {
220
+ throw new Error(`Cannot append '${child.typeName}' to '${this.container.constructor.name}': container does not support children`);
394
221
  }
395
- else if (isAddable(this.container)) {
222
+ if (strategy.type === "appendable" || strategy.type === "addable") {
396
223
  this.detachChildFromParent(child);
397
- this.container.add(child.container);
398
- }
399
- else if (hasSingleContent(this.container)) {
400
- this.container.setContent(child.container);
401
- }
402
- else if (isSingleChild(this.container)) {
403
- this.container.setChild(child.container);
404
- }
405
- else {
406
- throw new Error(`Cannot append '${child.typeName}' to '${this.container.constructor.name}': container does not support children`);
407
224
  }
225
+ performAttachment(child.container, strategy);
408
226
  }
409
227
  detachChild(child) {
410
- if (isRemovable(this.container)) {
411
- this.container.remove(child.container);
412
- }
413
- else if (hasSingleContent(this.container)) {
414
- this.container.setContent(null);
415
- }
416
- else if (isSingleChild(this.container)) {
417
- this.container.setChild(null);
418
- }
419
- else {
228
+ const strategy = getAttachmentStrategy(this.container);
229
+ if (!strategy) {
420
230
  throw new Error(`Cannot remove '${child.typeName}' from '${this.container.constructor.name}': container does not support child removal`);
421
231
  }
232
+ performDetachment(child.container, strategy);
422
233
  }
423
234
  findPreviousSibling(before) {
424
235
  let beforeChild = this.container.getFirstChild();
@@ -2,22 +2,28 @@ import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import type { Node } from "../node.js";
3
3
  import type { Container, ContainerClass, Props } from "../types.js";
4
4
  import { WidgetNode } from "./widget.js";
5
- type WindowProps = Props & {
5
+ type CreditSection = {
6
+ name: string;
7
+ people: string[];
8
+ };
9
+ export type WindowProps = Props & {
6
10
  defaultWidth?: number;
7
11
  defaultHeight?: number;
8
12
  onClose?: () => void;
13
+ creditSections?: CreditSection[];
9
14
  };
10
15
  export declare class WindowNode extends WidgetNode<Gtk.Window, WindowProps> {
11
16
  static priority: number;
12
17
  private menu;
13
18
  static matches(_type: string, containerOrClass?: Container | ContainerClass | null): boolean;
14
- static createContainer(props: Props, containerClass: typeof Gtk.Window, rootContainer?: Container): Gtk.Window;
15
- constructor(typeName: string, props: WindowProps, container: Gtk.Window, rootContainer?: Container);
19
+ static createContainer(props: Props, containerClass: typeof Gtk.Window, rootContainer: Container | undefined): Gtk.Window;
20
+ constructor(typeName: string, props: WindowProps, container: Gtk.Window, rootContainer: Container);
16
21
  appendChild(child: Node): void;
17
22
  removeChild(child: Node): void;
18
23
  insertBefore(child: Node, before: Node): void;
19
24
  mount(): void;
20
25
  unmount(): void;
21
26
  updateProps(oldProps: WindowProps | null, newProps: WindowProps): void;
27
+ protected applyOwnProps(oldProps: WindowProps | null, newProps: WindowProps): void;
22
28
  }
23
29
  export {};
@@ -1,25 +1,25 @@
1
1
  import * as Adw from "@gtkx/ffi/adw";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
3
  import { registerNodeClass } from "../registry.js";
4
- import { signalStore } from "./internal/signal-store.js";
5
- import { filterProps, isContainerType } from "./internal/utils.js";
6
- import { Menu } from "./models/menu.js";
4
+ import { DialogNode } from "./dialog.js";
5
+ import { filterProps, hasChanged, matchesAnyClass } from "./internal/utils.js";
6
+ import { MenuModel } from "./models/menu.js";
7
7
  import { WidgetNode } from "./widget.js";
8
- const PROPS = ["defaultWidth", "defaultHeight", "onClose"];
8
+ const OWN_PROPS = ["defaultWidth", "defaultHeight", "onClose"];
9
9
  export class WindowNode extends WidgetNode {
10
10
  static priority = 1;
11
11
  menu;
12
12
  static matches(_type, containerOrClass) {
13
- return isContainerType(Gtk.Window, containerOrClass);
13
+ return matchesAnyClass([Gtk.Window], containerOrClass);
14
14
  }
15
15
  static createContainer(props, containerClass, rootContainer) {
16
16
  const WindowClass = containerClass;
17
- if (isContainerType(Gtk.ApplicationWindow, WindowClass) ||
18
- isContainerType(Adw.ApplicationWindow, WindowClass)) {
17
+ if (matchesAnyClass([Gtk.ApplicationWindow], WindowClass) ||
18
+ matchesAnyClass([Adw.ApplicationWindow], WindowClass)) {
19
19
  if (!(rootContainer instanceof Gtk.Application)) {
20
20
  throw new Error("Expected ApplicationWindow to be created within Application");
21
21
  }
22
- if (isContainerType(Adw.ApplicationWindow, WindowClass)) {
22
+ if (matchesAnyClass([Adw.ApplicationWindow], WindowClass)) {
23
23
  return new Adw.ApplicationWindow(rootContainer);
24
24
  }
25
25
  return new Gtk.ApplicationWindow(rootContainer);
@@ -30,13 +30,22 @@ export class WindowNode extends WidgetNode {
30
30
  super(typeName, props, container, rootContainer);
31
31
  const application = rootContainer instanceof Gtk.Application ? rootContainer : undefined;
32
32
  const actionMap = container instanceof Gtk.ApplicationWindow ? container : undefined;
33
- this.menu = new Menu("root", {}, actionMap, application);
33
+ this.menu = new MenuModel("root", {}, rootContainer, actionMap, application);
34
+ if (container instanceof Gtk.AboutDialog && props.creditSections) {
35
+ for (const section of props.creditSections) {
36
+ container.addCreditSection(section.name, section.people);
37
+ }
38
+ }
34
39
  }
35
40
  appendChild(child) {
36
41
  if (child.container instanceof Gtk.Window) {
37
42
  child.container.setTransientFor(this.container);
38
43
  return;
39
44
  }
45
+ if (child instanceof DialogNode) {
46
+ child.parent = this.container;
47
+ return;
48
+ }
40
49
  this.menu.appendChild(child);
41
50
  super.appendChild(child);
42
51
  }
@@ -45,6 +54,10 @@ export class WindowNode extends WidgetNode {
45
54
  child.container.setTransientFor(null);
46
55
  return;
47
56
  }
57
+ if (child instanceof DialogNode) {
58
+ child.parent = null;
59
+ return;
60
+ }
48
61
  this.menu.removeChild(child);
49
62
  super.removeChild(child);
50
63
  }
@@ -61,14 +74,16 @@ export class WindowNode extends WidgetNode {
61
74
  super.unmount();
62
75
  }
63
76
  updateProps(oldProps, newProps) {
64
- if (!oldProps ||
65
- oldProps.defaultWidth !== newProps.defaultWidth ||
66
- oldProps.defaultHeight !== newProps.defaultHeight) {
77
+ super.updateProps(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
78
+ this.applyOwnProps(oldProps, newProps);
79
+ }
80
+ applyOwnProps(oldProps, newProps) {
81
+ if (hasChanged(oldProps, newProps, "defaultWidth") || hasChanged(oldProps, newProps, "defaultHeight")) {
67
82
  const width = newProps.defaultWidth ?? -1;
68
83
  const height = newProps.defaultHeight ?? -1;
69
84
  this.container.setDefaultSize(width, height);
70
85
  }
71
- if (!oldProps || oldProps.onClose !== newProps.onClose) {
86
+ if (hasChanged(oldProps, newProps, "onClose")) {
72
87
  const userHandler = newProps.onClose;
73
88
  const wrappedHandler = userHandler
74
89
  ? () => {
@@ -76,9 +91,8 @@ export class WindowNode extends WidgetNode {
76
91
  return true;
77
92
  }
78
93
  : undefined;
79
- signalStore.set(this, this.container, "close-request", wrappedHandler);
94
+ this.signalStore.set(this, this.container, "close-request", wrappedHandler);
80
95
  }
81
- super.updateProps(filterProps(oldProps ?? {}, PROPS), filterProps(newProps, PROPS));
82
96
  }
83
97
  }
84
98
  registerNodeClass(WindowNode);
@@ -1,7 +1,7 @@
1
1
  import type { Node } from "./node.js";
2
2
  import type { Container, Props } from "./types.js";
3
3
  type NodeClass<T = unknown, P = Props> = {
4
- new (typeName: string, props: P, container: T, rootContainer?: Container): Node<T, P>;
4
+ new (typeName: string, props: P, container: T, rootContainer: Container): Node<T, P>;
5
5
  } & Omit<typeof Node, "prototype">;
6
6
  export declare const NODE_CLASSES: NodeClass[];
7
7
  export declare const registerNodeClass: <T, P>(nodeClass: NodeClass<T, P>) => void;