diff options
author | Aylur <[email protected]> | 2024-10-15 01:26:32 +0200 |
---|---|---|
committer | Aylur <[email protected]> | 2024-10-15 01:26:32 +0200 |
commit | 2f71cd4c08bb4514efe43533e6a5d03535204c29 (patch) | |
tree | fc991a12e159ad645187862c90f40731794d6e47 /core/gjs/src | |
parent | 9fab13452a26ed55c01047d4225f699f43bba20d (diff) |
refactor lua and gjs lib
Diffstat (limited to 'core/gjs/src')
-rw-r--r-- | core/gjs/src/application.ts | 103 | ||||
-rw-r--r-- | core/gjs/src/astalify.ts | 322 | ||||
-rw-r--r-- | core/gjs/src/binding.ts | 89 | ||||
-rw-r--r-- | core/gjs/src/file.ts | 44 | ||||
-rw-r--r-- | core/gjs/src/gobject.ts | 178 | ||||
-rw-r--r-- | core/gjs/src/imports.ts | 10 | ||||
-rw-r--r-- | core/gjs/src/jsx/jsx-runtime.ts | 88 | ||||
-rw-r--r-- | core/gjs/src/process.ts | 68 | ||||
-rw-r--r-- | core/gjs/src/time.ts | 13 | ||||
-rw-r--r-- | core/gjs/src/variable.ts | 230 | ||||
-rw-r--r-- | core/gjs/src/widgets.ts | 154 |
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) } -} |