summaryrefslogtreecommitdiff
path: root/core/gjs/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/gjs/src')
-rw-r--r--core/gjs/src/application.ts103
-rw-r--r--core/gjs/src/astalify.ts322
-rw-r--r--core/gjs/src/binding.ts89
-rw-r--r--core/gjs/src/file.ts44
-rw-r--r--core/gjs/src/gobject.ts178
-rw-r--r--core/gjs/src/imports.ts10
-rw-r--r--core/gjs/src/jsx/jsx-runtime.ts88
-rw-r--r--core/gjs/src/process.ts68
-rw-r--r--core/gjs/src/time.ts13
-rw-r--r--core/gjs/src/variable.ts230
-rw-r--r--core/gjs/src/widgets.ts154
11 files changed, 0 insertions, 1299 deletions
diff --git a/core/gjs/src/application.ts b/core/gjs/src/application.ts
deleted file mode 100644
index bf82cbe..0000000
--- a/core/gjs/src/application.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-import { Astal, GObject, Gio, GLib } from "./imports.js"
-
-type RequestHandler = {
- (request: string, res: (response: any) => void): void
-}
-
-type Config = Partial<{
- icons: string
- instanceName: string
- gtkTheme: string
- iconTheme: string
- cursorTheme: string
- css: string
- requestHandler: RequestHandler
- main(...args: string[]): void
- client(message: (msg: string) => string, ...args: string[]): void
- hold: boolean
-}>
-
-// @ts-expect-error missing types
-// https://github.com/gjsify/ts-for-gir/issues/164
-import { setConsoleLogDomain } from "console"
-import { exit, programArgs } from "system"
-
-export default new (class AstalJS extends Astal.Application {
- static { GObject.registerClass(this) }
-
- eval(body: string): Promise<any> {
- return new Promise((res, rej) => {
- try {
- const fn = Function(`return (async function() {
- ${body.includes(";") ? body : `return ${body};`}
- })`)
- fn()()
- .then(res)
- .catch(rej)
- }
- catch (error) {
- rej(error)
- }
- })
- }
-
- requestHandler?: RequestHandler
-
- vfunc_request(msg: string, conn: Gio.SocketConnection): void {
- if (typeof this.requestHandler === "function") {
- this.requestHandler(msg, (response) => {
- Astal.write_sock(conn, String(response), (_, res) =>
- Astal.write_sock_finish(res),
- )
- })
- }
- else {
- super.vfunc_request(msg, conn)
- }
- }
-
- apply_css(style: string, reset = false) {
- super.apply_css(style, reset)
- }
-
- quit(code?: number): void {
- super.quit()
- exit(code ?? 0)
- }
-
- start({ requestHandler, css, hold, main, client, icons, ...cfg }: Config = {}) {
- client ??= () => {
- print(`Astal instance "${this.instanceName}" already running`)
- exit(1)
- }
-
- Object.assign(this, cfg)
- setConsoleLogDomain(this.instanceName)
-
- this.requestHandler = requestHandler
- this.connect("activate", () => {
- const path: string[] = import.meta.url.split("/").slice(3)
- const file = path.at(-1)!.replace(".js", ".css")
- const css = `/${path.slice(0, -1).join("/")}/${file}`
- if (file.endsWith(".css") && GLib.file_test(css, GLib.FileTest.EXISTS))
- this.apply_css(css, false)
-
- main?.(...programArgs)
- })
-
- if (!this.acquire_socket())
- return client(msg => Astal.Application.send_message(this.instanceName, msg)!, ...programArgs)
-
- if (css)
- this.apply_css(css, false)
-
- if (icons)
- this.add_icons(icons)
-
- hold ??= true
- if (hold)
- this.hold()
-
- this.runAsync([])
- }
-})
diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts
deleted file mode 100644
index c4cbc5c..0000000
--- a/core/gjs/src/astalify.ts
+++ /dev/null
@@ -1,322 +0,0 @@
-import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "./binding.js"
-import { Astal, Gtk, Gdk, GObject } from "./imports.js"
-import { execAsync } from "./process.js"
-import Variable from "./variable.js"
-
-function mergeBindings(array: any[]) {
- function getValues(...args: any[]) {
- let i = 0
- return array.map(value => value instanceof Binding
- ? args[i++]
- : value,
- )
- }
-
- const bindings = array.filter(i => i instanceof Binding)
-
- if (bindings.length === 0)
- return array
-
- if (bindings.length === 1)
- return bindings[0].as(getValues)
-
- return Variable.derive(bindings, getValues)()
-}
-
-function setProp(obj: any, prop: string, value: any) {
- try {
- // the setter method has to be used because
- // array like properties are not bound correctly as props
- const setter = `set_${snakeify(prop)}`
- if (typeof obj[setter] === "function")
- return obj[setter](value)
-
- return (obj[prop] = value)
- }
- catch (error) {
- console.error(`could not set property "${prop}" on ${obj}:`, error)
- }
-}
-
-export default function astalify<
- C extends { new(...args: any[]): Gtk.Widget },
->(cls: C) {
- class Widget extends cls {
- get css(): string { return Astal.widget_get_css(this) }
- set css(css: string) { Astal.widget_set_css(this, css) }
- get_css(): string { return this.css }
- set_css(css: string) { this.css = css }
-
- get className(): string { return Astal.widget_get_class_names(this).join(" ") }
- set className(className: string) { Astal.widget_set_class_names(this, className.split(/\s+/)) }
- get_class_name(): string { return this.className }
- set_class_name(className: string) { this.className = className }
-
- get cursor(): Cursor { return Astal.widget_get_cursor(this) as Cursor }
- set cursor(cursor: Cursor) { Astal.widget_set_cursor(this, cursor) }
- get_cursor(): Cursor { return this.cursor }
- set_cursor(cursor: Cursor) { this.cursor = cursor }
-
- get clickThrough(): boolean { return Astal.widget_get_click_through(this) }
- set clickThrough(clickThrough: boolean) { Astal.widget_set_click_through(this, clickThrough) }
- get_click_through(): boolean { return this.clickThrough }
- set_click_through(clickThrough: boolean) { this.clickThrough = clickThrough }
-
- declare __no_implicit_destroy: boolean
- get noImplicitDestroy(): boolean { return this.__no_implicit_destroy }
- set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value }
-
- _setChildren(children: Gtk.Widget[]) {
- children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget
- ? ch
- : new Gtk.Label({ visible: true, label: String(ch) }))
-
- // remove
- if (this instanceof Gtk.Bin) {
- const ch = this.get_child()
- if (ch)
- this.remove(ch)
- if (ch && !children.includes(ch) && !this.noImplicitDestroy)
- ch?.destroy()
- }
- else if (this instanceof Gtk.Container) {
- for (const ch of this.get_children()) {
- this.remove(ch)
- if (!children.includes(ch) && !this.noImplicitDestroy)
- ch?.destroy()
- }
- }
-
- // TODO: add more container types
- if (this instanceof Astal.Box) {
- this.set_children(children)
- }
-
- else if (this instanceof Astal.Stack) {
- this.set_children(children)
- }
-
- else if (this instanceof Astal.CenterBox) {
- this.startWidget = children[0]
- this.centerWidget = children[1]
- this.endWidget = children[2]
- }
-
- else if (this instanceof Astal.Overlay) {
- const [child, ...overlays] = children
- this.set_child(child)
- this.set_overlays(overlays)
- }
-
- else if (this instanceof Gtk.Container) {
- for (const ch of children)
- this.add(ch)
- }
- }
-
- toggleClassName(cn: string, cond = true) {
- Astal.widget_toggle_class_name(this, cn, cond)
- }
-
- hook(
- object: Connectable,
- signal: string,
- callback: (self: this, ...args: any[]) => void,
- ): this
- hook(
- object: Subscribable,
- callback: (self: this, ...args: any[]) => void,
- ): this
- hook(
- object: Connectable | Subscribable,
- signalOrCallback: string | ((self: this, ...args: any[]) => void),
- callback?: (self: this, ...args: any[]) => void,
- ) {
- if (typeof object.connect === "function" && callback) {
- const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => {
- callback(this, ...args)
- })
- this.connect("destroy", () => {
- (object.disconnect as Connectable["disconnect"])(id)
- })
- }
-
- else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") {
- const unsub = object.subscribe((...args: unknown[]) => {
- signalOrCallback(this, ...args)
- })
- this.connect("destroy", unsub)
- }
-
- return this
- }
-
- constructor(...params: any[]) {
- super()
- const [config] = params
-
- const { setup, child, children = [], ...props } = config
- props.visible ??= true
-
- if (child)
- children.unshift(child)
-
- // collect bindings
- const bindings = Object.keys(props).reduce((acc: any, prop) => {
- if (props[prop] instanceof Binding) {
- const binding = props[prop]
- delete props[prop]
- return [...acc, [prop, binding]]
- }
- return acc
- }, [])
-
- // collect signal handlers
- const onHandlers = Object.keys(props).reduce((acc: any, key) => {
- if (key.startsWith("on")) {
- const sig = kebabify(key).split("-").slice(1).join("-")
- const handler = props[key]
- delete props[key]
- return [...acc, [sig, handler]]
- }
- return acc
- }, [])
-
- // set children
- const mergedChildren = mergeBindings(children.flat(Infinity))
- if (mergedChildren instanceof Binding) {
- this._setChildren(mergedChildren.get())
- this.connect("destroy", mergedChildren.subscribe((v) => {
- this._setChildren(v)
- }))
- }
- else {
- if (mergedChildren.length > 0) {
- this._setChildren(mergedChildren)
- }
- }
-
- // setup signal handlers
- for (const [signal, callback] of onHandlers) {
- if (typeof callback === "function") {
- this.connect(signal, callback)
- }
- else {
- this.connect(signal, () => execAsync(callback)
- .then(print).catch(console.error))
- }
- }
-
- // setup bindings handlers
- for (const [prop, binding] of bindings) {
- if (prop === "child" || prop === "children") {
- this.connect("destroy", binding.subscribe((v: any) => {
- this._setChildren(v)
- }))
- }
- this.connect("destroy", binding.subscribe((v: any) => {
- setProp(this, prop, v)
- }))
- setProp(this, prop, binding.get())
- }
-
- Object.assign(this, props)
- setup?.(this)
- }
- }
-
- GObject.registerClass({
- GTypeName: `Astal_${cls.name}`,
- Properties: {
- "class-name": GObject.ParamSpec.string(
- "class-name", "", "", GObject.ParamFlags.READWRITE, "",
- ),
- "css": GObject.ParamSpec.string(
- "css", "", "", GObject.ParamFlags.READWRITE, "",
- ),
- "cursor": GObject.ParamSpec.string(
- "cursor", "", "", GObject.ParamFlags.READWRITE, "default",
- ),
- "click-through": GObject.ParamSpec.boolean(
- "click-through", "", "", GObject.ParamFlags.READWRITE, false,
- ),
- "no-implicit-destroy": GObject.ParamSpec.boolean(
- "no-implicit-destroy", "", "", GObject.ParamFlags.READWRITE, false,
- ),
- },
- }, Widget)
-
- return Widget
-}
-
-type BindableProps<T> = {
- [K in keyof T]: Binding<T[K]> | T[K];
-}
-
-type SigHandler<
- W extends InstanceType<typeof Gtk.Widget>,
- Args extends Array<unknown>,
-> = ((self: W, ...args: Args) => unknown) | string | string[]
-
-export type ConstructProps<
- Self extends InstanceType<typeof Gtk.Widget>,
- Props extends Gtk.Widget.ConstructorProps,
- Signals extends Record<`on${string}`, Array<unknown>> = Record<`on${string}`, any[]>,
-> = Partial<{
- // @ts-expect-error can't assign to unknown, but it works as expected though
- [S in keyof Signals]: SigHandler<Self, Signals[S]>
-}> & Partial<{
- [Key in `on${string}`]: SigHandler<Self, any[]>
-}> & BindableProps<Partial<Props> & {
- className?: string
- css?: string
- cursor?: string
- clickThrough?: boolean
-}> & {
- onDestroy?: (self: Self) => unknown
- onDraw?: (self: Self) => unknown
- onKeyPressEvent?: (self: Self, event: Gdk.Event) => unknown
- onKeyReleaseEvent?: (self: Self, event: Gdk.Event) => unknown
- onButtonPressEvent?: (self: Self, event: Gdk.Event) => unknown
- onButtonReleaseEvent?: (self: Self, event: Gdk.Event) => unknown
- onRealize?: (self: Self) => unknown
- setup?: (self: Self) => void
-}
-
-export type BindableChild = Gtk.Widget | Binding<Gtk.Widget>
-
-type Cursor =
- | "default"
- | "help"
- | "pointer"
- | "context-menu"
- | "progress"
- | "wait"
- | "cell"
- | "crosshair"
- | "text"
- | "vertical-text"
- | "alias"
- | "copy"
- | "no-drop"
- | "move"
- | "not-allowed"
- | "grab"
- | "grabbing"
- | "all-scroll"
- | "col-resize"
- | "row-resize"
- | "n-resize"
- | "e-resize"
- | "s-resize"
- | "w-resize"
- | "ne-resize"
- | "nw-resize"
- | "sw-resize"
- | "se-resize"
- | "ew-resize"
- | "ns-resize"
- | "nesw-resize"
- | "nwse-resize"
- | "zoom-in"
- | "zoom-out"
diff --git a/core/gjs/src/binding.ts b/core/gjs/src/binding.ts
deleted file mode 100644
index 95d905f..0000000
--- a/core/gjs/src/binding.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-export const snakeify = (str: string) => str
- .replace(/([a-z])([A-Z])/g, "$1_$2")
- .replaceAll("-", "_")
- .toLowerCase()
-
-export const kebabify = (str: string) => str
- .replace(/([a-z])([A-Z])/g, "$1-$2")
- .replaceAll("_", "-")
- .toLowerCase()
-
-export interface Subscribable<T = unknown> {
- subscribe(callback: (value: T) => void): () => void
- get(): T
- [key: string]: any
-}
-
-export interface Connectable {
- connect(signal: string, callback: (...args: any[]) => unknown): number
- disconnect(id: number): void
- [key: string]: any
-}
-
-export default class Binding<Value> {
- private transformFn = (v: any) => v
-
- #emitter: Subscribable<Value> | Connectable
- #prop?: string
-
- static bind<
- T extends Connectable,
- P extends keyof T,
- >(object: T, property: P): Binding<T[P]>
-
- static bind<T>(object: Subscribable<T>): Binding<T>
-
- static bind(emitter: Connectable | Subscribable, prop?: string) {
- return new Binding(emitter, prop)
- }
-
- private constructor(emitter: Connectable | Subscribable<Value>, prop?: string) {
- this.#emitter = emitter
- this.#prop = prop && kebabify(prop)
- }
-
- toString() {
- return `Binding<${this.#emitter}${this.#prop ? `, "${this.#prop}"` : ""}>`
- }
-
- as<T>(fn: (v: Value) => T): Binding<T> {
- const bind = new Binding(this.#emitter, this.#prop)
- bind.transformFn = (v: Value) => fn(this.transformFn(v))
- return bind as unknown as Binding<T>
- }
-
- get(): Value {
- if (typeof this.#emitter.get === "function")
- return this.transformFn(this.#emitter.get())
-
- if (typeof this.#prop === "string") {
- const getter = `get_${snakeify(this.#prop)}`
- if (typeof this.#emitter[getter] === "function")
- return this.transformFn(this.#emitter[getter]())
-
- return this.transformFn(this.#emitter[this.#prop])
- }
-
- throw Error("can not get value of binding")
- }
-
- subscribe(callback: (value: Value) => void): () => void {
- if (typeof this.#emitter.subscribe === "function") {
- return this.#emitter.subscribe(() => {
- callback(this.get())
- })
- }
- else if (typeof this.#emitter.connect === "function") {
- const signal = `notify::${this.#prop}`
- const id = this.#emitter.connect(signal, () => {
- callback(this.get())
- })
- return () => {
- (this.#emitter.disconnect as Connectable["disconnect"])(id)
- }
- }
- throw Error(`${this.#emitter} is not bindable`)
- }
-}
-
-export const { bind } = Binding
diff --git a/core/gjs/src/file.ts b/core/gjs/src/file.ts
deleted file mode 100644
index 90b33a1..0000000
--- a/core/gjs/src/file.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Astal, Gio } from "./imports.js"
-
-export function readFile(path: string): string {
- return Astal.read_file(path) || ""
-}
-
-export function readFileAsync(path: string): Promise<string> {
- return new Promise((resolve, reject) => {
- Astal.read_file_async(path, (_, res) => {
- try {
- resolve(Astal.read_file_finish(res) || "")
- }
- catch (error) {
- reject(error)
- }
- })
- })
-}
-
-export function writeFile(path: string, content: string): void {
- Astal.write_file(path, content)
-}
-
-export function writeFileAsync(path: string, content: string): Promise<void> {
- return new Promise((resolve, reject) => {
- Astal.write_file_async(path, content, (_, res) => {
- try {
- resolve(Astal.write_file_finish(res))
- }
- catch (error) {
- reject(error)
- }
- })
- })
-}
-
-export function monitorFile(
- path: string,
- callback: (file: string, event: Gio.FileMonitorEvent) => void,
-): Gio.FileMonitor {
- return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => {
- callback(file, event)
- })!
-}
diff --git a/core/gjs/src/gobject.ts b/core/gjs/src/gobject.ts
deleted file mode 100644
index 2658555..0000000
--- a/core/gjs/src/gobject.ts
+++ /dev/null
@@ -1,178 +0,0 @@
-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)
- }
- },
- })
-
- Object.defineProperty(target, `set_${name.replace("-", "_")}`, {
- value: function (v: any) {
- this[prop] = v
- }
- })
-
- Object.defineProperty(target, `get_${name.replace("-", "_")}`, {
- value: function () {
- return this[prop]
- }
- })
-
- 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/imports.ts b/core/gjs/src/imports.ts
deleted file mode 100644
index cbed004..0000000
--- a/core/gjs/src/imports.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// this file's purpose is to have glib versions in one place
-// this is only really needed for Gtk/Astal because
-// ts-gir might generate gtk4 versions too
-
-export { default as Astal } from "gi://Astal?version=0.1"
-export { default as GObject } from "gi://GObject?version=2.0"
-export { default as Gio } from "gi://Gio?version=2.0"
-export { default as Gtk } from "gi://Gtk?version=3.0"
-export { default as Gdk } from "gi://Gdk?version=3.0"
-export { default as GLib } from "gi://GLib?version=2.0"
diff --git a/core/gjs/src/jsx/jsx-runtime.ts b/core/gjs/src/jsx/jsx-runtime.ts
deleted file mode 100644
index f40dc05..0000000
--- a/core/gjs/src/jsx/jsx-runtime.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { Gtk } from "../imports.js"
-import * as Widget from "../widgets.js"
-
-function isArrowFunction(func: any): func is (args: any) => any {
- return !Object.hasOwn(func, "prototype")
-}
-
-export function jsx(
- ctor: keyof typeof ctors | typeof Gtk.Widget,
- { children, ...props }: any,
-) {
- children ??= []
-
- if (!Array.isArray(children))
- children = [children]
-
- children = children.filter(Boolean)
-
- if (children.length === 1)
- props.child = children[0]
- else if (children.length > 1)
- props.children = children
-
- if (typeof ctor === "string") {
- return new ctors[ctor](props)
- }
-
- if (isArrowFunction(ctor))
- return ctor(props)
-
- // @ts-expect-error can be class or function
- return new ctor(props)
-}
-
-const ctors = {
- box: Widget.Box,
- button: Widget.Button,
- centerbox: Widget.CenterBox,
- circularprogress: Widget.CircularProgress,
- drawingarea: Widget.DrawingArea,
- entry: Widget.Entry,
- eventbox: Widget.EventBox,
- // TODO: fixed
- // TODO: flowbox
- icon: Widget.Icon,
- label: Widget.Label,
- levelbar: Widget.LevelBar,
- // TODO: listbox
- overlay: Widget.Overlay,
- revealer: Widget.Revealer,
- scrollable: Widget.Scrollable,
- slider: Widget.Slider,
- stack: Widget.Stack,
- switch: Widget.Switch,
- window: Widget.Window,
-}
-
-declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace JSX {
- type Element = Gtk.Widget
- type ElementClass = Gtk.Widget
- interface IntrinsicElements {
- box: Widget.BoxProps
- button: Widget.ButtonProps
- centerbox: Widget.CenterBoxProps
- circularprogress: Widget.CircularProgressProps
- drawingarea: Widget.DrawingAreaProps
- entry: Widget.EntryProps
- eventbox: Widget.EventBoxProps
- // TODO: fixed
- // TODO: flowbox
- icon: Widget.IconProps
- label: Widget.LabelProps
- levelbar: Widget.LevelBarProps
- // TODO: listbox
- overlay: Widget.OverlayProps
- revealer: Widget.RevealerProps
- scrollable: Widget.ScrollableProps
- slider: Widget.SliderProps
- stack: Widget.StackProps
- switch: Widget.SwitchProps
- window: Widget.WindowProps
- }
- }
-}
-
-export const jsxs = jsx
diff --git a/core/gjs/src/process.ts b/core/gjs/src/process.ts
deleted file mode 100644
index bf8ea28..0000000
--- a/core/gjs/src/process.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { Astal } from "./imports.js"
-
-type Args = {
- cmd: string | string[]
- out?: (stdout: string) => void
- err?: (stderr: string) => void
-}
-
-export function subprocess(args: Args): Astal.Process
-
-export function subprocess(
- cmd: string | string[],
- onOut?: (stdout: string) => void,
- onErr?: (stderr: string) => void,
-): Astal.Process
-
-export function subprocess(
- argsOrCmd: Args | string | string[],
- onOut: (stdout: string) => void = print,
- onErr: (stderr: string) => void = printerr,
-) {
- const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string"
- const { cmd, err, out } = {
- cmd: args ? argsOrCmd : argsOrCmd.cmd,
- err: args ? onErr : argsOrCmd.err || onErr,
- out: args ? onOut : argsOrCmd.out || onOut,
- }
-
- const proc = Array.isArray(cmd)
- ? Astal.Process.subprocessv(cmd)
- : Astal.Process.subprocess(cmd)
-
- proc.connect("stdout", (_, stdout: string) => out(stdout))
- proc.connect("stderr", (_, stderr: string) => err(stderr))
- return proc
-}
-
-/** @throws {GLib.Error} Throws stderr */
-export function exec(cmd: string | string[]) {
- return Array.isArray(cmd)
- ? Astal.Process.execv(cmd)
- : Astal.Process.exec(cmd)
-}
-
-export function execAsync(cmd: string | string[]): Promise<string> {
- return new Promise((resolve, reject) => {
- if (Array.isArray(cmd)) {
- Astal.Process.exec_asyncv(cmd, (_, res) => {
- try {
- resolve(Astal.Process.exec_asyncv_finish(res))
- }
- catch (error) {
- reject(error)
- }
- })
- }
- else {
- Astal.Process.exec_async(cmd, (_, res) => {
- try {
- resolve(Astal.Process.exec_finish(res))
- }
- catch (error) {
- reject(error)
- }
- })
- }
- })
-}
diff --git a/core/gjs/src/time.ts b/core/gjs/src/time.ts
deleted file mode 100644
index 4e28ad0..0000000
--- a/core/gjs/src/time.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Astal } from "./imports.js"
-
-export function interval(interval: number, callback?: () => void) {
- return Astal.Time.interval(interval, () => void callback?.())
-}
-
-export function timeout(timeout: number, callback?: () => void) {
- return Astal.Time.timeout(timeout, () => void callback?.())
-}
-
-export function idle(callback?: () => void) {
- return Astal.Time.idle(() => void callback?.())
-}
diff --git a/core/gjs/src/variable.ts b/core/gjs/src/variable.ts
deleted file mode 100644
index b61e335..0000000
--- a/core/gjs/src/variable.ts
+++ /dev/null
@@ -1,230 +0,0 @@
-import Binding, { type Connectable, type Subscribable } from "./binding.js"
-import { Astal } from "./imports.js"
-import { interval } from "./time.js"
-import { execAsync, subprocess } from "./process.js"
-
-class VariableWrapper<T> extends Function {
- private variable!: Astal.VariableBase
- private errHandler? = console.error
-
- private _value: T
- private _poll?: Astal.Time
- private _watch?: Astal.Process
-
- private pollInterval = 1000
- private pollExec?: string[] | string
- private pollTransform?: (stdout: string, prev: T) => T
- private pollFn?: (prev: T) => T | Promise<T>
-
- private watchTransform?: (stdout: string, prev: T) => T
- private watchExec?: string[] | string
-
- constructor(init: T) {
- super()
- this._value = init
- this.variable = new Astal.VariableBase()
- this.variable.connect("dropped", () => {
- this.stopWatch()
- this.stopPoll()
- })
- this.variable.connect("error", (_, err) => this.errHandler?.(err))
- return new Proxy(this, {
- apply: (target, _, args) => target._call(args[0]),
- })
- }
-
- private _call<R = T>(transform?: (value: T) => R): Binding<R> {
- const b = Binding.bind(this)
- return transform ? b.as(transform) : b as unknown as Binding<R>
- }
-
- toString() {
- return String(`Variable<${this.get()}>`)
- }
-
- get(): T { return this._value }
- set(value: T) {
- if (value !== this._value) {
- this._value = value
- this.variable.emit("changed")
- }
- }
-
- startPoll() {
- if (this._poll)
- return
-
- if (this.pollFn) {
- this._poll = interval(this.pollInterval, () => {
- const v = this.pollFn!(this.get())
- if (v instanceof Promise) {
- v.then(v => this.set(v))
- .catch(err => this.variable.emit("error", err))
- }
- else {
- this.set(v)
- }
- })
- }
- else if (this.pollExec) {
- this._poll = interval(this.pollInterval, () => {
- execAsync(this.pollExec!)
- .then(v => this.set(this.pollTransform!(v, this.get())))
- .catch(err => this.variable.emit("error", err))
- })
- }
- }
-
- startWatch() {
- if (this._watch)
- return
-
- this._watch = subprocess({
- cmd: this.watchExec!,
- out: out => this.set(this.watchTransform!(out, this.get())),
- err: err => this.variable.emit("error", err),
- })
- }
-
- stopPoll() {
- this._poll?.cancel()
- delete this._poll
- }
-
- stopWatch() {
- this._watch?.kill()
- delete this._watch
- }
-
- isPolling() { return !!this._poll }
- isWatching() { return !!this._watch }
-
- drop() {
- this.variable.emit("dropped")
- }
-
- onDropped(callback: () => void) {
- this.variable.connect("dropped", callback)
- return this as unknown as Variable<T>
- }
-
- onError(callback: (err: string) => void) {
- delete this.errHandler
- this.variable.connect("error", (_, err) => callback(err))
- return this as unknown as Variable<T>
- }
-
- subscribe(callback: (value: T) => void) {
- const id = this.variable.connect("changed", () => {
- callback(this.get())
- })
- return () => this.variable.disconnect(id)
- }
-
- poll(
- interval: number,
- exec: string | string[],
- transform?: (stdout: string, prev: T) => T
- ): Variable<T>
-
- poll(
- interval: number,
- callback: (prev: T) => T | Promise<T>
- ): Variable<T>
-
- poll(
- interval: number,
- exec: string | string[] | ((prev: T) => T | Promise<T>),
- transform: (stdout: string, prev: T) => T = out => out as T,
- ) {
- this.stopPoll()
- this.pollInterval = interval
- this.pollTransform = transform
- if (typeof exec === "function") {
- this.pollFn = exec
- delete this.pollExec
- }
- else {
- this.pollExec = exec
- delete this.pollFn
- }
- this.startPoll()
- return this as unknown as Variable<T>
- }
-
- watch(
- exec: string | string[],
- transform: (stdout: string, prev: T) => T = out => out as T,
- ) {
- this.stopWatch()
- this.watchExec = exec
- this.watchTransform = transform
- this.startWatch()
- return this as unknown as Variable<T>
- }
-
- observe(
- objs: Array<[obj: Connectable, signal: string]>,
- callback: (...args: any[]) => T,
- ): Variable<T>
-
- observe(
- obj: Connectable,
- signal: string,
- callback: (...args: any[]) => T,
- ): Variable<T>
-
- observe(
- objs: Connectable | Array<[obj: Connectable, signal: string]>,
- sigOrFn: string | ((obj: Connectable, ...args: any[]) => T),
- callback?: (obj: Connectable, ...args: any[]) => T,
- ) {
- const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get())
- const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args))
-
- if (Array.isArray(objs)) {
- for (const obj of objs) {
- const [o, s] = obj
- const id = o.connect(s, set)
- this.onDropped(() => o.disconnect(id))
- }
- }
- else {
- if (typeof sigOrFn === "string") {
- const id = objs.connect(sigOrFn, set)
- this.onDropped(() => objs.disconnect(id))
- }
- }
-
- return this as unknown as Variable<T>
- }
-
- static derive<
- const Deps extends Array<Subscribable<any>>,
- Args extends {
- [K in keyof Deps]: Deps[K] extends Subscribable<infer T> ? T : never
- },
- V = Args,
- >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) {
- const update = () => fn(...deps.map(d => d.get()) as Args)
- const derived = new Variable(update())
- const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update())))
- derived.onDropped(() => unsubs.map(unsub => unsub()))
- return derived
- }
-}
-
-export interface Variable<T> extends Omit<VariableWrapper<T>, "bind"> {
- <R>(transform: (value: T) => R): Binding<R>
- (): Binding<T>
-}
-
-export const Variable = new Proxy(VariableWrapper as any, {
- apply: (_t, _a, args) => new VariableWrapper(args[0]),
-}) as {
- derive: typeof VariableWrapper["derive"]
- <T>(init: T): Variable<T>
- new<T>(init: T): Variable<T>
-}
-
-export default Variable
diff --git a/core/gjs/src/widgets.ts b/core/gjs/src/widgets.ts
deleted file mode 100644
index e14ca0b..0000000
--- a/core/gjs/src/widgets.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-/* eslint-disable max-len */
-import { Astal, GObject, Gtk } from "./imports.js"
-import astalify, { type ConstructProps, type BindableChild } from "./astalify.js"
-
-export { astalify, ConstructProps }
-
-// Box
-Object.defineProperty(Astal.Box.prototype, "children", {
- get() { return this.get_children() },
- set(v) { this.set_children(v) },
-})
-
-export type BoxProps = ConstructProps<Box, Astal.Box.ConstructorProps>
-export class Box extends astalify(Astal.Box) {
- static { GObject.registerClass({ GTypeName: "Box" }, this) }
- constructor(props?: BoxProps, ...children: Array<BindableChild>) { super({ children, ...props } as any) }
-}
-
-// Button
-export type ButtonProps = ConstructProps<Button, Astal.Button.ConstructorProps, {
- onClicked: []
- onClick: [event: Astal.ClickEvent]
- onClickRelease: [event: Astal.ClickEvent]
- onHover: [event: Astal.HoverEvent]
- onHoverLost: [event: Astal.HoverEvent]
- onScroll: [event: Astal.ScrollEvent]
-}>
-export class Button extends astalify(Astal.Button) {
- static { GObject.registerClass({ GTypeName: "Button" }, this) }
- constructor(props?: ButtonProps, child?: BindableChild) { super({ child, ...props } as any) }
-}
-
-// CenterBox
-export type CenterBoxProps = ConstructProps<CenterBox, Astal.CenterBox.ConstructorProps>
-export class CenterBox extends astalify(Astal.CenterBox) {
- static { GObject.registerClass({ GTypeName: "CenterBox" }, this) }
- constructor(props?: CenterBoxProps, ...children: Array<BindableChild>) { super({ children, ...props } as any) }
-}
-
-// CircularProgress
-export type CircularProgressProps = ConstructProps<CircularProgress, Astal.CircularProgress.ConstructorProps>
-export class CircularProgress extends astalify(Astal.CircularProgress) {
- static { GObject.registerClass({ GTypeName: "CircularProgress" }, this) }
- constructor(props?: CircularProgressProps, child?: BindableChild) { super({ child, ...props } as any) }
-}
-
-// DrawingArea
-export type DrawingAreaProps = ConstructProps<DrawingArea, Gtk.DrawingArea.ConstructorProps, {
- onDraw: [cr: any] // TODO: cairo types
-}>
-export class DrawingArea extends astalify(Gtk.DrawingArea) {
- static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) }
- constructor(props?: DrawingAreaProps) { super(props as any) }
-}
-
-// Entry
-export type EntryProps = ConstructProps<Entry, Gtk.Entry.ConstructorProps, {
- onChanged: []
- onActivate: []
-}>
-export class Entry extends astalify(Gtk.Entry) {
- static { GObject.registerClass({ GTypeName: "Entry" }, this) }
- constructor(props?: EntryProps) { super(props as any) }
-}
-
-// EventBox
-export type EventBoxProps = ConstructProps<EventBox, Astal.EventBox.ConstructorProps, {
- onClick: [event: Astal.ClickEvent]
- onClickRelease: [event: Astal.ClickEvent]
- onHover: [event: Astal.HoverEvent]
- onHoverLost: [event: Astal.HoverEvent]
- onScroll: [event: Astal.ScrollEvent]
-}>
-export class EventBox extends astalify(Astal.EventBox) {
- static { GObject.registerClass({ GTypeName: "EventBox" }, this) }
- constructor(props?: EventBoxProps, child?: BindableChild) { super({ child, ...props } as any) }
-}
-
-// // TODO: Fixed
-// // TODO: FlowBox
-//
-// Icon
-export type IconProps = ConstructProps<Icon, Astal.Icon.ConstructorProps>
-export class Icon extends astalify(Astal.Icon) {
- static { GObject.registerClass({ GTypeName: "Icon" }, this) }
- constructor(props?: IconProps) { super(props as any) }
-}
-
-// Label
-export type LabelProps = ConstructProps<Label, Astal.Label.ConstructorProps>
-export class Label extends astalify(Astal.Label) {
- static { GObject.registerClass({ GTypeName: "Label" }, this) }
- constructor(props?: LabelProps) { super(props as any) }
-}
-
-// LevelBar
-export type LevelBarProps = ConstructProps<LevelBar, Astal.LevelBar.ConstructorProps>
-export class LevelBar extends astalify(Astal.LevelBar) {
- static { GObject.registerClass({ GTypeName: "LevelBar" }, this) }
- constructor(props?: LevelBarProps) { super(props as any) }
-}
-
-// TODO: ListBox
-
-// Overlay
-export type OverlayProps = ConstructProps<Overlay, Astal.Overlay.ConstructorProps>
-export class Overlay extends astalify(Astal.Overlay) {
- static { GObject.registerClass({ GTypeName: "Overlay" }, this) }
- constructor(props?: OverlayProps, ...children: Array<BindableChild>) { super({ children, ...props } as any) }
-}
-
-// Revealer
-export type RevealerProps = ConstructProps<Revealer, Gtk.Revealer.ConstructorProps>
-export class Revealer extends astalify(Gtk.Revealer) {
- static { GObject.registerClass({ GTypeName: "Revealer" }, this) }
- constructor(props?: RevealerProps, child?: BindableChild) { super({ child, ...props } as any) }
-}
-
-// Scrollable
-export type ScrollableProps = ConstructProps<Scrollable, Astal.Scrollable.ConstructorProps>
-export class Scrollable extends astalify(Astal.Scrollable) {
- static { GObject.registerClass({ GTypeName: "Scrollable" }, this) }
- constructor(props?: ScrollableProps, child?: BindableChild) { super({ child, ...props } as any) }
-}
-
-// Slider
-export type SliderProps = ConstructProps<Slider, Astal.Slider.ConstructorProps, {
- onDragged: []
-}>
-export class Slider extends astalify(Astal.Slider) {
- static { GObject.registerClass({ GTypeName: "Slider" }, this) }
- constructor(props?: SliderProps) { super(props as any) }
-}
-
-// Stack
-export type StackProps = ConstructProps<Stack, Astal.Stack.ConstructorProps>
-export class Stack extends astalify(Astal.Stack) {
- static { GObject.registerClass({ GTypeName: "Stack" }, this) }
- constructor(props?: StackProps, ...children: Array<BindableChild>) { super({ children, ...props } as any) }
-}
-
-// Switch
-export type SwitchProps = ConstructProps<Switch, Gtk.Switch.ConstructorProps>
-export class Switch extends astalify(Gtk.Switch) {
- static { GObject.registerClass({ GTypeName: "Switch" }, this) }
- constructor(props?: SwitchProps) { super(props as any) }
-}
-
-// Window
-export type WindowProps = ConstructProps<Window, Astal.Window.ConstructorProps>
-export class Window extends astalify(Astal.Window) {
- static { GObject.registerClass({ GTypeName: "Window" }, this) }
- constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) }
-}