@gtkx/react 0.14.0 → 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 (52) hide show
  1. package/README.md +27 -27
  2. package/dist/generated/jsx.d.ts +235 -228
  3. package/dist/host-config.js +2 -2
  4. package/dist/jsx.d.ts +111 -2
  5. package/dist/jsx.js +79 -0
  6. package/dist/nodes/adjustment.d.ts +48 -0
  7. package/dist/nodes/adjustment.js +70 -0
  8. package/dist/nodes/calendar.js +3 -16
  9. package/dist/nodes/column-view-column.d.ts +1 -0
  10. package/dist/nodes/column-view-column.js +4 -0
  11. package/dist/nodes/column-view.js +6 -6
  12. package/dist/nodes/drawing-area.d.ts +1 -0
  13. package/dist/nodes/drawing-area.js +19 -0
  14. package/dist/nodes/expander-row.js +1 -0
  15. package/dist/nodes/index.d.ts +8 -0
  16. package/dist/nodes/index.js +8 -0
  17. package/dist/nodes/internal/constants.js +3 -0
  18. package/dist/nodes/internal/list-item-renderer.d.ts +1 -0
  19. package/dist/nodes/internal/list-item-renderer.js +5 -0
  20. package/dist/nodes/internal/predicates.d.ts +4 -0
  21. package/dist/nodes/internal/predicates.js +3 -0
  22. package/dist/nodes/internal/signal-store.d.ts +1 -0
  23. package/dist/nodes/internal/signal-store.js +7 -0
  24. package/dist/nodes/internal/tree-list-item-renderer.d.ts +1 -0
  25. package/dist/nodes/internal/tree-list-item-renderer.js +8 -0
  26. package/dist/nodes/level-bar.js +3 -16
  27. package/dist/nodes/list-view.js +6 -2
  28. package/dist/nodes/navigation-page.js +32 -23
  29. package/dist/nodes/notebook-page-tab.d.ts +1 -0
  30. package/dist/nodes/notebook-page-tab.js +15 -9
  31. package/dist/nodes/notebook-page.js +9 -6
  32. package/dist/nodes/scale.js +3 -16
  33. package/dist/nodes/scrolled-window.js +3 -2
  34. package/dist/nodes/shortcut-controller.d.ts +37 -0
  35. package/dist/nodes/shortcut-controller.js +74 -0
  36. package/dist/nodes/shortcut.d.ts +38 -0
  37. package/dist/nodes/shortcut.js +46 -0
  38. package/dist/nodes/source-buffer.d.ts +73 -0
  39. package/dist/nodes/source-buffer.js +149 -0
  40. package/dist/nodes/source-view.d.ts +1 -0
  41. package/dist/nodes/source-view.js +42 -0
  42. package/dist/nodes/stack-page.js +15 -9
  43. package/dist/nodes/text-buffer.d.ts +43 -0
  44. package/dist/nodes/text-buffer.js +81 -0
  45. package/dist/nodes/text-view.d.ts +1 -0
  46. package/dist/nodes/text-view.js +45 -0
  47. package/dist/nodes/tree-list-view.js +6 -2
  48. package/dist/nodes/widget.d.ts +4 -0
  49. package/dist/nodes/widget.js +85 -10
  50. package/dist/nodes/window.js +7 -5
  51. package/dist/types.d.ts +26 -0
  52. package/package.json +3 -3
@@ -0,0 +1,149 @@
1
+ import { batch } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
3
+ import * as GtkSource from "@gtkx/ffi/gtksource";
4
+ import { registerNodeClass } from "../registry.js";
5
+ import { signalStore } from "./internal/signal-store.js";
6
+ import { VirtualNode } from "./virtual.js";
7
+ export class SourceBufferNode extends VirtualNode {
8
+ static priority = 1;
9
+ sourceView;
10
+ buffer;
11
+ static matches(type) {
12
+ return type === "SourceBuffer";
13
+ }
14
+ setSourceView(sourceView) {
15
+ this.sourceView = sourceView;
16
+ this.setupBuffer();
17
+ }
18
+ resolveLanguage(language) {
19
+ if (typeof language === "string") {
20
+ const langManager = GtkSource.LanguageManager.getDefault();
21
+ return langManager.getLanguage(language);
22
+ }
23
+ return language;
24
+ }
25
+ resolveStyleScheme(scheme) {
26
+ if (typeof scheme === "string") {
27
+ const schemeManager = GtkSource.StyleSchemeManager.getDefault();
28
+ return schemeManager.getScheme(scheme);
29
+ }
30
+ return scheme;
31
+ }
32
+ setupBuffer() {
33
+ if (!this.sourceView)
34
+ return;
35
+ this.buffer = new GtkSource.Buffer();
36
+ this.sourceView.setBuffer(this.buffer);
37
+ if (this.props.enableUndo !== undefined) {
38
+ this.buffer.setEnableUndo(this.props.enableUndo);
39
+ }
40
+ if (this.props.text !== undefined) {
41
+ this.buffer.setText(this.props.text, -1);
42
+ }
43
+ this.applySourceProps();
44
+ this.updateSignalHandlers();
45
+ }
46
+ applySourceProps() {
47
+ if (!this.buffer)
48
+ return;
49
+ if (this.props.language !== undefined) {
50
+ const language = this.resolveLanguage(this.props.language);
51
+ this.buffer.setLanguage(language);
52
+ }
53
+ if (this.props.styleScheme !== undefined) {
54
+ const scheme = this.resolveStyleScheme(this.props.styleScheme);
55
+ this.buffer.setStyleScheme(scheme);
56
+ }
57
+ const highlightSyntax = this.props.highlightSyntax ?? this.props.language !== undefined;
58
+ this.buffer.setHighlightSyntax(highlightSyntax);
59
+ const highlightMatchingBrackets = this.props.highlightMatchingBrackets ?? true;
60
+ this.buffer.setHighlightMatchingBrackets(highlightMatchingBrackets);
61
+ if (this.props.implicitTrailingNewline !== undefined) {
62
+ this.buffer.setImplicitTrailingNewline(this.props.implicitTrailingNewline);
63
+ }
64
+ }
65
+ getBufferText() {
66
+ const buffer = this.buffer;
67
+ if (!buffer)
68
+ return "";
69
+ const startIter = new Gtk.TextIter();
70
+ const endIter = new Gtk.TextIter();
71
+ batch(() => {
72
+ buffer.getStartIter(startIter);
73
+ buffer.getEndIter(endIter);
74
+ });
75
+ return buffer.getText(startIter, endIter, true);
76
+ }
77
+ updateSignalHandlers() {
78
+ if (!this.buffer)
79
+ return;
80
+ const buffer = this.buffer;
81
+ const { onTextChanged, onCanUndoChanged, onCanRedoChanged, onCursorMoved, onHighlightUpdated } = this.props;
82
+ signalStore.set(this, buffer, "changed", onTextChanged ? () => onTextChanged(this.getBufferText()) : null);
83
+ signalStore.set(this, buffer, "notify::can-undo", onCanUndoChanged ? () => onCanUndoChanged(buffer.getCanUndo()) : null);
84
+ signalStore.set(this, buffer, "notify::can-redo", onCanRedoChanged ? () => onCanRedoChanged(buffer.getCanRedo()) : null);
85
+ signalStore.set(this, buffer, "cursor-moved", onCursorMoved ?? null);
86
+ signalStore.set(this, buffer, "highlight-updated", onHighlightUpdated
87
+ ? (_buffer, start, end) => onHighlightUpdated(start, end)
88
+ : null);
89
+ }
90
+ updateProps(oldProps, newProps) {
91
+ super.updateProps(oldProps, newProps);
92
+ if (!this.buffer)
93
+ return;
94
+ if (!oldProps || oldProps.enableUndo !== newProps.enableUndo) {
95
+ if (newProps.enableUndo !== undefined) {
96
+ this.buffer.setEnableUndo(newProps.enableUndo);
97
+ }
98
+ }
99
+ if (!oldProps || oldProps.text !== newProps.text) {
100
+ if (newProps.text !== undefined) {
101
+ const currentText = this.getBufferText();
102
+ if (currentText !== newProps.text) {
103
+ this.buffer.setText(newProps.text, -1);
104
+ }
105
+ }
106
+ }
107
+ if (!oldProps || oldProps.language !== newProps.language) {
108
+ if (newProps.language !== undefined) {
109
+ const language = this.resolveLanguage(newProps.language);
110
+ this.buffer.setLanguage(language);
111
+ }
112
+ }
113
+ if (!oldProps || oldProps.styleScheme !== newProps.styleScheme) {
114
+ if (newProps.styleScheme !== undefined) {
115
+ const scheme = this.resolveStyleScheme(newProps.styleScheme);
116
+ this.buffer.setStyleScheme(scheme);
117
+ }
118
+ }
119
+ if (!oldProps ||
120
+ oldProps.highlightSyntax !== newProps.highlightSyntax ||
121
+ oldProps.language !== newProps.language) {
122
+ const highlightSyntax = newProps.highlightSyntax ?? newProps.language !== undefined;
123
+ this.buffer.setHighlightSyntax(highlightSyntax);
124
+ }
125
+ if (!oldProps || oldProps.highlightMatchingBrackets !== newProps.highlightMatchingBrackets) {
126
+ const highlightMatchingBrackets = newProps.highlightMatchingBrackets ?? true;
127
+ this.buffer.setHighlightMatchingBrackets(highlightMatchingBrackets);
128
+ }
129
+ if (!oldProps || oldProps.implicitTrailingNewline !== newProps.implicitTrailingNewline) {
130
+ if (newProps.implicitTrailingNewline !== undefined) {
131
+ this.buffer.setImplicitTrailingNewline(newProps.implicitTrailingNewline);
132
+ }
133
+ }
134
+ if (!oldProps ||
135
+ oldProps.onTextChanged !== newProps.onTextChanged ||
136
+ oldProps.onCanUndoChanged !== newProps.onCanUndoChanged ||
137
+ oldProps.onCanRedoChanged !== newProps.onCanRedoChanged ||
138
+ oldProps.onCursorMoved !== newProps.onCursorMoved ||
139
+ oldProps.onHighlightUpdated !== newProps.onHighlightUpdated) {
140
+ this.updateSignalHandlers();
141
+ }
142
+ }
143
+ unmount() {
144
+ this.buffer = undefined;
145
+ this.sourceView = undefined;
146
+ super.unmount();
147
+ }
148
+ }
149
+ registerNodeClass(SourceBufferNode);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import * as GtkSource from "@gtkx/ffi/gtksource";
2
+ import { registerNodeClass } from "../registry.js";
3
+ import { isContainerType } from "./internal/utils.js";
4
+ import { SourceBufferNode } from "./source-buffer.js";
5
+ import { WidgetNode } from "./widget.js";
6
+ class SourceViewNode extends WidgetNode {
7
+ static priority = 1;
8
+ bufferChild;
9
+ static matches(_type, containerOrClass) {
10
+ return isContainerType(GtkSource.View, containerOrClass);
11
+ }
12
+ appendChild(child) {
13
+ if (this.tryAttachSourceBuffer(child))
14
+ return;
15
+ super.appendChild(child);
16
+ }
17
+ insertBefore(child, before) {
18
+ if (this.tryAttachSourceBuffer(child))
19
+ return;
20
+ super.insertBefore(child, before);
21
+ }
22
+ removeChild(child) {
23
+ if (child instanceof SourceBufferNode) {
24
+ if (this.bufferChild === child) {
25
+ this.bufferChild = undefined;
26
+ }
27
+ return;
28
+ }
29
+ super.removeChild(child);
30
+ }
31
+ tryAttachSourceBuffer(child) {
32
+ if (!(child instanceof SourceBufferNode))
33
+ return false;
34
+ if (this.bufferChild) {
35
+ throw new Error("SourceView can only have one SourceBuffer child");
36
+ }
37
+ this.bufferChild = child;
38
+ child.setSourceView(this.container);
39
+ return true;
40
+ }
41
+ }
42
+ registerNodeClass(SourceViewNode);
@@ -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`);
@@ -60,12 +64,12 @@ class TreeListViewNode extends WidgetNode {
60
64
  this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight);
61
65
  }
62
66
  const previousModel = this.treeList.getSelectionModel();
63
- this.treeList.updateProps(oldProps ? filterProps(oldProps, RENDERER_PROP_NAMES) : null, filterProps(newProps, RENDERER_PROP_NAMES));
67
+ this.treeList.updateProps(filterProps(oldProps ?? {}, RENDERER_PROP_NAMES), filterProps(newProps, RENDERER_PROP_NAMES));
64
68
  const currentModel = this.treeList.getSelectionModel();
65
69
  if (previousModel !== currentModel) {
66
70
  this.container.setModel(currentModel);
67
71
  }
68
- super.updateProps(oldProps ? filterProps(oldProps, PROP_NAMES) : null, filterProps(newProps, PROP_NAMES));
72
+ super.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
69
73
  }
70
74
  }
71
75
  registerNodeClass(TreeListViewNode);
@@ -9,6 +9,8 @@ export declare class WidgetNode<T extends Gtk.Widget = Gtk.Widget, P extends Pro
9
9
  private scrollController?;
10
10
  private dragSourceController?;
11
11
  private dropTargetController?;
12
+ private gestureDragController?;
13
+ private adjustmentChild?;
12
14
  static matches(_type: string, containerOrClass?: Container | ContainerClass | null): boolean;
13
15
  static createContainer(props: Props, containerClass: typeof Gtk.Widget): Container | null;
14
16
  appendChild(child: Node): void;
@@ -18,9 +20,11 @@ export declare class WidgetNode<T extends Gtk.Widget = Gtk.Widget, P extends Pro
18
20
  private insertBeforeInsertable;
19
21
  updateProps(oldProps: P | null, newProps: P): void;
20
22
  private updateSizeRequest;
23
+ private updateGrabFocus;
21
24
  private updateEventControllerProp;
22
25
  private ensureDragSource;
23
26
  private ensureDropTarget;
27
+ private ensureGestureDrag;
24
28
  private updateNotifyHandler;
25
29
  private propNameToSignalName;
26
30
  private getProperty;
@@ -4,12 +4,14 @@ 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";
7
8
  import { EVENT_CONTROLLER_PROPS } from "./internal/constants.js";
8
- 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";
9
10
  import { signalStore } from "./internal/signal-store.js";
10
11
  import { filterProps, isContainerType, resolvePropMeta, resolveSignal } from "./internal/utils.js";
12
+ import { ShortcutControllerNode } from "./shortcut-controller.js";
11
13
  import { SlotNode } from "./slot.js";
12
- const PROPS = ["children", "widthRequest", "heightRequest"];
14
+ const PROPS = ["children", "widthRequest", "heightRequest", "grabFocus"];
13
15
  export class WidgetNode extends Node {
14
16
  static priority = 3;
15
17
  motionController;
@@ -18,6 +20,8 @@ export class WidgetNode extends Node {
18
20
  scrollController;
19
21
  dragSourceController;
20
22
  dropTargetController;
23
+ gestureDragController;
24
+ adjustmentChild;
21
25
  static matches(_type, containerOrClass) {
22
26
  return isContainerType(Gtk.Widget, containerOrClass);
23
27
  }
@@ -28,10 +32,25 @@ export class WidgetNode extends Node {
28
32
  return new WidgetClass(...args);
29
33
  }
30
34
  appendChild(child) {
35
+ if (child instanceof ShortcutControllerNode) {
36
+ child.setParent(this.container);
37
+ return;
38
+ }
31
39
  if (child instanceof SlotNode) {
32
40
  child.setParent(this.container);
33
41
  return;
34
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
+ }
35
54
  if (!(child instanceof WidgetNode)) {
36
55
  throw new Error(`Cannot append '${child.typeName}' to 'Widget': expected WidgetNode child`);
37
56
  }
@@ -41,9 +60,19 @@ export class WidgetNode extends Node {
41
60
  batch(() => this.attachChild(child));
42
61
  }
43
62
  removeChild(child) {
63
+ if (child instanceof ShortcutControllerNode) {
64
+ child.setParent(undefined);
65
+ return;
66
+ }
44
67
  if (child instanceof SlotNode) {
45
68
  return;
46
69
  }
70
+ if (child instanceof AdjustmentNode) {
71
+ if (this.adjustmentChild === child) {
72
+ this.adjustmentChild = undefined;
73
+ }
74
+ return;
75
+ }
47
76
  if (!(child instanceof WidgetNode)) {
48
77
  throw new Error(`Cannot remove '${child.typeName}' from 'Widget': expected WidgetNode child`);
49
78
  }
@@ -53,10 +82,25 @@ export class WidgetNode extends Node {
53
82
  batch(() => this.detachChild(child));
54
83
  }
55
84
  insertBefore(child, before) {
85
+ if (child instanceof ShortcutControllerNode) {
86
+ child.setParent(this.container);
87
+ return;
88
+ }
56
89
  if (child instanceof SlotNode) {
57
90
  child.setParent(this.container);
58
91
  return;
59
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
+ }
60
104
  if (!(child instanceof WidgetNode) || !(before instanceof WidgetNode)) {
61
105
  throw new Error(`Cannot insert '${child.typeName}' before '${before.typeName}': expected WidgetNode children`);
62
106
  }
@@ -94,11 +138,13 @@ export class WidgetNode extends Node {
94
138
  }
95
139
  updateProps(oldProps, newProps) {
96
140
  this.updateSizeRequest(oldProps, newProps);
141
+ this.updateGrabFocus(oldProps, newProps);
97
142
  const propNames = new Set([
98
143
  ...Object.keys(filterProps(oldProps ?? {}, PROPS)),
99
144
  ...Object.keys(filterProps(newProps ?? {}, PROPS)),
100
145
  ]);
101
146
  const pendingSignals = [];
147
+ const pendingProperties = [];
102
148
  for (const name of propNames) {
103
149
  const oldValue = oldProps?.[name];
104
150
  const newValue = newProps[name];
@@ -117,14 +163,7 @@ export class WidgetNode extends Node {
117
163
  pendingSignals.push({ name, newValue });
118
164
  }
119
165
  else if (newValue !== undefined) {
120
- const isEditableText = name === "text" && isEditable(this.container);
121
- if (isEditableText && oldValue !== undefined) {
122
- const currentValue = this.getProperty(name);
123
- if (oldValue !== currentValue) {
124
- continue;
125
- }
126
- }
127
- this.setProperty(name, newValue);
166
+ pendingProperties.push({ name, oldValue, newValue });
128
167
  }
129
168
  }
130
169
  for (const { name, newValue } of pendingSignals) {
@@ -140,6 +179,16 @@ export class WidgetNode extends Node {
140
179
  signalStore.set(this, this.container, signalName, handler);
141
180
  }
142
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
+ }
143
192
  }
144
193
  updateSizeRequest(oldProps, newProps) {
145
194
  const oldWidth = oldProps?.widthRequest;
@@ -150,6 +199,13 @@ export class WidgetNode extends Node {
150
199
  this.container.setSizeRequest(newWidth ?? -1, newHeight ?? -1);
151
200
  }
152
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
+ }
153
209
  updateEventControllerProp(propName, handlerOrValue) {
154
210
  const wrappedHandler = typeof handlerOrValue === "function"
155
211
  ? (_self, ...args) => handlerOrValue(...args)
@@ -241,6 +297,18 @@ export class WidgetNode extends Node {
241
297
  }
242
298
  break;
243
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
+ }
244
312
  }
245
313
  }
246
314
  ensureDragSource() {
@@ -258,6 +326,13 @@ export class WidgetNode extends Node {
258
326
  }
259
327
  return this.dropTargetController;
260
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
+ }
261
336
  updateNotifyHandler(handler) {
262
337
  const wrappedHandler = handler
263
338
  ? (obj, pspec) => {
@@ -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));