@gtkx/react 0.16.0 → 0.17.2

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 (75) hide show
  1. package/dist/animation/css-builder.d.ts +3 -0
  2. package/dist/animation/css-builder.js +53 -0
  3. package/dist/animation/types.d.ts +120 -0
  4. package/dist/animation/types.js +1 -0
  5. package/dist/fiber-root.js +1 -1
  6. package/dist/generated/jsx.d.ts +354 -351
  7. package/dist/generated/jsx.js +2 -0
  8. package/dist/host-config.js +6 -11
  9. package/dist/jsx.d.ts +39 -10
  10. package/dist/jsx.js +7 -7
  11. package/dist/node.d.ts +1 -0
  12. package/dist/node.js +9 -0
  13. package/dist/nodes/abstract/virtual-container.d.ts +5 -1
  14. package/dist/nodes/abstract/virtual-container.js +9 -0
  15. package/dist/nodes/abstract/virtual-single-child.js +2 -1
  16. package/dist/nodes/action-row-child.js +8 -0
  17. package/dist/nodes/animation.d.ts +1 -0
  18. package/dist/nodes/animation.js +252 -0
  19. package/dist/nodes/event-controller.d.ts +22 -1
  20. package/dist/nodes/event-controller.js +2 -2
  21. package/dist/nodes/expander-row-child.js +8 -0
  22. package/dist/nodes/fixed-child.js +4 -0
  23. package/dist/nodes/grid-child.js +4 -0
  24. package/dist/nodes/index.d.ts +2 -4
  25. package/dist/nodes/index.js +2 -4
  26. package/dist/nodes/internal/deferred-action.d.ts +1 -0
  27. package/dist/nodes/internal/deferred-action.js +3 -0
  28. package/dist/nodes/internal/list-store.d.ts +4 -6
  29. package/dist/nodes/internal/list-store.js +26 -31
  30. package/dist/nodes/internal/selection-model.js +0 -4
  31. package/dist/nodes/internal/signal-store.d.ts +4 -1
  32. package/dist/nodes/internal/signal-store.js +6 -8
  33. package/dist/nodes/internal/text-buffer-controller.js +1 -1
  34. package/dist/nodes/internal/tree-store.d.ts +2 -6
  35. package/dist/nodes/internal/tree-store.js +58 -56
  36. package/dist/nodes/list-view.js +2 -1
  37. package/dist/nodes/models/tree-list.js +1 -1
  38. package/dist/nodes/overlay-child.js +7 -7
  39. package/dist/nodes/pack-child.js +8 -0
  40. package/dist/nodes/shortcut-controller.js +6 -61
  41. package/dist/nodes/slot.js +1 -1
  42. package/dist/nodes/text-paintable.d.ts +6 -0
  43. package/dist/nodes/toggle-group.js +1 -1
  44. package/dist/nodes/toolbar-child.js +22 -29
  45. package/dist/nodes/tree-list-view.js +2 -1
  46. package/dist/nodes/web-view.d.ts +1 -0
  47. package/dist/nodes/web-view.js +29 -0
  48. package/dist/render.js +1 -1
  49. package/package.json +4 -4
  50. package/dist/nodes/abstract/positional-parent.d.ts +0 -18
  51. package/dist/nodes/abstract/positional-parent.js +0 -48
  52. package/dist/nodes/action-row.d.ts +0 -6
  53. package/dist/nodes/action-row.js +0 -13
  54. package/dist/nodes/animation/animation-controller.d.ts +0 -17
  55. package/dist/nodes/animation/animation-controller.js +0 -107
  56. package/dist/nodes/animation/animation-factory.d.ts +0 -15
  57. package/dist/nodes/animation/animation-factory.js +0 -25
  58. package/dist/nodes/animation/animation-node.d.ts +0 -9
  59. package/dist/nodes/animation/animation-node.js +0 -126
  60. package/dist/nodes/animation/animation-style-sheet.d.ts +0 -16
  61. package/dist/nodes/animation/animation-style-sheet.js +0 -74
  62. package/dist/nodes/animation/index.d.ts +0 -4
  63. package/dist/nodes/animation/index.js +0 -1
  64. package/dist/nodes/animation/property-mapper.d.ts +0 -11
  65. package/dist/nodes/animation/property-mapper.js +0 -36
  66. package/dist/nodes/animation/transform-state.d.ts +0 -11
  67. package/dist/nodes/animation/transform-state.js +0 -57
  68. package/dist/nodes/animation/widget-registry.d.ts +0 -5
  69. package/dist/nodes/animation/widget-registry.js +0 -42
  70. package/dist/nodes/expander-row.d.ts +0 -6
  71. package/dist/nodes/expander-row.js +0 -18
  72. package/dist/nodes/internal/base-store.d.ts +0 -9
  73. package/dist/nodes/internal/base-store.js +0 -20
  74. package/dist/nodes/pack.d.ts +0 -6
  75. package/dist/nodes/pack.js +0 -13
@@ -7959,6 +7959,7 @@ export const AdwTabOverview = "AdwTabOverview";
7959
7959
  * are the accessible parent objects of the child widgets.
7960
7960
  */
7961
7961
  export const AdwTabView = "AdwTabView";
7962
+ /** A Vte.Terminal widget element. */
7962
7963
  export const VteTerminal = "VteTerminal";
7963
7964
  /**
7964
7965
  * A single-line text entry.
@@ -8975,6 +8976,7 @@ export const GtkVolumeButton = "GtkVolumeButton";
8975
8976
  * you don't need to embed it in a #GtkScrolledWindow.
8976
8977
  */
8977
8978
  export const WebKitWebView = "WebKitWebView";
8979
+ /** A WebKit.WebViewBase widget element. */
8978
8980
  export const WebKitWebViewBase = "WebKitWebViewBase";
8979
8981
  /**
8980
8982
  * Shows window frame controls.
@@ -2,7 +2,6 @@ import { getNativeId } from "@gtkx/ffi";
2
2
  import React from "react";
3
3
  import { createNode } from "./factory.js";
4
4
  import { isBufferedType } from "./nodes/internal/predicates.js";
5
- import { getSignalStore } from "./nodes/internal/signal-store.js";
6
5
  import { flushAfterCommit } from "./scheduler.js";
7
6
  if (!globalThis.__GTKX_CONTAINER_NODE_CACHE__) {
8
7
  globalThis.__GTKX_CONTAINER_NODE_CACHE__ = new Map();
@@ -55,13 +54,11 @@ export function createHostConfig() {
55
54
  parent.appendChild(child);
56
55
  },
57
56
  finalizeInitialChildren: (instance, _type, props) => {
58
- instance.signalStore.blockAll();
59
- instance.updateProps(null, props);
60
- instance.signalStore.unblockAll();
57
+ instance.commitProps(null, props);
61
58
  return true;
62
59
  },
63
60
  commitUpdate: (instance, _type, oldProps, newProps) => {
64
- instance.updateProps(oldProps, newProps);
61
+ instance.commitProps(oldProps, newProps);
65
62
  },
66
63
  commitMount: (instance, _type) => {
67
64
  instance.mount();
@@ -87,20 +84,18 @@ export function createHostConfig() {
87
84
  const parent = getOrCreateContainerNode(container);
88
85
  parent.insertBefore(child, beforeChild);
89
86
  },
90
- prepareForCommit: (containerInfo) => {
91
- getSignalStore(containerInfo).blockAll();
87
+ prepareForCommit: () => {
92
88
  return null;
93
89
  },
94
- resetAfterCommit: (containerInfo) => {
90
+ resetAfterCommit: () => {
95
91
  flushAfterCommit();
96
- getSignalStore(containerInfo).unblockAll();
97
92
  },
98
93
  commitTextUpdate: (textInstance, oldText, newText) => {
99
94
  if (textInstance.typeName === "TextSegment") {
100
- textInstance.updateProps({ text: oldText }, { text: newText });
95
+ textInstance.commitProps({ text: oldText }, { text: newText });
101
96
  }
102
97
  else {
103
- textInstance.updateProps({ label: oldText }, { label: newText });
98
+ textInstance.commitProps({ label: oldText }, { label: newText });
104
99
  }
105
100
  },
106
101
  clearContainer: () => { },
package/dist/jsx.d.ts CHANGED
@@ -2,25 +2,47 @@ import type * as Adw from "@gtkx/ffi/adw";
2
2
  import type * as Gsk from "@gtkx/ffi/gsk";
3
3
  import type * as Gtk from "@gtkx/ffi/gtk";
4
4
  import type { ReactElement, ReactNode } from "react";
5
+ import type { AnimationProps } from "./animation/types.js";
5
6
  import type { GtkGridViewProps as GeneratedGtkGridViewProps, GtkListViewProps as GeneratedGtkListViewProps, WidgetSlotNames } from "./generated/jsx.js";
6
- import type { AnimationProps as AnimationNodeProps } from "./nodes/animation/index.js";
7
7
  import type { RenderItemFn } from "./nodes/internal/list-item-renderer.js";
8
8
  import type { TreeRenderItemFn } from "./nodes/internal/tree-list-item-renderer.js";
9
9
  import type { ShortcutProps as ShortcutNodeProps } from "./nodes/shortcut.js";
10
10
  import type { TextAnchorProps } from "./nodes/text-anchor.js";
11
11
  import type { TextPaintableProps } from "./nodes/text-paintable.js";
12
12
  import type { TextTagProps } from "./nodes/text-tag.js";
13
- export type { AnimatableProperties, SpringTransition, TimedTransition, Transition } from "./nodes/animation/index.js";
13
+ export type { AnimatableProperties, AnimationMode, AnimationProps, SpringTransition, TimedTransition, } from "./animation/types.js";
14
14
  export type { TextAnchorProps } from "./nodes/text-anchor.js";
15
15
  export type { TextPaintableProps } from "./nodes/text-paintable.js";
16
16
  export type { TextTagProps } from "./nodes/text-tag.js";
17
+ /**
18
+ * Configuration for a mark on a GtkScale widget.
19
+ *
20
+ * Marks are visual indicators placed along the scale at specific values,
21
+ * optionally with text labels.
22
+ *
23
+ * @see {@link https://docs.gtk.org/gtk4/method.Scale.add_mark.html GtkScale.add_mark}
24
+ */
17
25
  export type ScaleMark = {
26
+ /** The value at which to place the mark */
18
27
  value: number;
28
+ /** Position of the mark relative to the scale (default: BOTTOM) */
19
29
  position?: Gtk.PositionType;
30
+ /** Optional text label to display at the mark */
20
31
  label?: string | null;
21
32
  };
33
+ /**
34
+ * Configuration for an offset threshold on a GtkLevelBar widget.
35
+ *
36
+ * Offsets define named value thresholds that change the bar's appearance
37
+ * (e.g., "low", "high", "full"). Built-in offsets include GTK_LEVEL_BAR_OFFSET_LOW,
38
+ * GTK_LEVEL_BAR_OFFSET_HIGH, and GTK_LEVEL_BAR_OFFSET_FULL.
39
+ *
40
+ * @see {@link https://docs.gtk.org/gtk4/method.LevelBar.add_offset_value.html GtkLevelBar.add_offset_value}
41
+ */
22
42
  export type LevelBarOffset = {
43
+ /** Unique identifier for this offset (e.g., "low", "high", "full") */
23
44
  id: string;
45
+ /** The threshold value at which this offset applies */
24
46
  value: number;
25
47
  };
26
48
  /**
@@ -272,6 +294,13 @@ export type ToggleProps = {
272
294
  /** Whether underline in label indicates mnemonic */
273
295
  useUnderline?: boolean;
274
296
  };
297
+ /**
298
+ * Props for response buttons in an AdwAlertDialog.
299
+ *
300
+ * Each response represents a button the user can click to dismiss the dialog.
301
+ *
302
+ * @see {@link x.AlertDialogResponse} for usage
303
+ */
275
304
  export type AlertDialogResponseProps = {
276
305
  /** Unique response ID (used in response signal and for default/close response) */
277
306
  id: string;
@@ -848,19 +877,19 @@ export declare const x: {
848
877
  */
849
878
  Shortcut: "Shortcut";
850
879
  /**
851
- * Declarative animation wrapper using libadwaita's animation primitives.
880
+ * Declarative animation wrapper using Adw.TimedAnimation or Adw.SpringAnimation.
852
881
  *
853
- * Provides Framer Motion-like API for animating GTK widgets with spring
854
- * or timed animations.
882
+ * Provides framer-motion-inspired API for animating child widgets.
855
883
  *
856
884
  * @example
857
885
  * ```tsx
858
886
  * <x.Animation
859
- * initial={{ opacity: 0, y: -20 }}
860
- * animate={{ opacity: 1, y: 0 }}
861
- * transition={{ type: "spring", stiffness: 300, damping: 20 }}
887
+ * mode="spring"
888
+ * initial={{ opacity: 0, scale: 0.9 }}
889
+ * animate={{ opacity: 1, scale: 1 }}
890
+ * animateOnMount
862
891
  * >
863
- * <GtkLabel label="Hello World" />
892
+ * <GtkButton label="Animated Button" />
864
893
  * </x.Animation>
865
894
  * ```
866
895
  */
@@ -873,6 +902,7 @@ declare global {
873
902
  ActionRowPrefix: VirtualSlotProps;
874
903
  ActionRowSuffix: VirtualSlotProps;
875
904
  AlertDialogResponse: AlertDialogResponseProps;
905
+ Animation: AnimationProps;
876
906
  ColumnViewColumn: ColumnViewColumnProps<any>;
877
907
  ExpanderRowAction: ExpanderRowChildProps;
878
908
  ExpanderRowRow: ExpanderRowChildProps;
@@ -898,7 +928,6 @@ declare global {
898
928
  TreeListItem: TreeListItemProps<any>;
899
929
  NavigationPage: NavigationPageProps;
900
930
  Shortcut: ShortcutProps;
901
- Animation: AnimationNodeProps;
902
931
  }
903
932
  }
904
933
  }
package/dist/jsx.js CHANGED
@@ -476,19 +476,19 @@ export const x = {
476
476
  */
477
477
  Shortcut: "Shortcut",
478
478
  /**
479
- * Declarative animation wrapper using libadwaita's animation primitives.
479
+ * Declarative animation wrapper using Adw.TimedAnimation or Adw.SpringAnimation.
480
480
  *
481
- * Provides Framer Motion-like API for animating GTK widgets with spring
482
- * or timed animations.
481
+ * Provides framer-motion-inspired API for animating child widgets.
483
482
  *
484
483
  * @example
485
484
  * ```tsx
486
485
  * <x.Animation
487
- * initial={{ opacity: 0, y: -20 }}
488
- * animate={{ opacity: 1, y: 0 }}
489
- * transition={{ type: "spring", stiffness: 300, damping: 20 }}
486
+ * mode="spring"
487
+ * initial={{ opacity: 0, scale: 0.9 }}
488
+ * animate={{ opacity: 1, scale: 1 }}
489
+ * animateOnMount
490
490
  * >
491
- * <GtkLabel label="Hello World" />
491
+ * <GtkButton label="Animated Button" />
492
492
  * </x.Animation>
493
493
  * ```
494
494
  */
package/dist/node.d.ts CHANGED
@@ -13,5 +13,6 @@ export declare class Node<T = unknown, P = Props> {
13
13
  insertBefore(_child: Node, _before: Node): void;
14
14
  updateProps(_oldProps: P | null, _newProps: P): void;
15
15
  mount(): void;
16
+ commitProps(oldProps: P | null, newProps: P): void;
16
17
  unmount(): void;
17
18
  }
package/dist/node.js CHANGED
@@ -20,6 +20,15 @@ export class Node {
20
20
  insertBefore(_child, _before) { }
21
21
  updateProps(_oldProps, _newProps) { }
22
22
  mount() { }
23
+ commitProps(oldProps, newProps) {
24
+ this.signalStore.blockAll();
25
+ try {
26
+ this.updateProps(oldProps, newProps);
27
+ }
28
+ finally {
29
+ this.signalStore.unblockAll();
30
+ }
31
+ }
23
32
  unmount() {
24
33
  this.signalStore.clear(this);
25
34
  }
@@ -1,14 +1,18 @@
1
1
  import type * as Gtk from "@gtkx/ffi/gtk";
2
2
  import type { Node } from "../../node.js";
3
+ import type { Attachable } from "../internal/predicates.js";
3
4
  import { VirtualNode } from "../virtual.js";
4
5
  type ChildParentWidget = Gtk.Widget & {
5
6
  remove(child: Gtk.Widget): void;
6
7
  };
7
- export declare abstract class VirtualContainerNode<P extends ChildParentWidget = ChildParentWidget> extends VirtualNode {
8
+ export declare abstract class VirtualContainerNode<P extends ChildParentWidget = ChildParentWidget> extends VirtualNode implements Attachable {
8
9
  protected parent: P | null;
9
10
  setParent(newParent: P | null): void;
10
11
  protected getParent(): P;
11
12
  protected abstract attachChild(parent: P, widget: Gtk.Widget): void;
13
+ canBeChildOf(parent: Node): boolean;
14
+ attachTo(parent: Node): void;
15
+ detachFrom(_parent: Node): void;
12
16
  unmount(): void;
13
17
  appendChild(child: Node): void;
14
18
  insertBefore(child: Node, _before: Node): void;
@@ -13,6 +13,15 @@ export class VirtualContainerNode extends VirtualNode {
13
13
  }
14
14
  return this.parent;
15
15
  }
16
+ canBeChildOf(parent) {
17
+ return parent instanceof WidgetNode;
18
+ }
19
+ attachTo(parent) {
20
+ if (parent instanceof WidgetNode) {
21
+ this.setParent(parent.container);
22
+ }
23
+ }
24
+ detachFrom(_parent) { }
16
25
  unmount() {
17
26
  this.parent = null;
18
27
  super.unmount();
@@ -1,3 +1,4 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
1
2
  import { CommitPriority, scheduleAfterCommit } from "../../scheduler.js";
2
3
  import { VirtualNode } from "../virtual.js";
3
4
  import { WidgetNode } from "../widget.js";
@@ -34,7 +35,7 @@ export class VirtualSingleChildNode extends VirtualNode {
34
35
  }
35
36
  const oldChild = this.child;
36
37
  scheduleAfterCommit(() => {
37
- if (oldChild === this.child) {
38
+ if (isObjectEqual(oldChild, this.child)) {
38
39
  this.child = null;
39
40
  }
40
41
  if (this.parent && oldChild) {
@@ -1,10 +1,15 @@
1
+ import { PREFIX_SUFFIX_INTERFACE_METHODS } from "../generated/internal.js";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { VirtualContainerNode } from "./abstract/virtual-container.js";
4
+ import { matchesInterface } from "./internal/utils.js";
3
5
  class ActionRowPrefixNode extends VirtualContainerNode {
4
6
  static priority = 1;
5
7
  static matches(type) {
6
8
  return type === "ActionRowPrefix";
7
9
  }
10
+ canBeChildOf(parent) {
11
+ return matchesInterface(PREFIX_SUFFIX_INTERFACE_METHODS, parent.container);
12
+ }
8
13
  attachChild(parent, widget) {
9
14
  parent.addPrefix(widget);
10
15
  }
@@ -14,6 +19,9 @@ class ActionRowSuffixNode extends VirtualContainerNode {
14
19
  static matches(type) {
15
20
  return type === "ActionRowSuffix";
16
21
  }
22
+ canBeChildOf(parent) {
23
+ return matchesInterface(PREFIX_SUFFIX_INTERFACE_METHODS, parent.container);
24
+ }
17
25
  attachChild(parent, widget) {
18
26
  parent.addSuffix(widget);
19
27
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,252 @@
1
+ import { isObjectEqual } from "@gtkx/ffi";
2
+ import * as Adw from "@gtkx/ffi/adw";
3
+ import * as Gdk from "@gtkx/ffi/gdk";
4
+ import * as Gtk from "@gtkx/ffi/gtk";
5
+ import { buildCss, interpolate } from "../animation/css-builder.js";
6
+ import { registerNodeClass } from "../registry.js";
7
+ import { CommitPriority, scheduleAfterCommit } from "../scheduler.js";
8
+ import { VirtualSingleChildNode } from "./abstract/virtual-single-child.js";
9
+ import { attachChild, detachChild, getAttachmentStrategy } from "./internal/child-attachment.js";
10
+ import { isRemovable } from "./internal/predicates.js";
11
+ let animationCounter = 0;
12
+ const DEFAULT_TIMED_DURATION = 300;
13
+ const DEFAULT_SPRING_DAMPING = 1;
14
+ const DEFAULT_SPRING_MASS = 1;
15
+ const DEFAULT_SPRING_STIFFNESS = 100;
16
+ class AnimationNode extends VirtualSingleChildNode {
17
+ static priority = 2;
18
+ static matches(type) {
19
+ return type === "Animation";
20
+ }
21
+ className;
22
+ provider = null;
23
+ display = null;
24
+ currentAnimation = null;
25
+ currentValues = {};
26
+ isExiting = false;
27
+ constructor(typeName, props, container, rootContainer) {
28
+ super(typeName, props, container, rootContainer);
29
+ this.className = `gtkx-anim-${animationCounter++}`;
30
+ }
31
+ updateProps(oldProps, newProps) {
32
+ super.updateProps(oldProps, newProps);
33
+ if (this.isExiting) {
34
+ return;
35
+ }
36
+ if (oldProps && newProps.animate && !this.arePropsEqual(oldProps.animate, newProps.animate)) {
37
+ const target = newProps.animate;
38
+ scheduleAfterCommit(() => {
39
+ if (this.child && !this.isExiting) {
40
+ this.animateTo(target);
41
+ }
42
+ }, CommitPriority.LOW);
43
+ }
44
+ }
45
+ onChildChange(oldChild) {
46
+ if (oldChild && this.provider) {
47
+ oldChild.removeCssClass(this.className);
48
+ }
49
+ if (oldChild && this.parent && this.isWidgetAttachedTo(oldChild, this.parent)) {
50
+ const strategy = getAttachmentStrategy(this.parent);
51
+ if (strategy) {
52
+ detachChild(oldChild, strategy);
53
+ }
54
+ else if (isRemovable(this.parent)) {
55
+ this.parent.remove(oldChild);
56
+ }
57
+ }
58
+ if (this.child && this.parent) {
59
+ const strategy = getAttachmentStrategy(this.parent);
60
+ if (strategy) {
61
+ attachChild(this.child, strategy);
62
+ }
63
+ this.setupCssProvider();
64
+ this.child.addCssClass(this.className);
65
+ scheduleAfterCommit(() => {
66
+ if (!this.child)
67
+ return;
68
+ const initial = this.props.initial;
69
+ const animate = this.props.animate;
70
+ if (initial === false || !this.props.animateOnMount) {
71
+ if (animate) {
72
+ this.currentValues = { ...animate };
73
+ this.applyValues(this.currentValues);
74
+ }
75
+ }
76
+ else {
77
+ const initialValues = initial ?? animate ?? {};
78
+ this.currentValues = { ...initialValues };
79
+ this.applyValues(this.currentValues);
80
+ if (this.props.animateOnMount && animate) {
81
+ this.animateTo(animate);
82
+ }
83
+ }
84
+ }, CommitPriority.LOW);
85
+ }
86
+ }
87
+ unmount() {
88
+ if (this.isExiting) {
89
+ return;
90
+ }
91
+ if (this.props.exit && this.child) {
92
+ this.isExiting = true;
93
+ this.animateTo(this.props.exit, () => {
94
+ this.detachChildFromParent();
95
+ this.cleanup();
96
+ super.unmount();
97
+ });
98
+ }
99
+ else {
100
+ this.detachChildFromParent();
101
+ this.cleanup();
102
+ super.unmount();
103
+ }
104
+ }
105
+ detachChildFromParent() {
106
+ if (this.child && this.parent && this.isChildAttachedToParent()) {
107
+ const strategy = getAttachmentStrategy(this.parent);
108
+ if (strategy) {
109
+ detachChild(this.child, strategy);
110
+ }
111
+ else if (isRemovable(this.parent)) {
112
+ this.parent.remove(this.child);
113
+ }
114
+ }
115
+ }
116
+ isChildAttachedToParent() {
117
+ return this.isWidgetAttachedTo(this.child, this.parent);
118
+ }
119
+ isWidgetAttachedTo(child, parent) {
120
+ if (!child || !parent)
121
+ return false;
122
+ const childParent = child.getParent();
123
+ return childParent !== null && isObjectEqual(childParent, parent);
124
+ }
125
+ setupCssProvider() {
126
+ if (this.provider || !this.child)
127
+ return;
128
+ this.provider = new Gtk.CssProvider();
129
+ this.display = Gdk.DisplayManager.get().getDefaultDisplay();
130
+ if (this.display) {
131
+ Gtk.StyleContext.addProviderForDisplay(this.display, this.provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
132
+ }
133
+ }
134
+ cleanup() {
135
+ if (this.currentAnimation) {
136
+ this.currentAnimation.skip();
137
+ this.currentAnimation = null;
138
+ }
139
+ if (this.provider && this.display) {
140
+ Gtk.StyleContext.removeProviderForDisplay(this.display, this.provider);
141
+ }
142
+ if (this.child) {
143
+ this.child.removeCssClass(this.className);
144
+ }
145
+ this.provider = null;
146
+ this.display = null;
147
+ }
148
+ animateTo(target, onComplete) {
149
+ if (!this.child)
150
+ return;
151
+ if (this.currentAnimation) {
152
+ this.currentAnimation.skip();
153
+ this.currentAnimation = null;
154
+ }
155
+ const from = { ...this.currentValues };
156
+ const to = { ...target };
157
+ this.props.onAnimationStart?.();
158
+ const callback = new Adw.CallbackAnimationTarget((progress) => {
159
+ const interpolated = interpolate(from, to, progress);
160
+ this.currentValues = interpolated;
161
+ this.applyValues(interpolated);
162
+ });
163
+ const animation = this.createAnimation(this.child, callback);
164
+ animation.connect("done", () => {
165
+ this.currentValues = { ...to };
166
+ this.currentAnimation = null;
167
+ this.props.onAnimationComplete?.();
168
+ onComplete?.();
169
+ });
170
+ this.currentAnimation = animation;
171
+ const transition = this.props.transition;
172
+ const delay = transition?.delay ?? 0;
173
+ if (delay > 0) {
174
+ setTimeout(() => {
175
+ if (this.currentAnimation === animation) {
176
+ animation.play();
177
+ }
178
+ }, delay);
179
+ }
180
+ else {
181
+ animation.play();
182
+ }
183
+ }
184
+ createAnimation(widget, target) {
185
+ const mode = this.props.mode;
186
+ if (mode === "spring") {
187
+ return this.createSpringAnimation(widget, target);
188
+ }
189
+ return this.createTimedAnimation(widget, target);
190
+ }
191
+ createTimedAnimation(widget, target) {
192
+ const transition = this.props.transition;
193
+ const duration = transition?.duration ?? DEFAULT_TIMED_DURATION;
194
+ const animation = new Adw.TimedAnimation(widget, 0, 1, duration, target);
195
+ if (transition?.easing !== undefined) {
196
+ animation.setEasing(transition.easing);
197
+ }
198
+ if (transition?.repeat !== undefined) {
199
+ animation.setRepeatCount(transition.repeat);
200
+ }
201
+ if (transition?.reverse !== undefined) {
202
+ animation.setReverse(transition.reverse);
203
+ }
204
+ if (transition?.alternate !== undefined) {
205
+ animation.setAlternate(transition.alternate);
206
+ }
207
+ return animation;
208
+ }
209
+ createSpringAnimation(widget, target) {
210
+ const transition = this.props.transition;
211
+ const damping = transition?.damping ?? DEFAULT_SPRING_DAMPING;
212
+ const mass = transition?.mass ?? DEFAULT_SPRING_MASS;
213
+ const stiffness = transition?.stiffness ?? DEFAULT_SPRING_STIFFNESS;
214
+ const springParams = new Adw.SpringParams(damping, mass, stiffness);
215
+ const animation = new Adw.SpringAnimation(widget, 0, 1, springParams, target);
216
+ if (transition?.initialVelocity !== undefined) {
217
+ animation.setInitialVelocity(transition.initialVelocity);
218
+ }
219
+ if (transition?.clamp !== undefined) {
220
+ animation.setClamp(transition.clamp);
221
+ }
222
+ return animation;
223
+ }
224
+ applyValues(values) {
225
+ if (!this.provider) {
226
+ return;
227
+ }
228
+ if (this.child && !this.child.getCssClasses()?.includes(this.className)) {
229
+ this.child.addCssClass(this.className);
230
+ }
231
+ const css = buildCss(this.className, values);
232
+ if (css) {
233
+ this.provider.loadFromString(css);
234
+ }
235
+ }
236
+ arePropsEqual(a, b) {
237
+ if (a === b)
238
+ return true;
239
+ if (!a || !b)
240
+ return false;
241
+ const keysA = Object.keys(a);
242
+ const keysB = Object.keys(b);
243
+ if (keysA.length !== keysB.length)
244
+ return false;
245
+ for (const key of keysA) {
246
+ if (a[key] !== b[key])
247
+ return false;
248
+ }
249
+ return true;
250
+ }
251
+ }
252
+ registerNodeClass(AnimationNode);
@@ -1 +1,22 @@
1
- export {};
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { Node } from "../node.js";
3
+ import type { Container, Props } from "../types.js";
4
+ import type { Attachable } from "./internal/predicates.js";
5
+ export declare class EventControllerNode<T extends Gtk.EventController = Gtk.EventController> extends Node<T, Props> implements Attachable {
6
+ static priority: number;
7
+ static matches(type: string): boolean;
8
+ static createContainer(props: Props, containerClass: typeof Gtk.EventController): Gtk.EventController;
9
+ props: Props;
10
+ protected parentWidget: Gtk.Widget | null;
11
+ constructor(typeName: string, props: Props, container: T, rootContainer: Container);
12
+ canBeChildOf(parent: Node): boolean;
13
+ appendChild(_child: Node): void;
14
+ removeChild(_child: Node): void;
15
+ insertBefore(_child: Node, _before: Node): void;
16
+ attachTo(parent: Node): void;
17
+ detachFrom(_parent: Node): void;
18
+ updateProps(oldProps: Props | null, newProps: Props): void;
19
+ unmount(): void;
20
+ private applyProps;
21
+ private setProperty;
22
+ }
@@ -5,7 +5,7 @@ import { registerNodeClass } from "../registry.js";
5
5
  import { propNameToSignalName, resolvePropMeta, resolveSignal } from "./internal/utils.js";
6
6
  import { WidgetNode } from "./widget.js";
7
7
  const G_TYPE_INVALID = 0;
8
- class EventControllerNode extends Node {
8
+ export class EventControllerNode extends Node {
9
9
  static priority = 1;
10
10
  static matches(type) {
11
11
  return type in CONTROLLER_CLASSES;
@@ -70,7 +70,7 @@ class EventControllerNode extends Node {
70
70
  const signalName = propNameToSignalName(name);
71
71
  if (resolveSignal(this.container, signalName)) {
72
72
  const handler = typeof newValue === "function" ? newValue : undefined;
73
- this.signalStore.set(this, this.container, signalName, handler);
73
+ this.signalStore.set(this, this.container, signalName, handler, { blockable: false });
74
74
  }
75
75
  else if (newValue !== undefined) {
76
76
  this.setProperty(name, newValue);
@@ -1,10 +1,15 @@
1
1
  import { registerNodeClass } from "../registry.js";
2
2
  import { VirtualContainerNode } from "./abstract/virtual-container.js";
3
+ import { matchesInterface } from "./internal/utils.js";
4
+ const EXPANDER_ROW_INTERFACE_METHODS = ["addRow", "addAction", "remove"];
3
5
  class ExpanderRowRowNode extends VirtualContainerNode {
4
6
  static priority = 1;
5
7
  static matches(type) {
6
8
  return type === "ExpanderRowRow";
7
9
  }
10
+ canBeChildOf(parent) {
11
+ return matchesInterface(EXPANDER_ROW_INTERFACE_METHODS, parent.container);
12
+ }
8
13
  attachChild(parent, widget) {
9
14
  parent.addRow(widget);
10
15
  }
@@ -14,6 +19,9 @@ class ExpanderRowActionNode extends VirtualContainerNode {
14
19
  static matches(type) {
15
20
  return type === "ExpanderRowAction";
16
21
  }
22
+ canBeChildOf(parent) {
23
+ return matchesInterface(EXPANDER_ROW_INTERFACE_METHODS, parent.container);
24
+ }
17
25
  attachChild(parent, widget) {
18
26
  parent.addAction(widget);
19
27
  }
@@ -1,3 +1,4 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { PositionalChildNode } from "./abstract/positional-child.js";
3
4
  import { hasChanged } from "./internal/utils.js";
@@ -6,6 +7,9 @@ class FixedChildNode extends PositionalChildNode {
6
7
  static matches(type) {
7
8
  return type === "FixedChild";
8
9
  }
10
+ canBeChildOf(parent) {
11
+ return parent.container instanceof Gtk.Fixed;
12
+ }
9
13
  onChildChange(oldChild) {
10
14
  super.onChildChange(oldChild);
11
15
  if (this.child) {
@@ -1,4 +1,5 @@
1
1
  import { isObjectEqual } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
2
3
  import { registerNodeClass } from "../registry.js";
3
4
  import { PositionalChildNode } from "./abstract/positional-child.js";
4
5
  import { hasChanged } from "./internal/utils.js";
@@ -7,6 +8,9 @@ class GridChildNode extends PositionalChildNode {
7
8
  static matches(type) {
8
9
  return type === "GridChild";
9
10
  }
11
+ canBeChildOf(parent) {
12
+ return parent.container instanceof Gtk.Grid;
13
+ }
10
14
  attachToParent(parent, child) {
11
15
  const grid = parent;
12
16
  const column = this.props.column ?? 0;