diff options
author | Aylur <[email protected]> | 2024-10-09 00:49:15 +0000 |
---|---|---|
committer | Aylur <[email protected]> | 2024-10-09 00:49:15 +0000 |
commit | f294fd8265873eae343d73fe4d83047387e4bf79 (patch) | |
tree | ca1a76f1d7ca80bed97bfb291fde3a83d8f5d602 /core | |
parent | 6932db56efec6c8114c0f78e08dc57f00b4a790f (diff) |
gjs: add gobject decorators
Diffstat (limited to 'core')
-rw-r--r-- | core/gjs/.gitignore | 1 | ||||
-rw-r--r-- | core/gjs/package.json | 5 | ||||
-rw-r--r-- | core/gjs/src/gobject.ts | 166 | ||||
-rw-r--r-- | core/gjs/src/jsx/jsx-runtime.ts | 2 | ||||
-rw-r--r-- | core/gjs/src/widgets.ts | 1 | ||||
-rw-r--r-- | core/gjs/tsconfig.json | 11 |
6 files changed, 178 insertions, 8 deletions
diff --git a/core/gjs/.gitignore b/core/gjs/.gitignore index 8c2cc59..53f4bc3 100644 --- a/core/gjs/.gitignore +++ b/core/gjs/.gitignore @@ -1,3 +1,4 @@ node_modules/ result/ dist/ +@girs/ diff --git a/core/gjs/package.json b/core/gjs/package.json index e3c86d0..6440b65 100644 --- a/core/gjs/package.json +++ b/core/gjs/package.json @@ -21,7 +21,8 @@ "./process": "./src/process.ts", "./time": "./src/time.ts", "./variable": "./src/variable.ts", - "./widgets": "./src/widgets.ts" + "./widgets": "./src/widgets.ts", + "./gobject": "./src/gobject.ts" }, "engines": { "gjs": ">=1.79.0" @@ -41,6 +42,6 @@ }, "scripts": { "lint": "eslint . --fix", - "types": "ts-for-gir generate -o node_modules/@girs --package" + "types": "ts-for-gir generate -o @girs" } } diff --git a/core/gjs/src/gobject.ts b/core/gjs/src/gobject.ts new file mode 100644 index 0000000..0b3551d --- /dev/null +++ b/core/gjs/src/gobject.ts @@ -0,0 +1,166 @@ +import GObject from "gi://GObject" +export default GObject + +const meta = Symbol("meta") + +const { ParamSpec, ParamFlags } = GObject + +const kebabify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replaceAll("_", "-") + .toLowerCase() + +type SignalDeclaration = { + flags?: GObject.SignalFlags + accumulator?: GObject.AccumulatorType + return_type?: GObject.GType + param_types?: Array<GObject.GType> +} + +type PropertyDeclaration = + | InstanceType<typeof GObject.ParamSpec> + | { $gtype: GObject.GType } + | typeof String + | typeof Number + | typeof Boolean + | typeof Object + +type GObjectConstructor = { + [meta]?: { + Properties?: { [key: string]: GObject.ParamSpec } + Signals?: { [key: string]: GObject.SignalDefinition } + } + new(...args: any[]): any +} + +type MetaInfo = GObject.MetaInfo<never, Array<{ $gtype: GObject.GType }>, never> + +export function register(options: MetaInfo = {}) { + return function (cls: GObjectConstructor) { + GObject.registerClass({ + Signals: { ...cls[meta]?.Signals }, + Properties: { ...cls[meta]?.Properties }, + ...options, + }, cls) + } +} + +export function property(declaration: PropertyDeclaration = Object) { + return function (target: any, prop: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Properties ??= {} + + const name = kebabify(prop) + + if (!desc) { + let value = defaultValue(declaration) + + Object.defineProperty(target, prop, { + get() { + return value + }, + set(v) { + if (v !== value) { + value = v + this.notify(name) + } + }, + }) + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, ParamFlags.READWRITE, declaration) + } + + else { + let flags = 0 + if (desc.get) flags |= ParamFlags.READABLE + if (desc.set) flags |= ParamFlags.WRITABLE + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, flags, declaration) + } + } +} + +export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal(declaration?: SignalDeclaration): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal( + declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, + ...params: Array<{ $gtype: GObject.GType } | typeof Object> +) { + return function (target: any, signal: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Signals ??= {} + + const name = kebabify(signal) + + if (declaration || params.length > 0) { + // @ts-expect-error TODO: type assert + const arr = [declaration, ...params].map(v => v.$gtype) + target.constructor[meta].Signals[name] = { + param_types: arr, + } + } + else { + target.constructor[meta].Signals[name] = declaration + } + + if (!desc) { + Object.defineProperty(target, signal, { + value: function (...args: any[]) { + this.emit(name, ...args) + }, + }) + } + else { + const og: ((...args: any[]) => void) = desc.value + desc.value = function (...args: any[]) { + // @ts-expect-error not typed + this.emit(name, ...args) + } + Object.defineProperty(target, `on_${name.replace("-", "_")}`, { + value: function (...args: any[]) { + return og(...args) + }, + }) + } + } +} + +function pspec(name: string, flags: number, declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration + + switch (declaration) { + case String: + return ParamSpec.string(name, "", "", flags, "") + case Number: + return ParamSpec.double(name, "", "", flags, -Number.MAX_VALUE, Number.MAX_VALUE, 0) + case Boolean: + return ParamSpec.boolean(name, "", "", flags, false) + case Object: + return ParamSpec.jsobject(name, "", "", flags) + default: + // @ts-expect-error misstyped + return ParamSpec.object(name, "", "", flags, declaration.$gtype) + } +} + +function defaultValue(declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration.get_default_value() + + switch (declaration) { + case String: + return "default-string" + case Number: + return 0 + case Boolean: + return false + case Object: + default: + return null + } +} diff --git a/core/gjs/src/jsx/jsx-runtime.ts b/core/gjs/src/jsx/jsx-runtime.ts index a0aafb6..cffcb76 100644 --- a/core/gjs/src/jsx/jsx-runtime.ts +++ b/core/gjs/src/jsx/jsx-runtime.ts @@ -63,7 +63,7 @@ declare global { box: Widget.BoxProps button: Widget.ButtonProps centerbox: Widget.CenterBoxProps - circularprogress: Widget.CircularProgressProps, + circularprogress: Widget.CircularProgressProps drawingarea: Widget.DrawingAreaProps entry: Widget.EntryProps eventbox: Widget.EventBoxProps diff --git a/core/gjs/src/widgets.ts b/core/gjs/src/widgets.ts index 82d3b8f..0c30b51 100644 --- a/core/gjs/src/widgets.ts +++ b/core/gjs/src/widgets.ts @@ -31,7 +31,6 @@ export type CircularProgress = Widget<Astal.CircularProgress> export const CircularProgress = astalify<typeof Astal.CircularProgress, CircularProgressProps, "CircularProgress">(Astal.CircularProgress) export type CircularProgressProps = ConstructProps<Astal.CircularProgress, Astal.CircularProgress.ConstructorProps> - // DrawingArea export type DrawingArea = Widget<Gtk.DrawingArea> export const DrawingArea = astalify<typeof Gtk.DrawingArea, DrawingAreaProps, "DrawingArea">(Gtk.DrawingArea) diff --git a/core/gjs/tsconfig.json b/core/gjs/tsconfig.json index b93779f..5a6ca1c 100644 --- a/core/gjs/tsconfig.json +++ b/core/gjs/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "experimentalDecorators": true, "target": "ESNext", "module": "ESNext", "lib": [ @@ -13,11 +14,13 @@ "checkJs": true, "allowJs": true, "jsx": "react-jsx", - "jsxImportSource": "./src/jsx", + "jsxImportSource": "src/jsx", + "baseUrl": ".", }, "include": [ - "./node_modules/@girs", - "./src/**/*", - "./index.ts", + "@girs", + "src/**/*", + "index.ts", + "app.ts" ] } |