summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-10-09 00:49:15 +0000
committerAylur <[email protected]>2024-10-09 00:49:15 +0000
commitf294fd8265873eae343d73fe4d83047387e4bf79 (patch)
treeca1a76f1d7ca80bed97bfb291fde3a83d8f5d602 /core
parent6932db56efec6c8114c0f78e08dc57f00b4a790f (diff)
gjs: add gobject decorators
Diffstat (limited to 'core')
-rw-r--r--core/gjs/.gitignore1
-rw-r--r--core/gjs/package.json5
-rw-r--r--core/gjs/src/gobject.ts166
-rw-r--r--core/gjs/src/jsx/jsx-runtime.ts2
-rw-r--r--core/gjs/src/widgets.ts1
-rw-r--r--core/gjs/tsconfig.json11
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"
]
}