@gtkx/react 0.10.5 → 0.11.1

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 (55) hide show
  1. package/README.md +103 -0
  2. package/dist/factory.js +1 -0
  3. package/dist/generated/internal.d.ts +27 -1
  4. package/dist/generated/internal.js +920 -9714
  5. package/dist/generated/jsx.d.ts +8029 -17908
  6. package/dist/generated/jsx.js +70 -9013
  7. package/dist/generated/registry.d.ts +0 -1
  8. package/dist/node.d.ts +1 -1
  9. package/dist/nodes/action-row-child.js +3 -2
  10. package/dist/nodes/action-row.js +3 -7
  11. package/dist/nodes/application.js +1 -1
  12. package/dist/nodes/autowrapped.js +7 -5
  13. package/dist/nodes/column-view-column.js +1 -1
  14. package/dist/nodes/column-view.js +4 -3
  15. package/dist/nodes/fixed-child.js +3 -3
  16. package/dist/nodes/grid-child.js +4 -4
  17. package/dist/nodes/internal/constants.d.ts +1 -0
  18. package/dist/nodes/internal/constants.js +10 -0
  19. package/dist/nodes/internal/list-item-renderer.d.ts +1 -1
  20. package/dist/nodes/internal/list-item-renderer.js +4 -2
  21. package/dist/nodes/internal/predicates.d.ts +18 -9
  22. package/dist/nodes/internal/signal-store.d.ts +1 -1
  23. package/dist/nodes/internal/simple-list-store.d.ts +2 -2
  24. package/dist/nodes/internal/simple-list-store.js +2 -2
  25. package/dist/nodes/internal/tree-list-item-renderer.d.ts +1 -1
  26. package/dist/nodes/internal/tree-list-item-renderer.js +7 -3
  27. package/dist/nodes/internal/tree-store.d.ts +1 -1
  28. package/dist/nodes/internal/tree-store.js +1 -1
  29. package/dist/nodes/internal/utils.d.ts +7 -1
  30. package/dist/nodes/internal/utils.js +33 -6
  31. package/dist/nodes/list-item.d.ts +1 -1
  32. package/dist/nodes/list-view.js +3 -3
  33. package/dist/nodes/models/list.js +3 -3
  34. package/dist/nodes/models/menu.js +2 -2
  35. package/dist/nodes/models/tree-list.js +11 -10
  36. package/dist/nodes/notebook-page-tab.d.ts +1 -1
  37. package/dist/nodes/notebook-page.d.ts +3 -3
  38. package/dist/nodes/notebook-page.js +2 -2
  39. package/dist/nodes/notebook.js +4 -4
  40. package/dist/nodes/overlay-child.js +2 -1
  41. package/dist/nodes/pack-child.js +3 -2
  42. package/dist/nodes/pack.js +3 -7
  43. package/dist/nodes/popover-menu.js +3 -4
  44. package/dist/nodes/simple-list-view.js +6 -7
  45. package/dist/nodes/slot.d.ts +1 -1
  46. package/dist/nodes/slot.js +9 -7
  47. package/dist/nodes/stack-page.js +2 -1
  48. package/dist/nodes/stack.js +3 -4
  49. package/dist/nodes/toolbar-child.js +2 -1
  50. package/dist/nodes/tree-list-item.d.ts +3 -3
  51. package/dist/nodes/tree-list-item.js +3 -3
  52. package/dist/nodes/widget.d.ts +10 -2
  53. package/dist/nodes/widget.js +112 -111
  54. package/dist/nodes/window.js +1 -1
  55. package/package.json +6 -7
@@ -1,12 +1,12 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { NOTEBOOK_CLASSES } from "../generated/internal.js";
2
2
  import { registerNodeClass } from "../registry.js";
3
- import { isContainerType } from "./internal/utils.js";
3
+ import { matchesAnyClass } from "./internal/utils.js";
4
4
  import { NotebookPageNode } from "./notebook-page.js";
5
5
  import { WidgetNode } from "./widget.js";
6
6
  class NotebookNode extends WidgetNode {
7
7
  static priority = 1;
8
8
  static matches(_type, containerOrClass) {
9
- return isContainerType(Gtk.Notebook, containerOrClass);
9
+ return matchesAnyClass(NOTEBOOK_CLASSES, containerOrClass);
10
10
  }
11
11
  appendChild(child) {
12
12
  if (!(child instanceof NotebookPageNode)) {
@@ -26,7 +26,7 @@ class NotebookNode extends WidgetNode {
26
26
  if (!(child instanceof NotebookPageNode)) {
27
27
  throw new Error(`Cannot remove '${child.typeName}' from 'Notebook': expected Notebook.Page`);
28
28
  }
29
- child.setPosition(undefined);
29
+ child.setPosition(null);
30
30
  }
31
31
  }
32
32
  registerNodeClass(NotebookNode);
@@ -1,3 +1,4 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { SlotNode } from "./slot.js";
3
4
  class OverlayChildNode extends SlotNode {
@@ -15,7 +16,7 @@ class OverlayChildNode extends SlotNode {
15
16
  const overlay = this.getOverlay();
16
17
  if (oldChild) {
17
18
  const parent = oldChild.getParent();
18
- if (parent?.equals(overlay)) {
19
+ if (parent && isObjectEqual(parent, overlay)) {
19
20
  overlay.removeOverlay(oldChild);
20
21
  }
21
22
  }
@@ -1,3 +1,4 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { scheduleAfterCommit } from "../scheduler.js";
3
4
  import { VirtualNode } from "./virtual.js";
@@ -22,7 +23,7 @@ export class PackChild extends VirtualNode {
22
23
  scheduleAfterCommit(() => {
23
24
  for (const widget of childrenToRemove) {
24
25
  const currentParent = widget.getParent();
25
- if (currentParent?.equals(parent)) {
26
+ if (currentParent && isObjectEqual(currentParent, parent)) {
26
27
  parent.remove(widget);
27
28
  }
28
29
  }
@@ -65,7 +66,7 @@ export class PackChild extends VirtualNode {
65
66
  scheduleAfterCommit(() => {
66
67
  if (parent) {
67
68
  const currentParent = widget.getParent();
68
- if (currentParent?.equals(parent)) {
69
+ if (currentParent && isObjectEqual(currentParent, parent)) {
69
70
  parent.remove(widget);
70
71
  }
71
72
  }
@@ -1,16 +1,12 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { PACK_INTERFACE_METHODS } from "../generated/internal.js";
2
2
  import { registerNodeClass } from "../registry.js";
3
+ import { matchesInterface } from "./internal/utils.js";
3
4
  import { PackChild } from "./pack-child.js";
4
5
  import { WidgetNode } from "./widget.js";
5
6
  class PackNode extends WidgetNode {
6
7
  static priority = 0;
7
8
  static matches(_type, containerOrClass) {
8
- if (!containerOrClass ||
9
- (typeof containerOrClass !== "function" && !(containerOrClass instanceof Gtk.Widget))) {
10
- return false;
11
- }
12
- const protoOrInstance = typeof containerOrClass === "function" ? containerOrClass.prototype : containerOrClass;
13
- return "packStart" in protoOrInstance && "packEnd" in protoOrInstance && "remove" in protoOrInstance;
9
+ return matchesInterface(PACK_INTERFACE_METHODS, containerOrClass);
14
10
  }
15
11
  appendChild(child) {
16
12
  if (child instanceof PackChild) {
@@ -1,7 +1,8 @@
1
1
  import * as Gio from "@gtkx/ffi/gio";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { POPOVER_MENU_CLASSES } from "../generated/internal.js";
3
4
  import { registerNodeClass } from "../registry.js";
4
- import { isContainerType } from "./internal/utils.js";
5
+ import { matchesAnyClass } from "./internal/utils.js";
5
6
  import { MenuNode } from "./menu.js";
6
7
  import { Menu } from "./models/menu.js";
7
8
  import { SlotNode } from "./slot.js";
@@ -11,9 +12,7 @@ class PopoverMenuNode extends WidgetNode {
11
12
  static priority = 1;
12
13
  menu;
13
14
  static matches(_type, containerOrClass) {
14
- return (isContainerType(Gtk.PopoverMenu, containerOrClass) ||
15
- isContainerType(Gtk.PopoverMenuBar, containerOrClass) ||
16
- isContainerType(Gtk.MenuButton, containerOrClass));
15
+ return matchesAnyClass(POPOVER_MENU_CLASSES, containerOrClass);
17
16
  }
18
17
  constructor(typeName, props, container, rootContainer) {
19
18
  super(typeName, props, container, rootContainer);
@@ -1,16 +1,15 @@
1
- import * as Adw from "@gtkx/ffi/adw";
2
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { DROP_DOWN_CLASSES } from "../generated/internal.js";
3
2
  import { registerNodeClass } from "../registry.js";
4
3
  import { signalStore } from "./internal/signal-store.js";
5
4
  import { SimpleListStore } from "./internal/simple-list-store.js";
6
- import { filterProps, isContainerType } from "./internal/utils.js";
5
+ import { filterProps, matchesAnyClass } from "./internal/utils.js";
7
6
  import { SimpleListItemNode } from "./simple-list-item.js";
8
7
  import { WidgetNode } from "./widget.js";
9
8
  const PROP_NAMES = ["selectedId", "onSelectionChanged"];
10
9
  class SimpleListViewNode extends WidgetNode {
11
10
  static priority = -1;
12
11
  static matches(_type, containerOrClass) {
13
- return isContainerType(Gtk.DropDown, containerOrClass) || isContainerType(Adw.ComboRow, containerOrClass);
12
+ return matchesAnyClass(DROP_DOWN_CLASSES, containerOrClass);
14
13
  }
15
14
  store = new SimpleListStore();
16
15
  constructor(typeName, props, container, rootContainer) {
@@ -24,7 +23,7 @@ class SimpleListViewNode extends WidgetNode {
24
23
  ? () => {
25
24
  const selectedIndex = this.container.getSelected();
26
25
  const id = this.store.getIdAtIndex(selectedIndex);
27
- if (id !== undefined) {
26
+ if (id !== null) {
28
27
  onSelectionChanged(id);
29
28
  }
30
29
  }
@@ -32,8 +31,8 @@ class SimpleListViewNode extends WidgetNode {
32
31
  signalStore.set(this, this.container, "notify::selected", handleSelectionChange);
33
32
  }
34
33
  if (!oldProps || oldProps.selectedId !== newProps.selectedId) {
35
- const index = newProps.selectedId !== undefined ? this.store.getIndexById(newProps.selectedId) : undefined;
36
- if (index !== undefined) {
34
+ const index = newProps.selectedId !== undefined ? this.store.getIndexById(newProps.selectedId) : null;
35
+ if (index !== null) {
37
36
  this.container.setSelected(index);
38
37
  }
39
38
  }
@@ -15,7 +15,7 @@ export declare class SlotNode<P extends SlotNodeProps = SlotNodeProps> extends V
15
15
  getChild(): Gtk.Widget;
16
16
  appendChild(child: Node): void;
17
17
  removeChild(): void;
18
- protected onChildChange(oldChild: Gtk.Widget | undefined): void;
18
+ protected onChildChange(oldChild: Gtk.Widget | null): void;
19
19
  private isDescendantOf;
20
20
  }
21
21
  export {};
@@ -1,7 +1,8 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import { toCamelCase } from "@gtkx/gir";
2
- import { PROPS } from "../generated/internal.js";
3
3
  import { registerNodeClass } from "../registry.js";
4
4
  import { scheduleAfterCommit } from "../scheduler.js";
5
+ import { resolvePropMeta } from "./internal/utils.js";
5
6
  import { VirtualNode } from "./virtual.js";
6
7
  import { WidgetNode } from "./widget.js";
7
8
  export class SlotNode extends VirtualNode {
@@ -18,7 +19,7 @@ export class SlotNode extends VirtualNode {
18
19
  if (this.parent && this.child) {
19
20
  const oldChild = this.child;
20
21
  this.child = undefined;
21
- this.onChildChange(oldChild);
22
+ this.onChildChange(oldChild ?? null);
22
23
  }
23
24
  this.parent = undefined;
24
25
  super.unmount();
@@ -50,7 +51,7 @@ export class SlotNode extends VirtualNode {
50
51
  this.child = child.container;
51
52
  scheduleAfterCommit(() => {
52
53
  if (this.parent) {
53
- this.onChildChange(oldChild);
54
+ this.onChildChange(oldChild ?? null);
54
55
  }
55
56
  });
56
57
  }
@@ -61,17 +62,18 @@ export class SlotNode extends VirtualNode {
61
62
  this.child = undefined;
62
63
  }
63
64
  if (this.parent) {
64
- this.onChildChange(oldChild);
65
+ this.onChildChange(oldChild ?? null);
65
66
  }
66
67
  });
67
68
  }
68
69
  onChildChange(oldChild) {
69
70
  const parent = this.getParent();
70
71
  const parentType = parent.constructor.glibTypeName;
71
- const [_, setterName] = PROPS[parentType]?.[this.getId()] ?? [];
72
- if (!setterName) {
72
+ const propMeta = resolvePropMeta(parent, this.getId());
73
+ if (!propMeta) {
73
74
  throw new Error(`Unable to find property for Slot '${this.getId()}' on type '${parentType}'`);
74
75
  }
76
+ const [_, setterName] = propMeta;
75
77
  const setter = parent[setterName];
76
78
  if (typeof setter !== "function") {
77
79
  throw new Error(`Expected setter function for Slot '${this.getId()}' on type '${parentType}'`);
@@ -88,7 +90,7 @@ export class SlotNode extends VirtualNode {
88
90
  isDescendantOf(widget, ancestor) {
89
91
  let current = widget;
90
92
  while (current) {
91
- if (current.equals(ancestor)) {
93
+ if (isObjectEqual(current, ancestor)) {
92
94
  return true;
93
95
  }
94
96
  current = current.getParent();
@@ -1,3 +1,4 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import * as Adw from "@gtkx/ffi/adw";
2
3
  import { registerNodeClass } from "../registry.js";
3
4
  import { SlotNode } from "./slot.js";
@@ -69,7 +70,7 @@ class StackPageNode extends SlotNode {
69
70
  return;
70
71
  }
71
72
  const currentParent = oldChild.getParent();
72
- if (currentParent?.equals(parent)) {
73
+ if (currentParent && isObjectEqual(currentParent, parent)) {
73
74
  parent.remove(oldChild);
74
75
  }
75
76
  }
@@ -1,14 +1,13 @@
1
- import * as Adw from "@gtkx/ffi/adw";
2
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { STACK_CLASSES } from "../generated/internal.js";
3
2
  import { registerNodeClass } from "../registry.js";
4
3
  import { scheduleAfterCommit } from "../scheduler.js";
5
- import { filterProps, isContainerType } from "./internal/utils.js";
4
+ import { filterProps, matchesAnyClass } from "./internal/utils.js";
6
5
  import { WidgetNode } from "./widget.js";
7
6
  const PROPS = ["visibleChildName"];
8
7
  class StackNode extends WidgetNode {
9
8
  static priority = 1;
10
9
  static matches(_type, containerOrClass) {
11
- return isContainerType(Gtk.Stack, containerOrClass) || isContainerType(Adw.ViewStack, containerOrClass);
10
+ return matchesAnyClass(STACK_CLASSES, containerOrClass);
12
11
  }
13
12
  updateProps(oldProps, newProps) {
14
13
  if (newProps.visibleChildName && this.container.getVisibleChildName() !== newProps.visibleChildName) {
@@ -1,3 +1,4 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { SlotNode } from "./slot.js";
3
4
  class ToolbarChildNode extends SlotNode {
@@ -18,7 +19,7 @@ class ToolbarChildNode extends SlotNode {
18
19
  const toolbar = this.getToolbar();
19
20
  if (oldChild) {
20
21
  const parent = oldChild.getParent();
21
- if (parent?.equals(toolbar)) {
22
+ if (parent && isObjectEqual(parent, toolbar)) {
22
23
  toolbar.remove(oldChild);
23
24
  }
24
25
  }
@@ -9,9 +9,9 @@ export declare class TreeListItemNode extends VirtualNode<Props> {
9
9
  private parentItemId?;
10
10
  private childNodes;
11
11
  static matches(type: string): boolean;
12
- setStore(store?: TreeStore): void;
13
- setParentItemId(parentId?: string): void;
14
- getParentItemId(): string | undefined;
12
+ setStore(store?: TreeStore | null): void;
13
+ setParentItemId(parentId?: string | null): void;
14
+ getParentItemId(): string | null;
15
15
  appendChild(child: Node): void;
16
16
  insertBefore(child: Node, before: Node): void;
17
17
  removeChild(child: Node): void;
@@ -19,7 +19,7 @@ export class TreeListItemNode extends VirtualNode {
19
19
  this.parentItemId = parentId;
20
20
  }
21
21
  getParentItemId() {
22
- return this.parentItemId;
22
+ return this.parentItemId ?? null;
23
23
  }
24
24
  appendChild(child) {
25
25
  if (!(child instanceof TreeListItemNode)) {
@@ -74,8 +74,8 @@ export class TreeListItemNode extends VirtualNode {
74
74
  if (this.store && child.props.id !== undefined) {
75
75
  this.store.removeItem(child.props.id, this.props.id);
76
76
  }
77
- child.setStore(undefined);
78
- child.setParentItemId(undefined);
77
+ child.setStore(null);
78
+ child.setParentItemId(null);
79
79
  }
80
80
  updateProps(oldProps, newProps) {
81
81
  super.updateProps(oldProps, newProps);
@@ -7,14 +7,22 @@ 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
- static matches(_type: string, containerOrClass?: Container | ContainerClass): boolean;
11
- static createContainer(props: Props, containerClass: typeof Gtk.Widget): Container | undefined;
10
+ static matches(_type: string, containerOrClass?: Container | ContainerClass | null): boolean;
11
+ static createContainer(props: Props, containerClass: typeof Gtk.Widget): Container | null;
12
12
  appendChild(child: Node): void;
13
13
  removeChild(child: Node): void;
14
14
  insertBefore(child: Node, before: Node): void;
15
+ private insertBeforeReorderable;
16
+ private insertBeforeInsertable;
15
17
  updateProps(oldProps: P | null, newProps: P): void;
16
18
  private updateEventControllerProp;
19
+ private updateNotifyHandler;
17
20
  private propNameToSignalName;
18
21
  private getProperty;
19
22
  private setProperty;
23
+ private detachChildFromParent;
24
+ private attachChild;
25
+ private detachChild;
26
+ private findPreviousSibling;
27
+ private findInsertPosition;
20
28
  }
@@ -1,23 +1,13 @@
1
- import { batch, NativeObject } from "@gtkx/ffi";
1
+ import { batch, isObjectEqual, NativeObject } from "@gtkx/ffi";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
- import { CONSTRUCTOR_PROPS, PROPS, SIGNALS } from "../generated/internal.js";
3
+ import { CONSTRUCTOR_PROPS } from "../generated/internal.js";
4
4
  import { Node } from "../node.js";
5
5
  import { registerNodeClass } from "../registry.js";
6
+ import { EVENT_CONTROLLER_PROPS } from "./internal/constants.js";
6
7
  import { hasSingleContent, isAddable, isAppendable, isEditable, isInsertable, isRemovable, isReorderable, isSingleChild, } from "./internal/predicates.js";
7
8
  import { signalStore } from "./internal/signal-store.js";
8
- import { filterProps, isContainerType } from "./internal/utils.js";
9
+ import { filterProps, isContainerType, resolvePropMeta, resolveSignal } from "./internal/utils.js";
9
10
  import { SlotNode } from "./slot.js";
10
- const EVENT_CONTROLLER_PROPS = new Set([
11
- "onEnter",
12
- "onLeave",
13
- "onMotion",
14
- "onPressed",
15
- "onReleased",
16
- "onKeyPressed",
17
- "onKeyReleased",
18
- "onScroll",
19
- "onNotify",
20
- ]);
21
11
  export class WidgetNode extends Node {
22
12
  static priority = 3;
23
13
  motionController;
@@ -44,31 +34,7 @@ export class WidgetNode extends Node {
44
34
  if (child.container instanceof Gtk.Window) {
45
35
  throw new Error(`Cannot append 'Window' to '${this.typeName}': windows must be top-level containers`);
46
36
  }
47
- batch(() => {
48
- if (isAppendable(this.container)) {
49
- const currentParent = child.container.getParent();
50
- if (currentParent !== null && isRemovable(currentParent)) {
51
- currentParent.remove(child.container);
52
- }
53
- this.container.append(child.container);
54
- }
55
- else if (isAddable(this.container)) {
56
- const currentParent = child.container.getParent();
57
- if (currentParent !== null && isRemovable(currentParent)) {
58
- currentParent.remove(child.container);
59
- }
60
- this.container.add(child.container);
61
- }
62
- else if (hasSingleContent(this.container)) {
63
- this.container.setContent(child.container);
64
- }
65
- else if (isSingleChild(this.container)) {
66
- this.container.setChild(child.container);
67
- }
68
- else {
69
- throw new Error(`Cannot append '${child.typeName}' to '${this.container.constructor.name}': container does not support children`);
70
- }
71
- });
37
+ batch(() => this.attachChild(child));
72
38
  }
73
39
  removeChild(child) {
74
40
  if (child instanceof SlotNode) {
@@ -80,20 +46,7 @@ export class WidgetNode extends Node {
80
46
  if (child.container instanceof Gtk.Window) {
81
47
  throw new Error(`Cannot remove 'Window' from '${this.typeName}': windows must be top-level containers`);
82
48
  }
83
- batch(() => {
84
- if (isRemovable(this.container)) {
85
- this.container.remove(child.container);
86
- }
87
- else if (hasSingleContent(this.container)) {
88
- this.container.setContent(undefined);
89
- }
90
- else if (isSingleChild(this.container)) {
91
- this.container.setChild(null);
92
- }
93
- else {
94
- throw new Error(`Cannot remove '${child.typeName}' from '${this.container.constructor.name}': container does not support child removal`);
95
- }
96
- });
49
+ batch(() => this.detachChild(child));
97
50
  }
98
51
  insertBefore(child, before) {
99
52
  if (child instanceof SlotNode) {
@@ -108,71 +61,53 @@ export class WidgetNode extends Node {
108
61
  }
109
62
  batch(() => {
110
63
  if (isReorderable(this.container)) {
111
- let beforeChild = this.container.getFirstChild();
112
- while (beforeChild) {
113
- if (beforeChild.equals(before.container)) {
114
- break;
115
- }
116
- beforeChild = beforeChild.getNextSibling();
117
- }
118
- if (!beforeChild) {
119
- throw new Error(`Cannot insert '${child.typeName}': 'before' child not found in container`);
120
- }
121
- const previousSibling = beforeChild.getPrevSibling() ?? undefined;
122
- const currentParent = child.container.getParent();
123
- const isChildOfThisContainer = currentParent?.equals(this.container);
124
- if (isChildOfThisContainer) {
125
- this.container.reorderChildAfter(child.container, previousSibling);
126
- }
127
- else {
128
- if (currentParent !== null && isRemovable(currentParent)) {
129
- currentParent.remove(child.container);
130
- }
131
- this.container.insertChildAfter(child.container, previousSibling);
132
- }
64
+ this.insertBeforeReorderable(this.container, child, before);
133
65
  }
134
66
  else if (isInsertable(this.container)) {
135
- const currentParent = child.container.getParent();
136
- if (currentParent !== null && isRemovable(currentParent)) {
137
- currentParent.remove(child.container);
138
- }
139
- let position = 0;
140
- let currentChild = this.container.getFirstChild();
141
- while (currentChild) {
142
- if (currentChild.equals(before.container)) {
143
- break;
144
- }
145
- position++;
146
- currentChild = currentChild.getNextSibling();
147
- }
148
- if (!currentChild) {
149
- throw new Error(`Cannot insert '${child.typeName}': 'before' child not found in container`);
150
- }
151
- this.container.insert(child.container, position);
67
+ this.insertBeforeInsertable(this.container, child, before);
152
68
  }
153
69
  else {
154
70
  this.appendChild(child);
155
71
  }
156
72
  });
157
73
  }
74
+ insertBeforeReorderable(container, child, before) {
75
+ const previousSibling = this.findPreviousSibling(before);
76
+ const currentParent = child.container.getParent();
77
+ const isChildOfThisContainer = currentParent && isObjectEqual(currentParent, container);
78
+ if (isChildOfThisContainer) {
79
+ container.reorderChildAfter(child.container, previousSibling);
80
+ }
81
+ else {
82
+ this.detachChildFromParent(child);
83
+ container.insertChildAfter(child.container, previousSibling);
84
+ }
85
+ }
86
+ insertBeforeInsertable(container, child, before) {
87
+ this.detachChildFromParent(child);
88
+ const position = this.findInsertPosition(before);
89
+ container.insert(child.container, position);
90
+ }
158
91
  updateProps(oldProps, newProps) {
159
92
  const propNames = new Set([
160
93
  ...Object.keys(filterProps(oldProps ?? {}, ["children"])),
161
94
  ...Object.keys(filterProps(newProps ?? {}, ["children"])),
162
95
  ]);
163
- const WidgetClass = this.container.constructor;
164
- const signals = SIGNALS[WidgetClass.glibTypeName] || new Set();
165
96
  for (const name of propNames) {
166
97
  const oldValue = oldProps?.[name];
167
98
  const newValue = newProps[name];
168
99
  if (oldValue === newValue)
169
100
  continue;
170
101
  if (EVENT_CONTROLLER_PROPS.has(name)) {
171
- this.updateEventControllerProp(name, newValue);
102
+ this.updateEventControllerProp(name, newValue ?? null);
103
+ continue;
104
+ }
105
+ if (name === "onNotify") {
106
+ this.updateNotifyHandler(newValue ?? null);
172
107
  continue;
173
108
  }
174
109
  const signalName = this.propNameToSignalName(name);
175
- if (signals.has(signalName)) {
110
+ if (resolveSignal(this.container, signalName)) {
176
111
  const handler = typeof newValue === "function" ? newValue : undefined;
177
112
  signalStore.set(this, this.container, signalName, handler);
178
113
  }
@@ -229,17 +164,16 @@ export class WidgetNode extends Node {
229
164
  signalStore.set(this, this.scrollController, "scroll", handler);
230
165
  break;
231
166
  }
232
- case "onNotify": {
233
- const wrappedHandler = handler
234
- ? (obj, pspec) => {
235
- handler(obj, pspec.getName());
236
- }
237
- : undefined;
238
- signalStore.set(this, this.container, "notify", wrappedHandler);
239
- break;
240
- }
241
167
  }
242
168
  }
169
+ updateNotifyHandler(handler) {
170
+ const wrappedHandler = handler
171
+ ? (obj, pspec) => {
172
+ handler(obj, pspec.getName());
173
+ }
174
+ : undefined;
175
+ signalStore.set(this, this.container, "notify", wrappedHandler);
176
+ }
243
177
  propNameToSignalName(name) {
244
178
  return name
245
179
  .slice(2)
@@ -248,8 +182,10 @@ export class WidgetNode extends Node {
248
182
  .replace(/^-/, "");
249
183
  }
250
184
  getProperty(key) {
251
- const WidgetClass = this.container.constructor;
252
- const [getterName] = PROPS[WidgetClass.glibTypeName]?.[key] || [];
185
+ const propMeta = resolvePropMeta(this.container, key);
186
+ if (!propMeta)
187
+ return undefined;
188
+ const [getterName] = propMeta;
253
189
  const getter = getterName ? this.container[getterName] : undefined;
254
190
  if (getter && typeof getter === "function") {
255
191
  return getter.call(this.container);
@@ -257,16 +193,20 @@ export class WidgetNode extends Node {
257
193
  return undefined;
258
194
  }
259
195
  setProperty(key, value) {
260
- const WidgetClass = this.container.constructor;
261
- const [getterName, setterName] = PROPS[WidgetClass.glibTypeName]?.[key] || [];
196
+ const propMeta = resolvePropMeta(this.container, key);
197
+ if (!propMeta)
198
+ return;
199
+ const [getterName, setterName] = propMeta;
262
200
  const setter = this.container[setterName];
263
- const getter = this.container[getterName];
201
+ const getter = getterName ? this.container[getterName] : undefined;
264
202
  if (getter && typeof getter === "function") {
265
203
  const currentValue = getter.call(this.container);
266
204
  if (currentValue === value) {
267
205
  return;
268
206
  }
269
- if (currentValue instanceof NativeObject && currentValue.equals(value)) {
207
+ if (currentValue instanceof NativeObject &&
208
+ value instanceof NativeObject &&
209
+ isObjectEqual(currentValue, value)) {
270
210
  return;
271
211
  }
272
212
  }
@@ -274,5 +214,66 @@ export class WidgetNode extends Node {
274
214
  setter.call(this.container, value);
275
215
  }
276
216
  }
217
+ detachChildFromParent(child) {
218
+ const currentParent = child.container.getParent();
219
+ if (currentParent !== null && isRemovable(currentParent)) {
220
+ currentParent.remove(child.container);
221
+ }
222
+ }
223
+ attachChild(child) {
224
+ if (isAppendable(this.container)) {
225
+ this.detachChildFromParent(child);
226
+ this.container.append(child.container);
227
+ }
228
+ else if (isAddable(this.container)) {
229
+ this.detachChildFromParent(child);
230
+ this.container.add(child.container);
231
+ }
232
+ else if (hasSingleContent(this.container)) {
233
+ this.container.setContent(child.container);
234
+ }
235
+ else if (isSingleChild(this.container)) {
236
+ this.container.setChild(child.container);
237
+ }
238
+ else {
239
+ throw new Error(`Cannot append '${child.typeName}' to '${this.container.constructor.name}': container does not support children`);
240
+ }
241
+ }
242
+ detachChild(child) {
243
+ if (isRemovable(this.container)) {
244
+ this.container.remove(child.container);
245
+ }
246
+ else if (hasSingleContent(this.container)) {
247
+ this.container.setContent(null);
248
+ }
249
+ else if (isSingleChild(this.container)) {
250
+ this.container.setChild(null);
251
+ }
252
+ else {
253
+ throw new Error(`Cannot remove '${child.typeName}' from '${this.container.constructor.name}': container does not support child removal`);
254
+ }
255
+ }
256
+ findPreviousSibling(before) {
257
+ let beforeChild = this.container.getFirstChild();
258
+ while (beforeChild) {
259
+ if (isObjectEqual(beforeChild, before.container)) {
260
+ return beforeChild.getPrevSibling() ?? undefined;
261
+ }
262
+ beforeChild = beforeChild.getNextSibling();
263
+ }
264
+ throw new Error(`Cannot find 'before' child in container`);
265
+ }
266
+ findInsertPosition(before) {
267
+ let position = 0;
268
+ let currentChild = this.container.getFirstChild();
269
+ while (currentChild) {
270
+ if (isObjectEqual(currentChild, before.container)) {
271
+ return position;
272
+ }
273
+ position++;
274
+ currentChild = currentChild.getNextSibling();
275
+ }
276
+ throw new Error(`Cannot find 'before' child in container`);
277
+ }
277
278
  }
278
279
  registerNodeClass(WidgetNode);
@@ -41,7 +41,7 @@ class WindowNode extends WidgetNode {
41
41
  }
42
42
  removeChild(child) {
43
43
  if (child.container instanceof Gtk.Window) {
44
- child.container.setTransientFor(undefined);
44
+ child.container.setTransientFor(null);
45
45
  return;
46
46
  }
47
47
  this.menu.removeChild(child);