@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.
- package/dist/animation/css-builder.d.ts +3 -0
- package/dist/animation/css-builder.js +53 -0
- package/dist/animation/types.d.ts +120 -0
- package/dist/animation/types.js +1 -0
- package/dist/fiber-root.js +1 -1
- package/dist/generated/jsx.d.ts +354 -351
- package/dist/generated/jsx.js +2 -0
- package/dist/host-config.js +6 -11
- package/dist/jsx.d.ts +39 -10
- package/dist/jsx.js +7 -7
- package/dist/node.d.ts +1 -0
- package/dist/node.js +9 -0
- package/dist/nodes/abstract/virtual-container.d.ts +5 -1
- package/dist/nodes/abstract/virtual-container.js +9 -0
- package/dist/nodes/abstract/virtual-single-child.js +2 -1
- package/dist/nodes/action-row-child.js +8 -0
- package/dist/nodes/animation.d.ts +1 -0
- package/dist/nodes/animation.js +252 -0
- package/dist/nodes/event-controller.d.ts +22 -1
- package/dist/nodes/event-controller.js +2 -2
- package/dist/nodes/expander-row-child.js +8 -0
- package/dist/nodes/fixed-child.js +4 -0
- package/dist/nodes/grid-child.js +4 -0
- package/dist/nodes/index.d.ts +2 -4
- package/dist/nodes/index.js +2 -4
- package/dist/nodes/internal/deferred-action.d.ts +1 -0
- package/dist/nodes/internal/deferred-action.js +3 -0
- package/dist/nodes/internal/list-store.d.ts +4 -6
- package/dist/nodes/internal/list-store.js +26 -31
- package/dist/nodes/internal/selection-model.js +0 -4
- package/dist/nodes/internal/signal-store.d.ts +4 -1
- package/dist/nodes/internal/signal-store.js +6 -8
- package/dist/nodes/internal/text-buffer-controller.js +1 -1
- package/dist/nodes/internal/tree-store.d.ts +2 -6
- package/dist/nodes/internal/tree-store.js +58 -56
- package/dist/nodes/list-view.js +2 -1
- package/dist/nodes/models/tree-list.js +1 -1
- package/dist/nodes/overlay-child.js +7 -7
- package/dist/nodes/pack-child.js +8 -0
- package/dist/nodes/shortcut-controller.js +6 -61
- package/dist/nodes/slot.js +1 -1
- package/dist/nodes/text-paintable.d.ts +6 -0
- package/dist/nodes/toggle-group.js +1 -1
- package/dist/nodes/toolbar-child.js +22 -29
- package/dist/nodes/tree-list-view.js +2 -1
- package/dist/nodes/web-view.d.ts +1 -0
- package/dist/nodes/web-view.js +29 -0
- package/dist/render.js +1 -1
- package/package.json +4 -4
- package/dist/nodes/abstract/positional-parent.d.ts +0 -18
- package/dist/nodes/abstract/positional-parent.js +0 -48
- package/dist/nodes/action-row.d.ts +0 -6
- package/dist/nodes/action-row.js +0 -13
- package/dist/nodes/animation/animation-controller.d.ts +0 -17
- package/dist/nodes/animation/animation-controller.js +0 -107
- package/dist/nodes/animation/animation-factory.d.ts +0 -15
- package/dist/nodes/animation/animation-factory.js +0 -25
- package/dist/nodes/animation/animation-node.d.ts +0 -9
- package/dist/nodes/animation/animation-node.js +0 -126
- package/dist/nodes/animation/animation-style-sheet.d.ts +0 -16
- package/dist/nodes/animation/animation-style-sheet.js +0 -74
- package/dist/nodes/animation/index.d.ts +0 -4
- package/dist/nodes/animation/index.js +0 -1
- package/dist/nodes/animation/property-mapper.d.ts +0 -11
- package/dist/nodes/animation/property-mapper.js +0 -36
- package/dist/nodes/animation/transform-state.d.ts +0 -11
- package/dist/nodes/animation/transform-state.js +0 -57
- package/dist/nodes/animation/widget-registry.d.ts +0 -5
- package/dist/nodes/animation/widget-registry.js +0 -42
- package/dist/nodes/expander-row.d.ts +0 -6
- package/dist/nodes/expander-row.js +0 -18
- package/dist/nodes/internal/base-store.d.ts +0 -9
- package/dist/nodes/internal/base-store.js +0 -20
- package/dist/nodes/pack.d.ts +0 -6
- package/dist/nodes/pack.js +0 -13
|
@@ -1,42 +1,18 @@
|
|
|
1
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
1
|
import { registerNodeClass } from "../registry.js";
|
|
3
|
-
import {
|
|
2
|
+
import { EventControllerNode } from "./event-controller.js";
|
|
4
3
|
import { ShortcutNode } from "./shortcut.js";
|
|
5
|
-
|
|
6
|
-
import { WidgetNode } from "./widget.js";
|
|
7
|
-
class ShortcutControllerNode extends VirtualNode {
|
|
4
|
+
class ShortcutControllerNode extends EventControllerNode {
|
|
8
5
|
static priority = 0;
|
|
9
6
|
static matches(type) {
|
|
10
7
|
return type === "GtkShortcutController";
|
|
11
8
|
}
|
|
12
|
-
controller = null;
|
|
13
|
-
parentWidget = null;
|
|
14
9
|
shortcuts = [];
|
|
15
|
-
setParent(widget) {
|
|
16
|
-
if (this.parentWidget && this.controller) {
|
|
17
|
-
this.parentWidget.removeController(this.controller);
|
|
18
|
-
}
|
|
19
|
-
this.parentWidget = widget;
|
|
20
|
-
if (widget) {
|
|
21
|
-
this.controller = new Gtk.ShortcutController();
|
|
22
|
-
this.applyScope();
|
|
23
|
-
widget.addController(this.controller);
|
|
24
|
-
for (const shortcut of this.shortcuts) {
|
|
25
|
-
this.addShortcutToController(shortcut);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
this.controller = null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
10
|
appendChild(child) {
|
|
33
11
|
if (!(child instanceof ShortcutNode)) {
|
|
34
12
|
throw new Error(`ShortcutController only accepts Shortcut children, got '${child.typeName}'`);
|
|
35
13
|
}
|
|
36
14
|
this.shortcuts.push(child);
|
|
37
|
-
|
|
38
|
-
this.addShortcutToController(child);
|
|
39
|
-
}
|
|
15
|
+
this.addShortcutToController(child);
|
|
40
16
|
}
|
|
41
17
|
removeChild(child) {
|
|
42
18
|
if (!(child instanceof ShortcutNode))
|
|
@@ -44,46 +20,15 @@ class ShortcutControllerNode extends VirtualNode {
|
|
|
44
20
|
const index = this.shortcuts.indexOf(child);
|
|
45
21
|
if (index !== -1) {
|
|
46
22
|
this.shortcuts.splice(index, 1);
|
|
47
|
-
if (
|
|
48
|
-
this.
|
|
23
|
+
if (child.shortcut) {
|
|
24
|
+
this.container.removeShortcut(child.shortcut);
|
|
49
25
|
}
|
|
50
26
|
}
|
|
51
27
|
}
|
|
52
|
-
updateProps(oldProps, newProps) {
|
|
53
|
-
super.updateProps(oldProps, newProps);
|
|
54
|
-
this.applyOwnProps(oldProps, newProps);
|
|
55
|
-
}
|
|
56
|
-
applyOwnProps(oldProps, newProps) {
|
|
57
|
-
if (hasChanged(oldProps, newProps, "scope")) {
|
|
58
|
-
this.applyScope();
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
unmount() {
|
|
62
|
-
this.setParent(null);
|
|
63
|
-
super.unmount();
|
|
64
|
-
}
|
|
65
|
-
canBeChildOf(parent) {
|
|
66
|
-
return parent instanceof WidgetNode;
|
|
67
|
-
}
|
|
68
|
-
attachTo(parent) {
|
|
69
|
-
if (parent instanceof WidgetNode) {
|
|
70
|
-
this.setParent(parent.container);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
detachFrom(_parent) {
|
|
74
|
-
this.setParent(null);
|
|
75
|
-
}
|
|
76
|
-
applyScope() {
|
|
77
|
-
if (!this.controller)
|
|
78
|
-
return;
|
|
79
|
-
this.controller.setScope(this.props.scope ?? Gtk.ShortcutScope.LOCAL);
|
|
80
|
-
}
|
|
81
28
|
addShortcutToController(node) {
|
|
82
|
-
if (!this.controller)
|
|
83
|
-
return;
|
|
84
29
|
node.createShortcut();
|
|
85
30
|
if (node.shortcut) {
|
|
86
|
-
this.
|
|
31
|
+
this.container.addShortcut(node.shortcut);
|
|
87
32
|
}
|
|
88
33
|
}
|
|
89
34
|
}
|
package/dist/nodes/slot.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type * as Gdk from "@gtkx/ffi/gdk";
|
|
2
2
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
3
|
import { VirtualNode } from "./virtual.js";
|
|
4
|
+
/**
|
|
5
|
+
* Props for the TextPaintable virtual element.
|
|
6
|
+
*
|
|
7
|
+
* Used to embed inline images or icons within text content in a GtkTextView.
|
|
8
|
+
*/
|
|
4
9
|
export type TextPaintableProps = {
|
|
10
|
+
/** The paintable (image, icon, etc.) to embed inline with the text */
|
|
5
11
|
paintable: Gdk.Paintable;
|
|
6
12
|
};
|
|
7
13
|
export declare class TextPaintableNode extends VirtualNode<TextPaintableProps> {
|
|
@@ -21,7 +21,7 @@ class ToggleGroupNode extends WidgetNode {
|
|
|
21
21
|
this.notifyHandler = null;
|
|
22
22
|
}
|
|
23
23
|
if (callback) {
|
|
24
|
-
this.notifyHandler = (pspec
|
|
24
|
+
this.notifyHandler = (pspec) => {
|
|
25
25
|
if (pspec.getName() === "active") {
|
|
26
26
|
callback(this.container.getActive(), this.container.getActiveName());
|
|
27
27
|
}
|
|
@@ -1,37 +1,30 @@
|
|
|
1
|
-
import { isObjectEqual } from "@gtkx/ffi";
|
|
2
1
|
import { registerNodeClass } from "../registry.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { VirtualContainerNode } from "./abstract/virtual-container.js";
|
|
3
|
+
import { matchesInterface } from "./internal/utils.js";
|
|
4
|
+
const TOOLBAR_INTERFACE_METHODS = ["addTopBar", "addBottomBar", "remove"];
|
|
5
|
+
class ToolbarTopNode extends VirtualContainerNode {
|
|
5
6
|
static priority = 1;
|
|
6
7
|
static matches(type) {
|
|
7
|
-
return type === "ToolbarTop"
|
|
8
|
+
return type === "ToolbarTop";
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
throw new Error("Expected ToolbarView reference to be set on ToolbarChildNode");
|
|
12
|
-
}
|
|
13
|
-
return this.parent;
|
|
10
|
+
canBeChildOf(parent) {
|
|
11
|
+
return matchesInterface(TOOLBAR_INTERFACE_METHODS, parent.container);
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
attachChild(parent, widget) {
|
|
14
|
+
parent.addTopBar(widget);
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
toolbar.addTopBar(this.child);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
toolbar.addBottomBar(this.child);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
16
|
+
}
|
|
17
|
+
class ToolbarBottomNode extends VirtualContainerNode {
|
|
18
|
+
static priority = 1;
|
|
19
|
+
static matches(type) {
|
|
20
|
+
return type === "ToolbarBottom";
|
|
21
|
+
}
|
|
22
|
+
canBeChildOf(parent) {
|
|
23
|
+
return matchesInterface(TOOLBAR_INTERFACE_METHODS, parent.container);
|
|
24
|
+
}
|
|
25
|
+
attachChild(parent, widget) {
|
|
26
|
+
parent.addBottomBar(widget);
|
|
35
27
|
}
|
|
36
28
|
}
|
|
37
|
-
registerNodeClass(
|
|
29
|
+
registerNodeClass(ToolbarTopNode);
|
|
30
|
+
registerNodeClass(ToolbarBottomNode);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isObjectEqual } from "@gtkx/ffi";
|
|
1
2
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
3
|
import { registerNodeClass } from "../registry.js";
|
|
3
4
|
import { TreeListItemRenderer } from "./internal/tree-list-item-renderer.js";
|
|
@@ -67,7 +68,7 @@ class TreeListViewNode extends WidgetNode {
|
|
|
67
68
|
const previousModel = this.treeList.getSelectionModel();
|
|
68
69
|
this.treeList.updateProps(oldProps ? filterProps(oldProps, RENDERER_PROP_NAMES) : null, filterProps(newProps, RENDERER_PROP_NAMES));
|
|
69
70
|
const currentModel = this.treeList.getSelectionModel();
|
|
70
|
-
if (previousModel
|
|
71
|
+
if (!isObjectEqual(previousModel, currentModel)) {
|
|
71
72
|
this.container.setModel(currentModel);
|
|
72
73
|
}
|
|
73
74
|
super.updateProps(oldProps ? filterProps(oldProps, PROP_NAMES) : null, filterProps(newProps, PROP_NAMES));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as WebKit from "@gtkx/ffi/webkit";
|
|
2
|
+
import { registerNodeClass } from "../registry.js";
|
|
3
|
+
import { filterProps, hasChanged, matchesAnyClass, propNameToSignalName, resolveSignal } from "./internal/utils.js";
|
|
4
|
+
import { WidgetNode } from "./widget.js";
|
|
5
|
+
const NON_BLOCKABLE_SIGNALS = ["load-changed"];
|
|
6
|
+
class WebViewNode extends WidgetNode {
|
|
7
|
+
static priority = 1;
|
|
8
|
+
static matches(_type, containerOrClass) {
|
|
9
|
+
return matchesAnyClass([WebKit.WebView], containerOrClass);
|
|
10
|
+
}
|
|
11
|
+
updateProps(oldProps, newProps) {
|
|
12
|
+
super.updateProps(oldProps ? filterProps(oldProps, NON_BLOCKABLE_SIGNALS) : null, filterProps(newProps, NON_BLOCKABLE_SIGNALS));
|
|
13
|
+
this.applyNonBlockableSignals(oldProps, newProps);
|
|
14
|
+
}
|
|
15
|
+
applyNonBlockableSignals(oldProps, newProps) {
|
|
16
|
+
for (const propName of NON_BLOCKABLE_SIGNALS) {
|
|
17
|
+
const camelCaseName = `on${propName.charAt(0).toUpperCase()}${propName.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase())}`;
|
|
18
|
+
if (hasChanged(oldProps, newProps, camelCaseName)) {
|
|
19
|
+
const signalName = propNameToSignalName(camelCaseName);
|
|
20
|
+
if (resolveSignal(this.container, signalName)) {
|
|
21
|
+
const newValue = newProps[camelCaseName];
|
|
22
|
+
const handler = typeof newValue === "function" ? newValue : undefined;
|
|
23
|
+
this.signalStore.set(this, this.container, signalName, handler, { blockable: false });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
registerNodeClass(WebViewNode);
|
package/dist/render.js
CHANGED
|
@@ -107,7 +107,7 @@ export const render = (element, appId, flags) => {
|
|
|
107
107
|
getSignalStore(application).forceUnblockAll();
|
|
108
108
|
const formattedError = formatBoundaryError(error);
|
|
109
109
|
console.error(formattedError.toString());
|
|
110
|
-
}, () => { }, () => { }
|
|
110
|
+
}, () => { }, () => { });
|
|
111
111
|
instance.updateContainer(_jsx(ApplicationContext.Provider, { value: application, children: element }), container, null, () => { });
|
|
112
112
|
};
|
|
113
113
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "Build GTK4 desktop applications with React and TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"react-reconciler": "^0.33.0",
|
|
40
|
-
"@gtkx/ffi": "0.
|
|
41
|
-
"@gtkx/gir": "0.
|
|
40
|
+
"@gtkx/ffi": "0.17.2",
|
|
41
|
+
"@gtkx/gir": "0.17.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@types/react-reconciler": "^0.
|
|
44
|
+
"@types/react-reconciler": "^0.33.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": "^19"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
-
import type { Node } from "../../node.js";
|
|
3
|
-
import type { Props } from "../../types.js";
|
|
4
|
-
import { WidgetNode } from "../widget.js";
|
|
5
|
-
type PositionalChildParentWidget = Gtk.Widget & {
|
|
6
|
-
remove(child: Gtk.Widget): void;
|
|
7
|
-
};
|
|
8
|
-
export declare abstract class PositionalParentNode<T extends PositionalChildParentWidget = PositionalChildParentWidget, P extends Props = Props> extends WidgetNode<T, P> {
|
|
9
|
-
protected abstract acceptedPositionalChildTypes: Set<string>;
|
|
10
|
-
protected abstract containerTypeName: string;
|
|
11
|
-
private isPositionalChild;
|
|
12
|
-
private formatExpectedTypes;
|
|
13
|
-
private assertValidChild;
|
|
14
|
-
appendChild(child: Node): void;
|
|
15
|
-
insertBefore(child: Node, before: Node): void;
|
|
16
|
-
removeChild(child: Node): void;
|
|
17
|
-
}
|
|
18
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { SlotNode } from "../slot.js";
|
|
2
|
-
import { WidgetNode } from "../widget.js";
|
|
3
|
-
import { VirtualContainerNode } from "./virtual-container.js";
|
|
4
|
-
export class PositionalParentNode extends WidgetNode {
|
|
5
|
-
isPositionalChild(child) {
|
|
6
|
-
return child instanceof VirtualContainerNode && this.acceptedPositionalChildTypes.has(child.typeName);
|
|
7
|
-
}
|
|
8
|
-
formatExpectedTypes() {
|
|
9
|
-
const types = Array.from(this.acceptedPositionalChildTypes)
|
|
10
|
-
.map((t) => `x.${t}`)
|
|
11
|
-
.join(", ");
|
|
12
|
-
return `${types}, or Widget`;
|
|
13
|
-
}
|
|
14
|
-
assertValidChild(child, operation) {
|
|
15
|
-
if (child instanceof SlotNode || child instanceof WidgetNode)
|
|
16
|
-
return;
|
|
17
|
-
const [verb, prep] = operation === "append"
|
|
18
|
-
? ["append", "to"]
|
|
19
|
-
: operation === "insert"
|
|
20
|
-
? ["insert", "into"]
|
|
21
|
-
: ["remove", "from"];
|
|
22
|
-
throw new Error(`Cannot ${verb} '${child.typeName}' ${prep} '${this.containerTypeName}': expected ${this.formatExpectedTypes()}`);
|
|
23
|
-
}
|
|
24
|
-
appendChild(child) {
|
|
25
|
-
if (this.isPositionalChild(child)) {
|
|
26
|
-
child.setParent(this.container);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
this.assertValidChild(child, "append");
|
|
30
|
-
super.appendChild(child);
|
|
31
|
-
}
|
|
32
|
-
insertBefore(child, before) {
|
|
33
|
-
if (this.isPositionalChild(child)) {
|
|
34
|
-
child.setParent(this.container);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
this.assertValidChild(child, "insert");
|
|
38
|
-
super.insertBefore(child, before);
|
|
39
|
-
}
|
|
40
|
-
removeChild(child) {
|
|
41
|
-
if (this.isPositionalChild(child)) {
|
|
42
|
-
child.unmount();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
this.assertValidChild(child, "remove");
|
|
46
|
-
super.removeChild(child);
|
|
47
|
-
}
|
|
48
|
-
}
|
package/dist/nodes/action-row.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { PREFIX_SUFFIX_INTERFACE_METHODS } from "../generated/internal.js";
|
|
2
|
-
import { registerNodeClass } from "../registry.js";
|
|
3
|
-
import { PositionalParentNode } from "./abstract/positional-parent.js";
|
|
4
|
-
import { matchesInterface } from "./internal/utils.js";
|
|
5
|
-
class ActionRowNode extends PositionalParentNode {
|
|
6
|
-
static priority = 0;
|
|
7
|
-
acceptedPositionalChildTypes = new Set(["ActionRowPrefix", "ActionRowSuffix"]);
|
|
8
|
-
containerTypeName = "ActionRow";
|
|
9
|
-
static matches(_type, containerOrClass) {
|
|
10
|
-
return matchesInterface(PREFIX_SUFFIX_INTERFACE_METHODS, containerOrClass);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
registerNodeClass(ActionRowNode);
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
-
import { type Transition } from "./animation-factory.js";
|
|
3
|
-
import { type AnimatableProperty } from "./property-mapper.js";
|
|
4
|
-
export type AnimatableProperties = Partial<Record<AnimatableProperty, number>>;
|
|
5
|
-
export declare class AnimationController {
|
|
6
|
-
private widget;
|
|
7
|
-
private activeAnimations;
|
|
8
|
-
private pendingCompletions;
|
|
9
|
-
private onComplete;
|
|
10
|
-
setWidget(widget: Gtk.Widget | null): void;
|
|
11
|
-
applyImmediate(properties: AnimatableProperties): void;
|
|
12
|
-
animate(from: AnimatableProperties, to: AnimatableProperties, transition: Transition, onComplete?: () => void): void;
|
|
13
|
-
private startPropertyAnimation;
|
|
14
|
-
private onAnimationDone;
|
|
15
|
-
skipAll(): void;
|
|
16
|
-
dispose(): void;
|
|
17
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import * as Adw from "@gtkx/ffi/adw";
|
|
2
|
-
import { createAnimation } from "./animation-factory.js";
|
|
3
|
-
import { getPropertyAccessor, isAnimatableProperty, isTransformProperty, } from "./property-mapper.js";
|
|
4
|
-
import { registerWidget, unregisterWidget } from "./widget-registry.js";
|
|
5
|
-
const DEFAULT_SPRING = { type: "spring", stiffness: 100, damping: 10 };
|
|
6
|
-
const DEFAULT_TIMED = { type: "timed", duration: 250, easing: Adw.Easing.EASE_OUT_CUBIC };
|
|
7
|
-
function getDefaultTransition(property) {
|
|
8
|
-
return isTransformProperty(property) ? DEFAULT_SPRING : DEFAULT_TIMED;
|
|
9
|
-
}
|
|
10
|
-
function isEmptyTransition(transition) {
|
|
11
|
-
return transition.type === undefined && !("duration" in transition) && !("stiffness" in transition);
|
|
12
|
-
}
|
|
13
|
-
export class AnimationController {
|
|
14
|
-
widget = null;
|
|
15
|
-
activeAnimations = new Map();
|
|
16
|
-
pendingCompletions = new Set();
|
|
17
|
-
onComplete = null;
|
|
18
|
-
setWidget(widget) {
|
|
19
|
-
if (this.widget === widget)
|
|
20
|
-
return;
|
|
21
|
-
if (this.widget) {
|
|
22
|
-
this.skipAll();
|
|
23
|
-
unregisterWidget(this.widget);
|
|
24
|
-
}
|
|
25
|
-
this.widget = widget;
|
|
26
|
-
if (widget) {
|
|
27
|
-
registerWidget(widget);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
applyImmediate(properties) {
|
|
31
|
-
if (!this.widget)
|
|
32
|
-
return;
|
|
33
|
-
for (const [key, value] of Object.entries(properties)) {
|
|
34
|
-
if (value === undefined || !isAnimatableProperty(key))
|
|
35
|
-
continue;
|
|
36
|
-
const accessor = getPropertyAccessor(key);
|
|
37
|
-
accessor.set(this.widget, value);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
animate(from, to, transition, onComplete) {
|
|
41
|
-
if (!this.widget)
|
|
42
|
-
return;
|
|
43
|
-
this.skipAll();
|
|
44
|
-
this.onComplete = onComplete ?? null;
|
|
45
|
-
this.pendingCompletions.clear();
|
|
46
|
-
const useDefaultTransitions = isEmptyTransition(transition);
|
|
47
|
-
const propertiesToAnimate = new Set([...Object.keys(from), ...Object.keys(to)]);
|
|
48
|
-
for (const key of propertiesToAnimate) {
|
|
49
|
-
if (!isAnimatableProperty(key))
|
|
50
|
-
continue;
|
|
51
|
-
const fromValue = from[key];
|
|
52
|
-
const toValue = to[key];
|
|
53
|
-
if (fromValue === undefined && toValue === undefined)
|
|
54
|
-
continue;
|
|
55
|
-
const accessor = getPropertyAccessor(key);
|
|
56
|
-
const currentValue = fromValue ?? accessor.get(this.widget);
|
|
57
|
-
const targetValue = toValue ?? accessor.get(this.widget);
|
|
58
|
-
if (currentValue === targetValue)
|
|
59
|
-
continue;
|
|
60
|
-
const effectiveTransition = useDefaultTransitions ? getDefaultTransition(key) : transition;
|
|
61
|
-
this.pendingCompletions.add(key);
|
|
62
|
-
this.startPropertyAnimation(key, currentValue, targetValue, effectiveTransition);
|
|
63
|
-
}
|
|
64
|
-
if (this.pendingCompletions.size === 0 && this.onComplete) {
|
|
65
|
-
this.onComplete();
|
|
66
|
-
this.onComplete = null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
startPropertyAnimation(property, from, to, transition) {
|
|
70
|
-
if (!this.widget)
|
|
71
|
-
return;
|
|
72
|
-
const accessor = getPropertyAccessor(property);
|
|
73
|
-
const widget = this.widget;
|
|
74
|
-
const animation = createAnimation(widget, from, to, transition, (value) => {
|
|
75
|
-
accessor.set(widget, value);
|
|
76
|
-
});
|
|
77
|
-
const connectionId = animation.connect("done", () => {
|
|
78
|
-
this.onAnimationDone(property);
|
|
79
|
-
});
|
|
80
|
-
this.activeAnimations.set(property, { animation, connectionId });
|
|
81
|
-
animation.play();
|
|
82
|
-
}
|
|
83
|
-
onAnimationDone(property) {
|
|
84
|
-
this.activeAnimations.delete(property);
|
|
85
|
-
this.pendingCompletions.delete(property);
|
|
86
|
-
if (this.pendingCompletions.size === 0 && this.onComplete) {
|
|
87
|
-
const callback = this.onComplete;
|
|
88
|
-
this.onComplete = null;
|
|
89
|
-
callback();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
skipAll() {
|
|
93
|
-
this.onComplete = null;
|
|
94
|
-
this.pendingCompletions.clear();
|
|
95
|
-
for (const { animation } of this.activeAnimations.values()) {
|
|
96
|
-
animation.skip();
|
|
97
|
-
}
|
|
98
|
-
this.activeAnimations.clear();
|
|
99
|
-
}
|
|
100
|
-
dispose() {
|
|
101
|
-
this.skipAll();
|
|
102
|
-
if (this.widget) {
|
|
103
|
-
unregisterWidget(this.widget);
|
|
104
|
-
}
|
|
105
|
-
this.widget = null;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import * as Adw from "@gtkx/ffi/adw";
|
|
2
|
-
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
3
|
-
export type TimedTransition = {
|
|
4
|
-
type?: "timed";
|
|
5
|
-
duration?: number;
|
|
6
|
-
easing?: Adw.Easing;
|
|
7
|
-
};
|
|
8
|
-
export type SpringTransition = {
|
|
9
|
-
type: "spring";
|
|
10
|
-
stiffness?: number;
|
|
11
|
-
damping?: number;
|
|
12
|
-
mass?: number;
|
|
13
|
-
};
|
|
14
|
-
export type Transition = TimedTransition | SpringTransition;
|
|
15
|
-
export declare function createAnimation(widget: Gtk.Widget, from: number, to: number, transition: Transition, onValue: (value: number) => void): Adw.Animation;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as Adw from "@gtkx/ffi/adw";
|
|
2
|
-
const DEFAULT_TIMED_DURATION = 250;
|
|
3
|
-
const DEFAULT_SPRING_STIFFNESS = 100;
|
|
4
|
-
const DEFAULT_SPRING_DAMPING = 10;
|
|
5
|
-
const DEFAULT_SPRING_MASS = 1;
|
|
6
|
-
function convertToDampingRatio(stiffness, damping, mass) {
|
|
7
|
-
const criticalDamping = 2 * Math.sqrt(stiffness * mass);
|
|
8
|
-
return damping / criticalDamping;
|
|
9
|
-
}
|
|
10
|
-
export function createAnimation(widget, from, to, transition, onValue) {
|
|
11
|
-
const target = new Adw.CallbackAnimationTarget(onValue);
|
|
12
|
-
if (transition.type === "spring") {
|
|
13
|
-
const stiffness = transition.stiffness ?? DEFAULT_SPRING_STIFFNESS;
|
|
14
|
-
const damping = transition.damping ?? DEFAULT_SPRING_DAMPING;
|
|
15
|
-
const mass = transition.mass ?? DEFAULT_SPRING_MASS;
|
|
16
|
-
const dampingRatio = convertToDampingRatio(stiffness, damping, mass);
|
|
17
|
-
const springParams = new Adw.SpringParams(dampingRatio, mass, stiffness);
|
|
18
|
-
return new Adw.SpringAnimation(widget, from, to, springParams, target);
|
|
19
|
-
}
|
|
20
|
-
const duration = transition.duration ?? DEFAULT_TIMED_DURATION;
|
|
21
|
-
const easing = transition.easing ?? Adw.Easing.EASE_OUT_CUBIC;
|
|
22
|
-
const animation = new Adw.TimedAnimation(widget, from, to, duration, target);
|
|
23
|
-
animation.setEasing(easing);
|
|
24
|
-
return animation;
|
|
25
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type AnimatableProperties } from "./animation-controller.js";
|
|
2
|
-
import type { Transition } from "./animation-factory.js";
|
|
3
|
-
export type AnimationProps = {
|
|
4
|
-
initial?: AnimatableProperties;
|
|
5
|
-
animate?: AnimatableProperties;
|
|
6
|
-
transition?: Transition;
|
|
7
|
-
onAnimationComplete?: () => void;
|
|
8
|
-
children?: React.ReactNode;
|
|
9
|
-
};
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { registerNodeClass } from "../../registry.js";
|
|
2
|
-
import { CommitPriority, scheduleAfterCommit } from "../../scheduler.js";
|
|
3
|
-
import { VirtualSingleChildNode } from "../abstract/virtual-single-child.js";
|
|
4
|
-
import { attachChild, detachChild, getAttachmentStrategy } from "../internal/child-attachment.js";
|
|
5
|
-
import { isRemovable } from "../internal/predicates.js";
|
|
6
|
-
import { WidgetNode } from "../widget.js";
|
|
7
|
-
import { AnimationController } from "./animation-controller.js";
|
|
8
|
-
class AnimationNode extends VirtualSingleChildNode {
|
|
9
|
-
static priority = 2;
|
|
10
|
-
controller = new AnimationController();
|
|
11
|
-
hasAppliedInitial = false;
|
|
12
|
-
hasStartedAnimation = false;
|
|
13
|
-
static matches(type) {
|
|
14
|
-
return type === "Animation";
|
|
15
|
-
}
|
|
16
|
-
appendChild(child) {
|
|
17
|
-
if (!(child instanceof WidgetNode)) {
|
|
18
|
-
throw new Error(`Cannot append '${child.typeName}' to 'x.Animation': expected Widget`);
|
|
19
|
-
}
|
|
20
|
-
const oldChild = this.child;
|
|
21
|
-
this.child = child.container;
|
|
22
|
-
scheduleAfterCommit(() => {
|
|
23
|
-
if (this.parent) {
|
|
24
|
-
this.onChildChange(oldChild);
|
|
25
|
-
}
|
|
26
|
-
}, CommitPriority.NORMAL);
|
|
27
|
-
}
|
|
28
|
-
removeChild(child) {
|
|
29
|
-
if (!(child instanceof WidgetNode)) {
|
|
30
|
-
throw new Error(`Cannot remove '${child.typeName}' from 'x.Animation': expected Widget`);
|
|
31
|
-
}
|
|
32
|
-
const oldChild = this.child;
|
|
33
|
-
scheduleAfterCommit(() => {
|
|
34
|
-
if (oldChild === this.child) {
|
|
35
|
-
this.child = null;
|
|
36
|
-
this.controller.setWidget(null);
|
|
37
|
-
}
|
|
38
|
-
if (this.parent && oldChild) {
|
|
39
|
-
this.onChildChange(oldChild);
|
|
40
|
-
}
|
|
41
|
-
}, CommitPriority.HIGH);
|
|
42
|
-
}
|
|
43
|
-
updateProps(oldProps, newProps) {
|
|
44
|
-
super.updateProps(oldProps, newProps);
|
|
45
|
-
const animateChanged = !shallowObjectEqual(oldProps?.animate, newProps.animate);
|
|
46
|
-
if (animateChanged) {
|
|
47
|
-
this.hasStartedAnimation = false;
|
|
48
|
-
}
|
|
49
|
-
if (this.child) {
|
|
50
|
-
this.controller.setWidget(this.child);
|
|
51
|
-
if (!this.hasAppliedInitial && newProps.initial) {
|
|
52
|
-
this.controller.applyImmediate(newProps.initial);
|
|
53
|
-
this.hasAppliedInitial = true;
|
|
54
|
-
}
|
|
55
|
-
if (!this.hasStartedAnimation && newProps.animate) {
|
|
56
|
-
this.hasStartedAnimation = true;
|
|
57
|
-
this.startAnimation();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
unmount() {
|
|
62
|
-
if (this.parent && this.child) {
|
|
63
|
-
const strategy = getAttachmentStrategy(this.parent);
|
|
64
|
-
if (strategy) {
|
|
65
|
-
detachChild(this.child, strategy);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
this.controller.dispose();
|
|
69
|
-
this.hasAppliedInitial = false;
|
|
70
|
-
this.child = null;
|
|
71
|
-
super.unmount();
|
|
72
|
-
}
|
|
73
|
-
onChildChange(oldChild) {
|
|
74
|
-
if (!this.parent)
|
|
75
|
-
return;
|
|
76
|
-
const strategy = getAttachmentStrategy(this.parent);
|
|
77
|
-
if (!strategy)
|
|
78
|
-
return;
|
|
79
|
-
if (oldChild && !this.child) {
|
|
80
|
-
detachChild(oldChild, strategy);
|
|
81
|
-
}
|
|
82
|
-
else if (oldChild && this.child && oldChild !== this.child) {
|
|
83
|
-
if (isRemovable(this.parent)) {
|
|
84
|
-
this.parent.remove(oldChild);
|
|
85
|
-
}
|
|
86
|
-
attachChild(this.child, strategy);
|
|
87
|
-
}
|
|
88
|
-
else if (!oldChild && this.child) {
|
|
89
|
-
attachChild(this.child, strategy);
|
|
90
|
-
}
|
|
91
|
-
if (!this.child)
|
|
92
|
-
return;
|
|
93
|
-
this.controller.setWidget(this.child);
|
|
94
|
-
if (!this.hasAppliedInitial && this.props.initial) {
|
|
95
|
-
this.controller.applyImmediate(this.props.initial);
|
|
96
|
-
this.hasAppliedInitial = true;
|
|
97
|
-
}
|
|
98
|
-
if (!this.hasStartedAnimation && this.props.animate) {
|
|
99
|
-
this.hasStartedAnimation = true;
|
|
100
|
-
this.startAnimation();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
startAnimation() {
|
|
104
|
-
const from = this.props.initial ?? {};
|
|
105
|
-
const to = this.props.animate ?? {};
|
|
106
|
-
const transition = this.props.transition ?? {};
|
|
107
|
-
const onComplete = this.props.onAnimationComplete;
|
|
108
|
-
this.controller.animate(from, to, transition, onComplete);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function shallowObjectEqual(a, b) {
|
|
112
|
-
if (a === b)
|
|
113
|
-
return true;
|
|
114
|
-
if (!a || !b)
|
|
115
|
-
return false;
|
|
116
|
-
const keysA = Object.keys(a);
|
|
117
|
-
const keysB = Object.keys(b);
|
|
118
|
-
if (keysA.length !== keysB.length)
|
|
119
|
-
return false;
|
|
120
|
-
for (const key of keysA) {
|
|
121
|
-
if (a[key] !== b[key])
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
registerNodeClass(AnimationNode);
|