From a6141011c2bed071eb908f4468274f44e18dc64a Mon Sep 17 00:00:00 2001 From: Aylur Date: Thu, 3 Oct 2024 21:39:40 +0000 Subject: docs: window toggling --- core/gjs/src/astalify.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts index a568a50..b69638b 100644 --- a/core/gjs/src/astalify.ts +++ b/core/gjs/src/astalify.ts @@ -19,11 +19,12 @@ function setChildren(parent: Gtk.Widget, children: Gtk.Widget[]) { if (ch) parent.remove(ch) } - else if (parent instanceof Gtk.Container && - !(parent instanceof Astal.Box || - parent instanceof Astal.Stack)) { - for(const ch of parent.get_children()) - parent.remove(ch) + else if ( + parent instanceof Gtk.Container && + !(parent instanceof Astal.Box || parent instanceof Astal.Stack) + ) { + for (const ch of parent.get_children()) + parent.remove(ch) } // TODO: add more container types -- cgit v1.2.3 From f294fd8265873eae343d73fe4d83047387e4bf79 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 9 Oct 2024 00:49:15 +0000 Subject: gjs: add gobject decorators --- core/gjs/src/gobject.ts | 166 ++++++++++++++++++++++++++++++++++++++++ core/gjs/src/jsx/jsx-runtime.ts | 2 +- core/gjs/src/widgets.ts | 1 - 3 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 core/gjs/src/gobject.ts (limited to 'core/gjs/src') 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 +} + +type PropertyDeclaration = + | InstanceType + | { $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> + +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 export const CircularProgress = astalify(Astal.CircularProgress) export type CircularProgressProps = ConstructProps - // DrawingArea export type DrawingArea = Widget export const DrawingArea = astalify(Gtk.DrawingArea) -- cgit v1.2.3 From 2fa83eb09047e603835c462ff0d8a04eba016807 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 9 Oct 2024 02:52:02 +0000 Subject: gjs: refactor widgets into classes --- core/gjs/src/astalify.ts | 446 +++++++++++++++++++--------------------- core/gjs/src/jsx/jsx-runtime.ts | 7 +- core/gjs/src/widgets.ts | 119 +++++++---- 3 files changed, 294 insertions(+), 278 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts index b69638b..76c2081 100644 --- a/core/gjs/src/astalify.ts +++ b/core/gjs/src/astalify.ts @@ -1,59 +1,8 @@ import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "./binding.js" -import { Astal, Gtk, Gdk } from "./imports.js" +import { Astal, Gtk, Gdk, GObject } from "./imports.js" import { execAsync } from "./process.js" import Variable from "./variable.js" -Object.defineProperty(Astal.Box.prototype, "children", { - get() { return this.get_children() }, - set(v) { this.set_children(v) }, -}) - -function setChildren(parent: Gtk.Widget, children: Gtk.Widget[]) { - children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget - ? ch - : new Gtk.Label({ visible: true, label: String(ch) })) - - // remove - if (parent instanceof Gtk.Bin) { - const ch = parent.get_child() - if (ch) - parent.remove(ch) - } - else if ( - parent instanceof Gtk.Container && - !(parent instanceof Astal.Box || parent instanceof Astal.Stack) - ) { - for (const ch of parent.get_children()) - parent.remove(ch) - } - - // TODO: add more container types - if (parent instanceof Astal.Box) { - parent.set_children(children) - } - - else if (parent instanceof Astal.Stack) { - parent.set_children(children) - } - - else if (parent instanceof Astal.CenterBox) { - parent.startWidget = children[0] - parent.centerWidget = children[1] - parent.endWidget = children[2] - } - - else if (parent instanceof Astal.Overlay) { - const [child, ...overlays] = children - parent.set_child(child) - parent.set_overlays(overlays) - } - - else if (parent instanceof Gtk.Container) { - for (const ch of children) - parent.add(ch) - } -} - function mergeBindings(array: any[]) { function getValues(...args: any[]) { let i = 0 @@ -90,190 +39,215 @@ function setProp(obj: any, prop: string, value: any) { console.error(`could not set property "${prop}" on ${obj}`) } -export type Widget> = C & { - className: string - css: string - cursor: Cursor - clickThrough: boolean - toggleClassName(name: string, on?: boolean): void - hook( - object: Connectable, - signal: string, - callback: (self: Widget, ...args: any[]) => void, - ): Widget - hook( - object: Subscribable, - callback: (self: Widget, ...args: any[]) => void, - ): Widget -} - -function hook( - self: Gtk.Widget, - object: Connectable | Subscribable, - signalOrCallback: string | ((self: Gtk.Widget, ...args: any[]) => void), - callback?: (self: Gtk.Widget, ...args: any[]) => void, -) { - if (typeof object.connect === "function" && callback) { - const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => { - callback(self, ...args) - }) - self.connect("destroy", () => { - (object.disconnect as Connectable["disconnect"])(id) - }) - } - - else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") { - const unsub = object.subscribe((...args: unknown[]) => { - signalOrCallback(self, ...args) - }) - self.connect("destroy", unsub) - } - - return self -} - -function ctor(self: any, config: any = {}, children: any = []) { - const { setup, ...props } = config - props.visible ??= true - - // 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 - children = mergeBindings(children.flat(Infinity)) - if (children instanceof Binding) { - setChildren(self, children.get()) - self.connect("destroy", children.subscribe((v) => { - setChildren(self, v) - })) - } - else { - if (children.length > 0) { - setChildren(self, children) +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) + } } - } - // setup signal handlers - for (const [signal, callback] of onHandlers) { - if (typeof callback === "function") { - self.connect(signal, callback) + toggleClassName(cn: string, cond = true) { + Astal.widget_toggle_class_name(this, cn, cond) } - else { - self.connect(signal, () => execAsync(callback) - .then(print).catch(console.error)) + + 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 } - } - // setup bindings handlers - for (const [prop, binding] of bindings) { - if (prop === "child" || prop === "children") { - self.connect("destroy", binding.subscribe((v: any) => { - setChildren(self, v) - })) + 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) } - self.connect("destroy", binding.subscribe((v: any) => { - setProp(self, prop, v) - })) - setProp(self, prop, binding.get()) } - Object.assign(self, props) - setup?.(self) - return self -} - -function proxify< - C extends typeof Gtk.Widget, ->(klass: C) { - Object.defineProperty(klass.prototype, "className", { - get() { return Astal.widget_get_class_names(this).join(" ") }, - set(v) { Astal.widget_set_class_names(this, v.split(/\s+/)) }, - }) - - Object.defineProperty(klass.prototype, "css", { - get() { return Astal.widget_get_css(this) }, - set(v) { Astal.widget_set_css(this, v) }, - }) - - Object.defineProperty(klass.prototype, "cursor", { - get() { return Astal.widget_get_cursor(this) }, - set(v) { Astal.widget_set_cursor(this, v) }, - }) - - Object.defineProperty(klass.prototype, "clickThrough", { - get() { return Astal.widget_get_click_through(this) }, - set(v) { Astal.widget_set_click_through(this, v) }, - }) - - Object.assign(klass.prototype, { - hook: function (obj: any, sig: any, callback: any) { - return hook(this as InstanceType, obj, sig, callback) - }, - toggleClassName: function name(cn: string, cond = true) { - Astal.widget_toggle_class_name(this as InstanceType, cn, cond) - }, - set_class_name: function (name: string) { - // @ts-expect-error unknown key - this.className = name - }, - set_css: function (css: string) { - // @ts-expect-error unknown key - this.css = css + 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, + ), }, - set_cursor: function (cursor: string) { - // @ts-expect-error unknown key - this.cursor = cursor - }, - set_click_through: function (clickThrough: boolean) { - // @ts-expect-error unknown key - this.clickThrough = clickThrough - }, - }) - - const proxy = new Proxy(klass, { - construct(_, [conf, ...children]) { - // @ts-expect-error abstract class - return ctor(new klass(), conf, children) - }, - apply(_t, _a, [conf, ...children]) { - // @ts-expect-error abstract class - return ctor(new klass(), conf, children) - }, - }) + }, Widget) - return proxy -} - -export default function astalify< - C extends typeof Gtk.Widget, - P extends Record, - N extends string = "Widget", ->(klass: C) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type Astal = Omit & { - new(props?: P, ...children: Gtk.Widget[]): Widget> - (props?: P, ...children: Gtk.Widget[]): Widget> - } - - return proxify(klass) as unknown as Astal + return Widget } type BindableProps = { @@ -283,7 +257,7 @@ type BindableProps = { type SigHandler< W extends InstanceType, Args extends Array, -> = ((self: Widget, ...args: Args) => unknown) | string | string[] +> = ((self: W, ...args: Args) => unknown) | string | string[] export type ConstructProps< Self extends InstanceType, @@ -300,16 +274,18 @@ export type ConstructProps< cursor?: string clickThrough?: boolean }> & { - onDestroy?: (self: Widget) => unknown - onDraw?: (self: Widget) => unknown - onKeyPressEvent?: (self: Widget, event: Gdk.Event) => unknown - onKeyReleaseEvent?: (self: Widget, event: Gdk.Event) => unknown - onButtonPressEvent?: (self: Widget, event: Gdk.Event) => unknown - onButtonReleaseEvent?: (self: Widget, event: Gdk.Event) => unknown - onRealize?: (self: Widget) => unknown - setup?: (self: Widget) => void + 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 + type Cursor = | "default" | "help" diff --git a/core/gjs/src/jsx/jsx-runtime.ts b/core/gjs/src/jsx/jsx-runtime.ts index cffcb76..f40dc05 100644 --- a/core/gjs/src/jsx/jsx-runtime.ts +++ b/core/gjs/src/jsx/jsx-runtime.ts @@ -16,14 +16,15 @@ export function jsx( children = children.filter(Boolean) - if (typeof ctor === "string") - return (ctors as any)[ctor](props, children) - 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) diff --git a/core/gjs/src/widgets.ts b/core/gjs/src/widgets.ts index 0c30b51..50fe303 100644 --- a/core/gjs/src/widgets.ts +++ b/core/gjs/src/widgets.ts @@ -1,17 +1,22 @@ /* eslint-disable max-len */ -import { Astal, Gtk } from "./imports.js" -import astalify, { type ConstructProps, type Widget } from "./astalify.js" +import { Astal, GObject, Gtk } from "./imports.js" +import astalify, { type ConstructProps, type BindableChild } from "./astalify.js" export { astalify, ConstructProps } // Box -export type Box = Widget -export const Box = astalify(Astal.Box) +Object.defineProperty(Astal.Box.prototype, "children", { + get() { return this.get_children() }, + set(v) { this.set_children(v) }, +}) + export type BoxProps = ConstructProps +export class Box extends astalify(Astal.Box) { + static { GObject.registerClass({ GTypeName: "Box" }, this) } + constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } +} // Button -export type Button = Widget -export const Button = astalify(Astal.Button) export type ButtonProps = ConstructProps +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 CenterBox = Widget -export const CenterBox = astalify(Astal.CenterBox) export type CenterBoxProps = ConstructProps +export class CenterBox extends astalify(Astal.CenterBox) { + static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } + constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } +} // CircularProgress -export type CircularProgress = Widget -export const CircularProgress = astalify(Astal.CircularProgress) export type CircularProgressProps = ConstructProps +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 DrawingArea = Widget -export const DrawingArea = astalify(Gtk.DrawingArea) export type DrawingAreaProps = ConstructProps +export class DrawingArea extends astalify(Gtk.DrawingArea) { + static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) } + constructor(props?: DrawingAreaProps) { super(props as any) } +} // Entry -export type Entry = Widget -export const Entry = astalify(Gtk.Entry) export type EntryProps = ConstructProps +export class Entry extends astalify(Gtk.Entry) { + static { GObject.registerClass({ GTypeName: "Entry" }, this) } + constructor(props?: EntryProps) { super(props as any) } +} // EventBox -export type EventBox = Widget -export const EventBox = astalify(Astal.EventBox) export type EventBoxProps = ConstructProps - -// TODO: Fixed -// TODO: FlowBox - +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 Icon = Widget -export const Icon = astalify(Astal.Icon) export type IconProps = ConstructProps +export class Icon extends astalify(Astal.Icon) { + static { GObject.registerClass({ GTypeName: "Icon" }, this) } + constructor(props?: IconProps) { super(props as any) } +} // Label -export type Label = Widget -export const Label = astalify(Astal.Label) export type LabelProps = ConstructProps +export class Label extends astalify(Astal.Label) { + static { GObject.registerClass({ GTypeName: "Label" }, this) } + constructor(props?: LabelProps) { super(props as any) } +} // LevelBar -export type LevelBar = Widget -export const LevelBar = astalify(Astal.LevelBar) export type LevelBarProps = ConstructProps +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 Overlay = Widget -export const Overlay = astalify(Astal.Overlay) export type OverlayProps = ConstructProps +export class Overlay extends astalify(Astal.Overlay) { + static { GObject.registerClass({ GTypeName: "Overlay" }, this) } + constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } +} // Revealer -export type Revealer = Widget -export const Revealer = astalify(Gtk.Revealer) export type RevealerProps = ConstructProps +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 Scrollable = Widget -export const Scrollable = astalify(Astal.Scrollable) export type ScrollableProps = ConstructProps +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 Slider = Widget -export const Slider = astalify(Astal.Slider) export type SliderProps = ConstructProps +export class Slider extends astalify(Astal.Slider) { + static { GObject.registerClass({ GTypeName: "Slider" }, this) } + constructor(props?: SliderProps) { super(props as any) } +} // Stack -export type Stack = Widget -export const Stack = astalify(Astal.Stack) export type StackProps = ConstructProps +export class Stack extends astalify(Astal.Stack) { + static { GObject.registerClass({ GTypeName: "Stack" }, this) } + constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } +} // Switch -export type Switch = Widget -export const Switch = astalify(Gtk.Switch) export type SwitchProps = ConstructProps +export class Switch extends astalify(Gtk.Switch) { + static { GObject.registerClass({ GTypeName: "Switch" }, this) } + constructor(props?: SwitchProps) { super(props as any) } +} // Window -export type Window = Widget -export const Window = astalify(Astal.Window) export type WindowProps = ConstructProps +export class Window extends astalify(Astal.Window) { + static { GObject.registerClass({ GTypeName: "Window" }, this) } + constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } +} -- cgit v1.2.3 From d6cbbdfb3f6e81c8265af36c88fd9547d93f9cbd Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 9 Oct 2024 03:08:51 +0000 Subject: core: implement generic no_implicit_destroy prop --- core/gjs/src/astalify.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts index 76c2081..85d7531 100644 --- a/core/gjs/src/astalify.ts +++ b/core/gjs/src/astalify.ts @@ -77,14 +77,14 @@ export default function astalify< const ch = this.get_child() if (ch) this.remove(ch) - // if (ch && !children.includes(ch) && !this.noImplicitDestroy) - // ch?.destroy() + 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() + if (!children.includes(ch) && !this.noImplicitDestroy) + ch?.destroy() } } -- cgit v1.2.3 From 8108bcc6a34e0460f642dc724f0a26f33b938825 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 9 Oct 2024 11:50:50 +0000 Subject: gjs: fix widget props type delete test --- core/gjs/src/widgets.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/widgets.ts b/core/gjs/src/widgets.ts index 50fe303..e14ca0b 100644 --- a/core/gjs/src/widgets.ts +++ b/core/gjs/src/widgets.ts @@ -10,14 +10,14 @@ Object.defineProperty(Astal.Box.prototype, "children", { set(v) { this.set_children(v) }, }) -export type BoxProps = ConstructProps +export type BoxProps = ConstructProps export class Box extends astalify(Astal.Box) { static { GObject.registerClass({ GTypeName: "Box" }, this) } constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } } // Button -export type ButtonProps = ConstructProps +export type CenterBoxProps = ConstructProps export class CenterBox extends astalify(Astal.CenterBox) { static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } } // CircularProgress -export type CircularProgressProps = ConstructProps +export type CircularProgressProps = ConstructProps 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 export class DrawingArea extends astalify(Gtk.DrawingArea) { @@ -54,7 +54,7 @@ export class DrawingArea extends astalify(Gtk.DrawingArea) { } // Entry -export type EntryProps = ConstructProps @@ -64,7 +64,7 @@ export class Entry extends astalify(Gtk.Entry) { } // EventBox -export type EventBoxProps = ConstructProps +export type IconProps = ConstructProps 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 +export type LabelProps = ConstructProps 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 +export type LevelBarProps = ConstructProps export class LevelBar extends astalify(Astal.LevelBar) { static { GObject.registerClass({ GTypeName: "LevelBar" }, this) } constructor(props?: LevelBarProps) { super(props as any) } @@ -103,28 +103,28 @@ export class LevelBar extends astalify(Astal.LevelBar) { // TODO: ListBox // Overlay -export type OverlayProps = ConstructProps +export type OverlayProps = ConstructProps export class Overlay extends astalify(Astal.Overlay) { static { GObject.registerClass({ GTypeName: "Overlay" }, this) } constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } } // Revealer -export type RevealerProps = ConstructProps +export type RevealerProps = ConstructProps 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 +export type ScrollableProps = ConstructProps 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 export class Slider extends astalify(Astal.Slider) { @@ -133,21 +133,21 @@ export class Slider extends astalify(Astal.Slider) { } // Stack -export type StackProps = ConstructProps +export type StackProps = ConstructProps export class Stack extends astalify(Astal.Stack) { static { GObject.registerClass({ GTypeName: "Stack" }, this) } constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } } // Switch -export type SwitchProps = ConstructProps +export type SwitchProps = ConstructProps 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 +export type WindowProps = ConstructProps export class Window extends astalify(Astal.Window) { static { GObject.registerClass({ GTypeName: "Window" }, this) } constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } -- cgit v1.2.3 From 0def6d7e9fd8b66220171ee228f0e845ed6edb57 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 9 Oct 2024 21:02:24 +0200 Subject: core(gjs, lua): widget constructor set props before children --- core/gjs/src/astalify.ts | 33 ++++++++++++++++----------------- core/gjs/src/gobject.ts | 16 ++++++++++++++-- 2 files changed, 30 insertions(+), 19 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts index 85d7531..29e7a5f 100644 --- a/core/gjs/src/astalify.ts +++ b/core/gjs/src/astalify.ts @@ -25,18 +25,17 @@ function mergeBindings(array: any[]) { 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) - if (Object.hasOwn(obj, prop)) - return (obj[prop] = value) + return (obj[prop] = value) } catch (error) { console.error(`could not set property "${prop}" on ${obj}:`, error) } - - console.error(`could not set property "${prop}" on ${obj}`) } export default function astalify< @@ -183,6 +182,19 @@ export default function astalify< return acc }, []) + // 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()) + } + // set children const mergedChildren = mergeBindings(children.flat(Infinity)) if (mergedChildren instanceof Binding) { @@ -208,19 +220,6 @@ export default function astalify< } } - // 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) } diff --git a/core/gjs/src/gobject.ts b/core/gjs/src/gobject.ts index 0b3551d..2658555 100644 --- a/core/gjs/src/gobject.ts +++ b/core/gjs/src/gobject.ts @@ -67,6 +67,18 @@ export function property(declaration: PropertyDeclaration = Object) { }, }) + 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) } @@ -81,10 +93,10 @@ export function property(declaration: PropertyDeclaration = Object) { } export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): -(target: any, signal: any, desc?: PropertyDescriptor) => void + (target: any, signal: any, desc?: PropertyDescriptor) => void export function signal(declaration?: SignalDeclaration): -(target: any, signal: any, desc?: PropertyDescriptor) => void + (target: any, signal: any, desc?: PropertyDescriptor) => void export function signal( declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, -- cgit v1.2.3 From 075c7e34aa0fbdc07c86965ab5a2ae79b92d3fd3 Mon Sep 17 00:00:00 2001 From: Aylur Date: Thu, 10 Oct 2024 00:13:37 +0000 Subject: core: reverse constructor props order to make stack shown property behave as expected --- core/gjs/src/astalify.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'core/gjs/src') diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts index 29e7a5f..c4cbc5c 100644 --- a/core/gjs/src/astalify.ts +++ b/core/gjs/src/astalify.ts @@ -182,19 +182,6 @@ export default function astalify< return acc }, []) - // 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()) - } - // set children const mergedChildren = mergeBindings(children.flat(Infinity)) if (mergedChildren instanceof Binding) { @@ -220,6 +207,19 @@ export default function astalify< } } + // 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) } -- cgit v1.2.3