@gtkx/react 0.13.3 → 0.15.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 (62) hide show
  1. package/README.md +8 -7
  2. package/dist/generated/internal.js +98 -0
  3. package/dist/generated/jsx.d.ts +908 -1572
  4. package/dist/host-config.js +2 -2
  5. package/dist/jsx.d.ts +176 -36
  6. package/dist/jsx.js +96 -5
  7. package/dist/nodes/adjustment.d.ts +48 -0
  8. package/dist/nodes/adjustment.js +70 -0
  9. package/dist/nodes/application.js +6 -6
  10. package/dist/nodes/calendar.js +3 -16
  11. package/dist/nodes/column-view-column.d.ts +1 -0
  12. package/dist/nodes/column-view-column.js +4 -0
  13. package/dist/nodes/column-view.js +6 -6
  14. package/dist/nodes/drawing-area.d.ts +1 -0
  15. package/dist/nodes/drawing-area.js +19 -0
  16. package/dist/nodes/expander-row.js +1 -0
  17. package/dist/nodes/index.d.ts +9 -0
  18. package/dist/nodes/index.js +9 -0
  19. package/dist/nodes/internal/constants.js +14 -0
  20. package/dist/nodes/internal/list-item-renderer.d.ts +1 -0
  21. package/dist/nodes/internal/list-item-renderer.js +6 -1
  22. package/dist/nodes/internal/predicates.d.ts +4 -0
  23. package/dist/nodes/internal/predicates.js +3 -0
  24. package/dist/nodes/internal/signal-store.d.ts +1 -0
  25. package/dist/nodes/internal/signal-store.js +7 -0
  26. package/dist/nodes/internal/tree-list-item-renderer.d.ts +1 -0
  27. package/dist/nodes/internal/tree-list-item-renderer.js +9 -1
  28. package/dist/nodes/level-bar.js +3 -16
  29. package/dist/nodes/list-view.js +11 -2
  30. package/dist/nodes/models/list.d.ts +6 -1
  31. package/dist/nodes/models/list.js +21 -6
  32. package/dist/nodes/models/tree-list.d.ts +6 -1
  33. package/dist/nodes/models/tree-list.js +21 -7
  34. package/dist/nodes/navigation-page.js +32 -23
  35. package/dist/nodes/notebook-page-tab.d.ts +1 -0
  36. package/dist/nodes/notebook-page-tab.js +15 -9
  37. package/dist/nodes/notebook-page.js +9 -6
  38. package/dist/nodes/scale.js +3 -16
  39. package/dist/nodes/scrolled-window.d.ts +1 -0
  40. package/dist/nodes/scrolled-window.js +22 -0
  41. package/dist/nodes/shortcut-controller.d.ts +37 -0
  42. package/dist/nodes/shortcut-controller.js +74 -0
  43. package/dist/nodes/shortcut.d.ts +38 -0
  44. package/dist/nodes/shortcut.js +46 -0
  45. package/dist/nodes/slot.js +11 -2
  46. package/dist/nodes/source-buffer.d.ts +73 -0
  47. package/dist/nodes/source-buffer.js +149 -0
  48. package/dist/nodes/source-view.d.ts +1 -0
  49. package/dist/nodes/source-view.js +42 -0
  50. package/dist/nodes/stack-page.js +15 -9
  51. package/dist/nodes/text-buffer.d.ts +43 -0
  52. package/dist/nodes/text-buffer.js +81 -0
  53. package/dist/nodes/text-view.d.ts +1 -0
  54. package/dist/nodes/text-view.js +45 -0
  55. package/dist/nodes/tree-list-view.js +11 -2
  56. package/dist/nodes/widget.d.ts +9 -0
  57. package/dist/nodes/widget.js +166 -13
  58. package/dist/nodes/window.js +7 -5
  59. package/dist/render.d.ts +14 -14
  60. package/dist/render.js +14 -14
  61. package/dist/types.d.ts +110 -1
  62. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import { isObjectEqual } from "@gtkx/ffi";
1
+ import { batch, isObjectEqual } from "@gtkx/ffi";
2
2
  import * as Adw from "@gtkx/ffi/adw";
3
3
  import { registerNodeClass } from "../registry.js";
4
4
  import { SlotNode } from "./slot.js";
@@ -13,11 +13,15 @@ class StackPageNode extends SlotNode {
13
13
  if (!this.page) {
14
14
  return;
15
15
  }
16
- if (newProps.title && (!oldProps || oldProps.title !== newProps.title)) {
17
- this.page.setTitle(newProps.title);
16
+ if (!oldProps || oldProps.title !== newProps.title) {
17
+ if (newProps.title !== undefined) {
18
+ this.page.setTitle(newProps.title);
19
+ }
18
20
  }
19
- if (newProps.iconName && (!oldProps || oldProps.iconName !== newProps.iconName)) {
20
- this.page.setIconName(newProps.iconName);
21
+ if (!oldProps || oldProps.iconName !== newProps.iconName) {
22
+ if (newProps.iconName !== undefined) {
23
+ this.page.setIconName(newProps.iconName);
24
+ }
21
25
  }
22
26
  if (!oldProps || oldProps.needsAttention !== newProps.needsAttention) {
23
27
  this.page.setNeedsAttention(newProps.needsAttention ?? false);
@@ -75,10 +79,12 @@ class StackPageNode extends SlotNode {
75
79
  }
76
80
  }
77
81
  onChildChange(oldChild) {
78
- this.removePage(oldChild);
79
- if (this.child) {
80
- this.addPage();
81
- }
82
+ batch(() => {
83
+ this.removePage(oldChild);
84
+ if (this.child) {
85
+ this.addPage();
86
+ }
87
+ });
82
88
  }
83
89
  }
84
90
  registerNodeClass(StackPageNode);
@@ -0,0 +1,43 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { VirtualNode } from "./virtual.js";
3
+ /**
4
+ * Props for the TextBuffer virtual element.
5
+ *
6
+ * Used to declaratively configure the text buffer for a GtkTextView.
7
+ * For GtkSourceView with syntax highlighting, use {@link SourceBufferProps} instead.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <GtkTextView>
12
+ * <x.TextBuffer
13
+ * text="Hello, World!"
14
+ * enableUndo
15
+ * onTextChanged={(text) => console.log("Text:", text)}
16
+ * />
17
+ * </GtkTextView>
18
+ * ```
19
+ */
20
+ export type TextBufferProps = {
21
+ /** Text content */
22
+ text?: string;
23
+ /** Whether to enable undo/redo */
24
+ enableUndo?: boolean;
25
+ /** Callback when the text content changes */
26
+ onTextChanged?: (text: string) => void;
27
+ /** Callback when can-undo state changes */
28
+ onCanUndoChanged?: (canUndo: boolean) => void;
29
+ /** Callback when can-redo state changes */
30
+ onCanRedoChanged?: (canRedo: boolean) => void;
31
+ };
32
+ export declare class TextBufferNode extends VirtualNode<TextBufferProps> {
33
+ static priority: number;
34
+ private textView?;
35
+ private buffer?;
36
+ static matches(type: string): boolean;
37
+ setTextView(textView: Gtk.TextView): void;
38
+ private setupBuffer;
39
+ private getBufferText;
40
+ private updateSignalHandlers;
41
+ updateProps(oldProps: TextBufferProps | null, newProps: TextBufferProps): void;
42
+ unmount(): void;
43
+ }
@@ -0,0 +1,81 @@
1
+ import { batch } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { registerNodeClass } from "../registry.js";
4
+ import { signalStore } from "./internal/signal-store.js";
5
+ import { VirtualNode } from "./virtual.js";
6
+ export class TextBufferNode extends VirtualNode {
7
+ static priority = 1;
8
+ textView;
9
+ buffer;
10
+ static matches(type) {
11
+ return type === "TextBuffer";
12
+ }
13
+ setTextView(textView) {
14
+ this.textView = textView;
15
+ this.setupBuffer();
16
+ }
17
+ setupBuffer() {
18
+ if (!this.textView)
19
+ return;
20
+ this.buffer = new Gtk.TextBuffer();
21
+ this.textView.setBuffer(this.buffer);
22
+ if (this.props.enableUndo !== undefined) {
23
+ this.buffer.setEnableUndo(this.props.enableUndo);
24
+ }
25
+ if (this.props.text !== undefined) {
26
+ this.buffer.setText(this.props.text, -1);
27
+ }
28
+ this.updateSignalHandlers();
29
+ }
30
+ getBufferText() {
31
+ const buffer = this.buffer;
32
+ if (!buffer)
33
+ return "";
34
+ const startIter = new Gtk.TextIter();
35
+ const endIter = new Gtk.TextIter();
36
+ batch(() => {
37
+ buffer.getStartIter(startIter);
38
+ buffer.getEndIter(endIter);
39
+ });
40
+ return buffer.getText(startIter, endIter, true);
41
+ }
42
+ updateSignalHandlers() {
43
+ if (!this.buffer)
44
+ return;
45
+ const buffer = this.buffer;
46
+ const { onTextChanged, onCanUndoChanged, onCanRedoChanged } = this.props;
47
+ signalStore.set(this, buffer, "changed", onTextChanged ? () => onTextChanged(this.getBufferText()) : null);
48
+ signalStore.set(this, buffer, "notify::can-undo", onCanUndoChanged ? () => onCanUndoChanged(buffer.getCanUndo()) : null);
49
+ signalStore.set(this, buffer, "notify::can-redo", onCanRedoChanged ? () => onCanRedoChanged(buffer.getCanRedo()) : null);
50
+ }
51
+ updateProps(oldProps, newProps) {
52
+ super.updateProps(oldProps, newProps);
53
+ if (!this.buffer)
54
+ return;
55
+ if (!oldProps || oldProps.enableUndo !== newProps.enableUndo) {
56
+ if (newProps.enableUndo !== undefined) {
57
+ this.buffer.setEnableUndo(newProps.enableUndo);
58
+ }
59
+ }
60
+ if (!oldProps || oldProps.text !== newProps.text) {
61
+ if (newProps.text !== undefined) {
62
+ const currentText = this.getBufferText();
63
+ if (currentText !== newProps.text) {
64
+ this.buffer.setText(newProps.text, -1);
65
+ }
66
+ }
67
+ }
68
+ if (!oldProps ||
69
+ oldProps.onTextChanged !== newProps.onTextChanged ||
70
+ oldProps.onCanUndoChanged !== newProps.onCanUndoChanged ||
71
+ oldProps.onCanRedoChanged !== newProps.onCanRedoChanged) {
72
+ this.updateSignalHandlers();
73
+ }
74
+ }
75
+ unmount() {
76
+ this.buffer = undefined;
77
+ this.textView = undefined;
78
+ super.unmount();
79
+ }
80
+ }
81
+ registerNodeClass(TextBufferNode);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import * as GtkSource from "@gtkx/ffi/gtksource";
3
+ import { registerNodeClass } from "../registry.js";
4
+ import { isContainerType } from "./internal/utils.js";
5
+ import { TextBufferNode } from "./text-buffer.js";
6
+ import { WidgetNode } from "./widget.js";
7
+ class TextViewNode extends WidgetNode {
8
+ static priority = 1;
9
+ bufferChild;
10
+ static matches(_type, containerOrClass) {
11
+ if (isContainerType(GtkSource.View, containerOrClass))
12
+ return false;
13
+ return isContainerType(Gtk.TextView, containerOrClass);
14
+ }
15
+ appendChild(child) {
16
+ if (this.tryAttachTextBuffer(child))
17
+ return;
18
+ super.appendChild(child);
19
+ }
20
+ insertBefore(child, before) {
21
+ if (this.tryAttachTextBuffer(child))
22
+ return;
23
+ super.insertBefore(child, before);
24
+ }
25
+ removeChild(child) {
26
+ if (child instanceof TextBufferNode) {
27
+ if (this.bufferChild === child) {
28
+ this.bufferChild = undefined;
29
+ }
30
+ return;
31
+ }
32
+ super.removeChild(child);
33
+ }
34
+ tryAttachTextBuffer(child) {
35
+ if (!(child instanceof TextBufferNode))
36
+ return false;
37
+ if (this.bufferChild) {
38
+ throw new Error("TextView can only have one TextBuffer child");
39
+ }
40
+ this.bufferChild = child;
41
+ child.setTextView(this.container);
42
+ return true;
43
+ }
44
+ }
45
+ registerNodeClass(TextViewNode);
@@ -34,6 +34,10 @@ class TreeListViewNode extends WidgetNode {
34
34
  super.mount();
35
35
  this.container.setModel(this.treeList.getSelectionModel());
36
36
  }
37
+ unmount() {
38
+ this.itemRenderer.dispose();
39
+ super.unmount();
40
+ }
37
41
  appendChild(child) {
38
42
  if (!(child instanceof TreeListItemNode)) {
39
43
  throw new Error(`Cannot append '${child.typeName}' to 'TreeListView': expected x.TreeListItem`);
@@ -59,8 +63,13 @@ class TreeListViewNode extends WidgetNode {
59
63
  if (!oldProps || oldProps.estimatedItemHeight !== newProps.estimatedItemHeight) {
60
64
  this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight);
61
65
  }
62
- this.treeList.updateProps(oldProps ? filterProps(oldProps, RENDERER_PROP_NAMES) : null, filterProps(newProps, RENDERER_PROP_NAMES));
63
- super.updateProps(oldProps ? filterProps(oldProps, PROP_NAMES) : null, filterProps(newProps, PROP_NAMES));
66
+ const previousModel = this.treeList.getSelectionModel();
67
+ this.treeList.updateProps(filterProps(oldProps ?? {}, RENDERER_PROP_NAMES), filterProps(newProps, RENDERER_PROP_NAMES));
68
+ const currentModel = this.treeList.getSelectionModel();
69
+ if (previousModel !== currentModel) {
70
+ this.container.setModel(currentModel);
71
+ }
72
+ super.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
64
73
  }
65
74
  }
66
75
  registerNodeClass(TreeListViewNode);
@@ -7,6 +7,10 @@ export declare class WidgetNode<T extends Gtk.Widget = Gtk.Widget, P extends Pro
7
7
  private clickController?;
8
8
  private keyController?;
9
9
  private scrollController?;
10
+ private dragSourceController?;
11
+ private dropTargetController?;
12
+ private gestureDragController?;
13
+ private adjustmentChild?;
10
14
  static matches(_type: string, containerOrClass?: Container | ContainerClass | null): boolean;
11
15
  static createContainer(props: Props, containerClass: typeof Gtk.Widget): Container | null;
12
16
  appendChild(child: Node): void;
@@ -15,7 +19,12 @@ export declare class WidgetNode<T extends Gtk.Widget = Gtk.Widget, P extends Pro
15
19
  private insertBeforeReorderable;
16
20
  private insertBeforeInsertable;
17
21
  updateProps(oldProps: P | null, newProps: P): void;
22
+ private updateSizeRequest;
23
+ private updateGrabFocus;
18
24
  private updateEventControllerProp;
25
+ private ensureDragSource;
26
+ private ensureDropTarget;
27
+ private ensureGestureDrag;
19
28
  private updateNotifyHandler;
20
29
  private propNameToSignalName;
21
30
  private getProperty;
@@ -1,19 +1,27 @@
1
1
  import { batch, isObjectEqual, NativeObject } from "@gtkx/ffi";
2
+ import * as Gdk from "@gtkx/ffi/gdk";
2
3
  import * as Gtk from "@gtkx/ffi/gtk";
3
4
  import { CONSTRUCTOR_PROPS } from "../generated/internal.js";
4
5
  import { Node } from "../node.js";
5
6
  import { registerNodeClass } from "../registry.js";
7
+ import { AdjustmentNode } from "./adjustment.js";
6
8
  import { EVENT_CONTROLLER_PROPS } from "./internal/constants.js";
7
- import { hasSingleContent, isAddable, isAppendable, isEditable, isInsertable, isRemovable, isReorderable, isSingleChild, } from "./internal/predicates.js";
9
+ import { hasSingleContent, isAddable, isAdjustable, isAppendable, isEditable, isInsertable, isRemovable, isReorderable, isSingleChild, } from "./internal/predicates.js";
8
10
  import { signalStore } from "./internal/signal-store.js";
9
11
  import { filterProps, isContainerType, resolvePropMeta, resolveSignal } from "./internal/utils.js";
12
+ import { ShortcutControllerNode } from "./shortcut-controller.js";
10
13
  import { SlotNode } from "./slot.js";
14
+ const PROPS = ["children", "widthRequest", "heightRequest", "grabFocus"];
11
15
  export class WidgetNode extends Node {
12
16
  static priority = 3;
13
17
  motionController;
14
18
  clickController;
15
19
  keyController;
16
20
  scrollController;
21
+ dragSourceController;
22
+ dropTargetController;
23
+ gestureDragController;
24
+ adjustmentChild;
17
25
  static matches(_type, containerOrClass) {
18
26
  return isContainerType(Gtk.Widget, containerOrClass);
19
27
  }
@@ -24,10 +32,25 @@ export class WidgetNode extends Node {
24
32
  return new WidgetClass(...args);
25
33
  }
26
34
  appendChild(child) {
35
+ if (child instanceof ShortcutControllerNode) {
36
+ child.setParent(this.container);
37
+ return;
38
+ }
27
39
  if (child instanceof SlotNode) {
28
40
  child.setParent(this.container);
29
41
  return;
30
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);
52
+ return;
53
+ }
31
54
  if (!(child instanceof WidgetNode)) {
32
55
  throw new Error(`Cannot append '${child.typeName}' to 'Widget': expected WidgetNode child`);
33
56
  }
@@ -37,9 +60,19 @@ export class WidgetNode extends Node {
37
60
  batch(() => this.attachChild(child));
38
61
  }
39
62
  removeChild(child) {
63
+ if (child instanceof ShortcutControllerNode) {
64
+ child.setParent(undefined);
65
+ return;
66
+ }
40
67
  if (child instanceof SlotNode) {
41
68
  return;
42
69
  }
70
+ if (child instanceof AdjustmentNode) {
71
+ if (this.adjustmentChild === child) {
72
+ this.adjustmentChild = undefined;
73
+ }
74
+ return;
75
+ }
43
76
  if (!(child instanceof WidgetNode)) {
44
77
  throw new Error(`Cannot remove '${child.typeName}' from 'Widget': expected WidgetNode child`);
45
78
  }
@@ -49,10 +82,25 @@ export class WidgetNode extends Node {
49
82
  batch(() => this.detachChild(child));
50
83
  }
51
84
  insertBefore(child, before) {
85
+ if (child instanceof ShortcutControllerNode) {
86
+ child.setParent(this.container);
87
+ return;
88
+ }
52
89
  if (child instanceof SlotNode) {
53
90
  child.setParent(this.container);
54
91
  return;
55
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);
102
+ return;
103
+ }
56
104
  if (!(child instanceof WidgetNode) || !(before instanceof WidgetNode)) {
57
105
  throw new Error(`Cannot insert '${child.typeName}' before '${before.typeName}': expected WidgetNode children`);
58
106
  }
@@ -89,11 +137,14 @@ export class WidgetNode extends Node {
89
137
  container.insert(child.container, position);
90
138
  }
91
139
  updateProps(oldProps, newProps) {
140
+ this.updateSizeRequest(oldProps, newProps);
141
+ this.updateGrabFocus(oldProps, newProps);
92
142
  const propNames = new Set([
93
- ...Object.keys(filterProps(oldProps ?? {}, ["children"])),
94
- ...Object.keys(filterProps(newProps ?? {}, ["children"])),
143
+ ...Object.keys(filterProps(oldProps ?? {}, PROPS)),
144
+ ...Object.keys(filterProps(newProps ?? {}, PROPS)),
95
145
  ]);
96
146
  const pendingSignals = [];
147
+ const pendingProperties = [];
97
148
  for (const name of propNames) {
98
149
  const oldValue = oldProps?.[name];
99
150
  const newValue = newProps[name];
@@ -112,14 +163,7 @@ export class WidgetNode extends Node {
112
163
  pendingSignals.push({ name, newValue });
113
164
  }
114
165
  else if (newValue !== undefined) {
115
- const isEditableText = name === "text" && isEditable(this.container);
116
- if (isEditableText && oldValue !== undefined) {
117
- const currentValue = this.getProperty(name);
118
- if (oldValue !== currentValue) {
119
- continue;
120
- }
121
- }
122
- this.setProperty(name, newValue);
166
+ pendingProperties.push({ name, oldValue, newValue });
123
167
  }
124
168
  }
125
169
  for (const { name, newValue } of pendingSignals) {
@@ -135,9 +179,37 @@ export class WidgetNode extends Node {
135
179
  signalStore.set(this, this.container, signalName, handler);
136
180
  }
137
181
  }
182
+ 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) {
187
+ continue;
188
+ }
189
+ }
190
+ this.setProperty(name, newValue);
191
+ }
138
192
  }
139
- updateEventControllerProp(propName, handler) {
140
- const wrappedHandler = handler ? (_self, ...args) => handler(...args) : undefined;
193
+ updateSizeRequest(oldProps, newProps) {
194
+ const oldWidth = oldProps?.widthRequest;
195
+ const oldHeight = oldProps?.heightRequest;
196
+ const newWidth = newProps.widthRequest;
197
+ const newHeight = newProps.heightRequest;
198
+ if (oldWidth !== newWidth || oldHeight !== newHeight) {
199
+ this.container.setSizeRequest(newWidth ?? -1, newHeight ?? -1);
200
+ }
201
+ }
202
+ updateGrabFocus(oldProps, newProps) {
203
+ const oldGrabFocus = oldProps?.grabFocus;
204
+ const newGrabFocus = newProps.grabFocus;
205
+ if (!oldGrabFocus && newGrabFocus) {
206
+ this.container.grabFocus();
207
+ }
208
+ }
209
+ updateEventControllerProp(propName, handlerOrValue) {
210
+ const wrappedHandler = typeof handlerOrValue === "function"
211
+ ? (_self, ...args) => handlerOrValue(...args)
212
+ : undefined;
141
213
  switch (propName) {
142
214
  case "onEnter":
143
215
  case "onLeave":
@@ -178,7 +250,88 @@ export class WidgetNode extends Node {
178
250
  signalStore.set(this, this.scrollController, "scroll", wrappedHandler);
179
251
  break;
180
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);
181
333
  }
334
+ return this.gestureDragController;
182
335
  }
183
336
  updateNotifyHandler(handler) {
184
337
  const wrappedHandler = handler
@@ -68,12 +68,14 @@ export class WindowNode extends WidgetNode {
68
68
  const height = newProps.defaultHeight ?? -1;
69
69
  this.container.setDefaultSize(width, height);
70
70
  }
71
- if (oldProps?.onClose !== newProps.onClose) {
71
+ if (!oldProps || oldProps.onClose !== newProps.onClose) {
72
72
  const userHandler = newProps.onClose;
73
- const wrappedHandler = () => {
74
- userHandler?.();
75
- return true;
76
- };
73
+ const wrappedHandler = userHandler
74
+ ? () => {
75
+ userHandler();
76
+ return true;
77
+ }
78
+ : undefined;
77
79
  signalStore.set(this, this.container, "close-request", wrappedHandler);
78
80
  }
79
81
  super.updateProps(filterProps(oldProps ?? {}, PROPS), filterProps(newProps, PROPS));
package/dist/render.d.ts CHANGED
@@ -9,9 +9,9 @@ import { type ReactNode } from "react";
9
9
  * @example
10
10
  * ```tsx
11
11
  * const App = () => {
12
- * const app = useApplication();
13
- * console.log(app.applicationId);
14
- * return <GtkLabel label="Hello" />;
12
+ * const app = useApplication();
13
+ * console.log(app.applicationId);
14
+ * return <GtkLabel label="Hello" />;
15
15
  * };
16
16
  * ```
17
17
  */
@@ -27,8 +27,8 @@ export declare const ApplicationContext: React.Context<Gtk.Application | null>;
27
27
  * @example
28
28
  * ```tsx
29
29
  * const MyComponent = () => {
30
- * const app = useApplication();
31
- * return <GtkLabel label={app.applicationId} />;
30
+ * const app = useApplication();
31
+ * return <GtkLabel label={app.applicationId} />;
32
32
  * };
33
33
  * ```
34
34
  *
@@ -69,9 +69,9 @@ export declare const setHotReloading: (value: boolean) => void;
69
69
  * import { render, quit } from "@gtkx/react";
70
70
  *
71
71
  * const App = () => (
72
- * <GtkApplicationWindow title="My App" onClose={quit}>
73
- * <GtkLabel label="Hello, GTKX!" />
74
- * </GtkApplicationWindow>
72
+ * <GtkApplicationWindow title="My App" onClose={quit}>
73
+ * <GtkLabel label="Hello, GTKX!" />
74
+ * </GtkApplicationWindow>
75
75
  * );
76
76
  *
77
77
  * render(<App />, "com.example.myapp");
@@ -94,9 +94,9 @@ export declare const render: (element: ReactNode, appId: string, flags?: Gio.App
94
94
  * ```tsx
95
95
  * // In HMR handler
96
96
  * if (import.meta.hot) {
97
- * import.meta.hot.accept(() => {
98
- * update(<App />);
99
- * });
97
+ * import.meta.hot.accept(() => {
98
+ * update(<App />);
99
+ * });
100
100
  * }
101
101
  * ```
102
102
  *
@@ -114,9 +114,9 @@ export declare const update: (element: ReactNode) => Promise<void>;
114
114
  * import { quit } from "@gtkx/react";
115
115
  *
116
116
  * const App = () => (
117
- * <GtkApplicationWindow title="My App" onClose={quit}>
118
- * <GtkButton label="Quit" onClicked={quit} />
119
- * </GtkApplicationWindow>
117
+ * <GtkApplicationWindow title="My App" onClose={quit}>
118
+ * <GtkButton label="Quit" onClicked={quit} />
119
+ * </GtkApplicationWindow>
120
120
  * );
121
121
  * ```
122
122
  *
package/dist/render.js CHANGED
@@ -11,9 +11,9 @@ import { reconciler } from "./reconciler.js";
11
11
  * @example
12
12
  * ```tsx
13
13
  * const App = () => {
14
- * const app = useApplication();
15
- * console.log(app.applicationId);
16
- * return <GtkLabel label="Hello" />;
14
+ * const app = useApplication();
15
+ * console.log(app.applicationId);
16
+ * return <GtkLabel label="Hello" />;
17
17
  * };
18
18
  * ```
19
19
  */
@@ -29,8 +29,8 @@ export const ApplicationContext = createContext(null);
29
29
  * @example
30
30
  * ```tsx
31
31
  * const MyComponent = () => {
32
- * const app = useApplication();
33
- * return <GtkLabel label={app.applicationId} />;
32
+ * const app = useApplication();
33
+ * return <GtkLabel label={app.applicationId} />;
34
34
  * };
35
35
  * ```
36
36
  *
@@ -84,9 +84,9 @@ export const setHotReloading = (value) => {
84
84
  * import { render, quit } from "@gtkx/react";
85
85
  *
86
86
  * const App = () => (
87
- * <GtkApplicationWindow title="My App" onClose={quit}>
88
- * <GtkLabel label="Hello, GTKX!" />
89
- * </GtkApplicationWindow>
87
+ * <GtkApplicationWindow title="My App" onClose={quit}>
88
+ * <GtkLabel label="Hello, GTKX!" />
89
+ * </GtkApplicationWindow>
90
90
  * );
91
91
  *
92
92
  * render(<App />, "com.example.myapp");
@@ -121,9 +121,9 @@ export const render = (element, appId, flags) => {
121
121
  * ```tsx
122
122
  * // In HMR handler
123
123
  * if (import.meta.hot) {
124
- * import.meta.hot.accept(() => {
125
- * update(<App />);
126
- * });
124
+ * import.meta.hot.accept(() => {
125
+ * update(<App />);
126
+ * });
127
127
  * }
128
128
  * ```
129
129
  *
@@ -147,9 +147,9 @@ export const update = (element) => {
147
147
  * import { quit } from "@gtkx/react";
148
148
  *
149
149
  * const App = () => (
150
- * <GtkApplicationWindow title="My App" onClose={quit}>
151
- * <GtkButton label="Quit" onClicked={quit} />
152
- * </GtkApplicationWindow>
150
+ * <GtkApplicationWindow title="My App" onClose={quit}>
151
+ * <GtkButton label="Quit" onClicked={quit} />
152
+ * </GtkApplicationWindow>
153
153
  * );
154
154
  * ```
155
155
  *