diff --git a/packages/example/gen/godot/src/TestUi2.nodes.gen.ts b/packages/example/gen/godot/src/TestUi2.nodes.gen.ts new file mode 100644 index 0000000..a2d1200 --- /dev/null +++ b/packages/example/gen/godot/src/TestUi2.nodes.gen.ts @@ -0,0 +1,5 @@ +declare module "godot" { + interface SceneNodes { + "src/TestUi2.tscn": {}; + } +} diff --git a/packages/example/gen/godot/src/TestUi2.tscn.gen.ts b/packages/example/gen/godot/src/TestUi2.tscn.gen.ts new file mode 100644 index 0000000..89801b3 --- /dev/null +++ b/packages/example/gen/godot/src/TestUi2.tscn.gen.ts @@ -0,0 +1,6 @@ +import TestUi2 from "../../../src/TestUi2"; +declare module "godot" { + interface ResourceTypes { + "res://src/TestUi2.tscn": PackedScene; + } +} diff --git a/packages/example/src/TestUi1.ts b/packages/example/src/TestUi1.ts index bbb4586..c637de1 100644 --- a/packages/example/src/TestUi1.ts +++ b/packages/example/src/TestUi1.ts @@ -8,4 +8,8 @@ export default class TestUi1 extends Control { _ready(): void { Renderer.renderComponent(this, TestUi1Component) } + + _exit_tree(): void { + console.log("exit tree") + } } diff --git a/packages/example/src/TestUi1Component.tsx b/packages/example/src/TestUi1Component.tsx index cfdc55f..b92c927 100644 --- a/packages/example/src/TestUi1Component.tsx +++ b/packages/example/src/TestUi1Component.tsx @@ -1,5 +1,5 @@ import Godot, { Vector2 } from "godot" -import { useEffect, useState } from "react" +import { useState } from "react" import { Component } from "react-godot-renderer" @@ -11,26 +11,16 @@ const Button = Component.make(Godot.Button) export function TestUi1Component() { const [text, setText] = useState("Default text") - const textEditRef = TextEdit.useUnsafeRef() - const buttonRef = Button.useUnsafeRef() + const textEditRef = TextEdit.useRef() + Component.useSignal(textEditRef, "text_changed", function(this) { + setText(this.text) + }) - useEffect(() => { - const onTextChanged = Godot.Callable.create(() => { - setText(textEditRef.current.text) - }) + const buttonRef = Button.useRef() + Component.useSignal(buttonRef, "pressed", () => { + console.log("Pressed!") + }) - textEditRef.current.text_changed.connect(onTextChanged) - return () => { textEditRef.current.text_changed.disconnect(onTextChanged) } - }, [textEditRef.current]) - - useEffect(() => { - const onPressed = Godot.Callable.create(() => { - console.log("Pressed!") - }) - - buttonRef.current.pressed.connect(onPressed) - return () => { buttonRef.current.pressed.disconnect(onPressed) } - }, [buttonRef.current]) return ( > = { readonly ref?: React.RefObject } & { // biome-ignore lint/complexity/noBannedTypes: using Function here is completely fine - [K in keyof T as T[K] extends Function ? never : K]?: T[K] + [K in keyof T as T[K] extends Function | Godot.Signal ? never : K]?: T[K] } export interface Prototype> { @@ -36,3 +36,28 @@ export const make = >( ), Prototype, ) + +export declare namespace useSignal { + export type SignalNames> = { + [K in keyof T & string]: T[K] extends Godot.Signal ? K : never + }[keyof T & string] + + export type Function, N extends useSignal.SignalNames> = ( + T[N] extends Godot.Signal + ? (this: T, ...args: Parameters) => ReturnType + : never + ) +} + +export const useSignal = , N extends useSignal.SignalNames>( + ref: React.RefObject, + name: N, + f: useSignal.Function, +// biome-ignore lint/correctness/useExhaustiveDependencies: "f" is non-reactive +): void => React.useEffect(() => { + if (!ref.current) return + const signal = ref.current[name] as Godot.Signal + const callable = Godot.Callable.create(ref.current, f) + signal.connect(callable) + return () => { signal.disconnect(callable) } +}, [ref.current])