refactor(ags4): split up astalify code
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2025-01-14 10:12:20 -05:00
parent e44065588d
commit c3c4054793
22 changed files with 223 additions and 224 deletions

View file

@ -1,126 +1,22 @@
// A mixin class must have a constructor with a single rest parameter of type 'any[]'
/* eslint "@typescript-eslint/no-explicit-any": ["error", { "ignoreRestArgs": true }] */
import { property, register } from 'astal';
import { Gdk, Gtk } from 'astal/gtk4';
import Binding, { type Connectable, type Subscribable } from 'astal/binding';
import { Gtk, hook } from 'astal/gtk4';
import { type Connectable, type Subscribable } from 'astal/binding';
import construct from './construct';
import setupControllers from './controller';
import {
type EventController,
hook,
type BindableProps,
dummyBuilder,
type MixinParams,
noImplicitDestroy,
setChildren,
construct,
} from './_astal';
childType,
} from './generics';
import { type AstalifyProps, type BindableProps } from './_astal';
export { type AstalifyProps, type BindableProps };
export type BindableChild = Gtk.Widget | Binding<Gtk.Widget>;
export const type = Symbol('child type');
const dummyBuilder = new Gtk.Builder();
const setupControllers = <T>(widget: Gtk.Widget, {
onFocusEnter,
onFocusLeave,
onKeyPressed,
onKeyReleased,
onKeyModifier,
onLegacy,
onButtonPressed,
onButtonReleased,
onHoverEnter,
onHoverLeave,
onMotion,
onScroll,
onScrollDecelerate,
...props
}: EventController<Gtk.Widget> & T) => {
if (onFocusEnter || onFocusLeave) {
const focus = new Gtk.EventControllerFocus();
widget.add_controller(focus);
if (onFocusEnter) { focus.connect('focus-enter', () => onFocusEnter(widget)); }
if (onFocusLeave) { focus.connect('focus-leave', () => onFocusLeave(widget)); }
}
if (onKeyPressed || onKeyReleased || onKeyModifier) {
const key = new Gtk.EventControllerKey();
widget.add_controller(key);
if (onKeyPressed) {
key.connect('key-pressed', (_, val, code, state) => onKeyPressed(widget, val, code, state));
}
if (onKeyReleased) {
key.connect('key-released', (_, val, code, state) =>
onKeyReleased(widget, val, code, state));
}
if (onKeyModifier) {
key.connect('modifiers', (_, state) => onKeyModifier(widget, state));
}
}
if (onLegacy || onButtonPressed || onButtonReleased) {
const legacy = new Gtk.EventControllerLegacy();
widget.add_controller(legacy);
legacy.connect('event', (_, event) => {
if (event.get_event_type() === Gdk.EventType.BUTTON_PRESS) {
onButtonPressed?.(widget, event as Gdk.ButtonEvent);
}
if (event.get_event_type() === Gdk.EventType.BUTTON_RELEASE) {
onButtonReleased?.(widget, event as Gdk.ButtonEvent);
}
onLegacy?.(widget, event);
});
}
if (onMotion || onHoverEnter || onHoverLeave) {
const hover = new Gtk.EventControllerMotion();
widget.add_controller(hover);
if (onHoverEnter) {
hover.connect('enter', (_, x, y) => onHoverEnter(widget, x, y));
}
if (onHoverLeave) {
hover.connect('leave', () => onHoverLeave(widget));
}
if (onMotion) {
hover.connect('motion', (_, x, y) => onMotion(widget, x, y));
}
}
if (onScroll || onScrollDecelerate) {
const scroll = new Gtk.EventControllerScroll();
widget.add_controller(scroll);
if (onScroll) {
scroll.connect('scroll', (_, x, y) => onScroll(widget, x, y));
}
if (onScrollDecelerate) {
scroll.connect('decelerate', (_, x, y) => onScrollDecelerate(widget, x, y));
}
}
return props;
};
export default <
C extends new (...props: any[]) => Gtk.Widget,
C extends new (...props: MixinParams) => Gtk.Widget,
ConstructorProps,
>(
cls: C,
@ -151,12 +47,12 @@ export default <
}
declare private [type]: string;
declare private [childType]: string;
@property(String)
get type(): string { return this[type]; }
get type(): string { return this[childType]; }
set type(value: string) { this[type] = value; }
set type(value: string) { this[childType] = value; }
@property(Object)
@ -194,7 +90,7 @@ export default <
widget.vfunc_add_child(
dummyBuilder,
child,
type in widget ? widget[type] as string : null,
childType in widget ? widget[childType] as string : null,
);
}
}
@ -234,7 +130,7 @@ export default <
}
constructor(...params: any[]) {
constructor(...params: MixinParams) {
const props = params[0] || {};
super('cssName' in props ? { cssName: props.cssName } : {});
@ -264,5 +160,6 @@ export default <
Partial<BindableProps<ConstructorProps>>
>;
// override the parameters of the `super` constructor
return Widget as unknown as WidgetClass;
};

View file

@ -0,0 +1,27 @@
import { Variable } from 'astal';
import { Binding } from 'astal/binding';
export const mergeBindings = <Value = unknown>(
array: (Value | Binding<Value> | Binding<Value[]>)[],
): Value[] | Binding<Value[]> => {
const getValues = (args: Value[]) => {
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 as Value[];
}
if (bindings.length === 1) {
return (bindings[0] as Binding<Value[]>).as(getValues);
}
return Variable.derive(bindings, getValues)();
};

View file

@ -1,92 +1,14 @@
import { execAsync, Variable } from 'astal';
import { type Gdk, type Gtk, type ConstructProps } from 'astal/gtk4';
import { Binding, type Connectable, kebabify, snakeify, type Subscribable } from 'astal/binding';
import { execAsync } from 'astal';
import { type Gtk, type ConstructProps } from 'astal/gtk4';
import { Binding, kebabify, snakeify } from 'astal/binding';
export interface EventController<Self extends Gtk.Widget> {
onFocusEnter?: (self: Self) => void
onFocusLeave?: (self: Self) => void
import { mergeBindings } from './bindings';
import { type EventController } from './controller';
import { type AstalifyProps, type BindableProps, type GenericWidget, setChildren } from './generics';
onKeyPressed?: (self: Self, keyval: number, keycode: number, state: Gdk.ModifierType) => void
onKeyReleased?: (self: Self, keyval: number, keycode: number, state: Gdk.ModifierType) => void
onKeyModifier?: (self: Self, state: Gdk.ModifierType) => void
onLegacy?: (self: Self, event: Gdk.Event) => void
onButtonPressed?: (self: Self, state: Gdk.ButtonEvent) => void
onButtonReleased?: (self: Self, state: Gdk.ButtonEvent) => void
onHoverEnter?: (self: Self, x: number, y: number) => void
onHoverLeave?: (self: Self) => void
onMotion?: (self: Self, x: number, y: number) => void
onScroll?: (self: Self, dx: number, dy: number) => void
onScrollDecelerate?: (self: Self, vel_x: number, vel_y: number) => void
}
export type BindableProps<T> = {
[K in keyof T]: Binding<T[K]> | T[K];
};
export interface AstalifyProps {
css: string
child: Gtk.Widget
children: Gtk.Widget[]
}
export const noImplicitDestroy = Symbol('no no implicit destroy');
export const setChildren = Symbol('children setter method');
const mergeBindings = <Value = unknown>(
array: (Value | Binding<Value> | Binding<Value[]>)[],
): Value[] | Binding<Value[]> => {
const getValues = (args: Value[]) => {
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 as Value[];
}
if (bindings.length === 1) {
return (bindings[0] as Binding<Value[]>).as(getValues);
}
return Variable.derive(bindings, getValues)();
};
export const hook = <Widget extends Connectable>(
widget: Widget,
object: Connectable | Subscribable,
signalOrCallback: string | ((self: Widget, ...args: unknown[]) => void),
callback?: (self: Widget, ...args: unknown[]) => void,
) => {
if (typeof object.connect === 'function' && callback) {
const id = object.connect(signalOrCallback, (_: unknown, ...args: unknown[]) => {
callback(widget, ...args);
});
widget.connect('destroy', () => {
(object.disconnect as Connectable['disconnect'])(id);
});
}
else if (typeof object.subscribe === 'function' && typeof signalOrCallback === 'function') {
const unsub = object.subscribe((...args: unknown[]) => {
signalOrCallback(widget, ...args);
});
widget.connect('destroy', unsub);
}
};
export const construct = <
Self extends InstanceType<typeof Gtk.Widget> & {
[setChildren]: (children: Gtk.Widget[]) => void
},
export default <
Self extends GenericWidget,
Props extends Gtk.Widget.ConstructorProps,
>(
widget: Self,

View file

@ -0,0 +1,120 @@
import { Gdk, Gtk } from 'astal/gtk4';
export interface EventController<Self extends Gtk.Widget> {
onFocusEnter?: (self: Self) => void
onFocusLeave?: (self: Self) => void
onKeyPressed?: (self: Self, keyval: number, keycode: number, state: Gdk.ModifierType) => void
onKeyReleased?: (self: Self, keyval: number, keycode: number, state: Gdk.ModifierType) => void
onKeyModifier?: (self: Self, state: Gdk.ModifierType) => void
onLegacy?: (self: Self, event: Gdk.Event) => void
onButtonPressed?: (self: Self, state: Gdk.ButtonEvent) => void
onButtonReleased?: (self: Self, state: Gdk.ButtonEvent) => void
onHoverEnter?: (self: Self, x: number, y: number) => void
onHoverLeave?: (self: Self) => void
onMotion?: (self: Self, x: number, y: number) => void
onScroll?: (self: Self, dx: number, dy: number) => void
onScrollDecelerate?: (self: Self, vel_x: number, vel_y: number) => void
}
export default <T>(widget: Gtk.Widget, {
onFocusEnter,
onFocusLeave,
onKeyPressed,
onKeyReleased,
onKeyModifier,
onLegacy,
onButtonPressed,
onButtonReleased,
onHoverEnter,
onHoverLeave,
onMotion,
onScroll,
onScrollDecelerate,
...props
}: EventController<Gtk.Widget> & T) => {
if (onFocusEnter || onFocusLeave) {
const focus = new Gtk.EventControllerFocus();
widget.add_controller(focus);
if (onFocusEnter) { focus.connect('focus-enter', () => onFocusEnter(widget)); }
if (onFocusLeave) { focus.connect('focus-leave', () => onFocusLeave(widget)); }
}
if (onKeyPressed || onKeyReleased || onKeyModifier) {
const key = new Gtk.EventControllerKey();
widget.add_controller(key);
if (onKeyPressed) {
key.connect('key-pressed', (_, val, code, state) => onKeyPressed(widget, val, code, state));
}
if (onKeyReleased) {
key.connect('key-released', (_, val, code, state) =>
onKeyReleased(widget, val, code, state));
}
if (onKeyModifier) {
key.connect('modifiers', (_, state) => onKeyModifier(widget, state));
}
}
if (onLegacy || onButtonPressed || onButtonReleased) {
const legacy = new Gtk.EventControllerLegacy();
widget.add_controller(legacy);
legacy.connect('event', (_, event) => {
if (event.get_event_type() === Gdk.EventType.BUTTON_PRESS) {
onButtonPressed?.(widget, event as Gdk.ButtonEvent);
}
if (event.get_event_type() === Gdk.EventType.BUTTON_RELEASE) {
onButtonReleased?.(widget, event as Gdk.ButtonEvent);
}
onLegacy?.(widget, event);
});
}
if (onMotion || onHoverEnter || onHoverLeave) {
const hover = new Gtk.EventControllerMotion();
widget.add_controller(hover);
if (onHoverEnter) {
hover.connect('enter', (_, x, y) => onHoverEnter(widget, x, y));
}
if (onHoverLeave) {
hover.connect('leave', () => onHoverLeave(widget));
}
if (onMotion) {
hover.connect('motion', (_, x, y) => onMotion(widget, x, y));
}
}
if (onScroll || onScrollDecelerate) {
const scroll = new Gtk.EventControllerScroll();
widget.add_controller(scroll);
if (onScroll) {
scroll.connect('scroll', (_, x, y) => onScroll(widget, x, y));
}
if (onScrollDecelerate) {
scroll.connect('decelerate', (_, x, y) => onScrollDecelerate(widget, x, y));
}
}
return props;
};

View file

@ -0,0 +1,28 @@
import { Gtk } from 'astal/gtk4';
import { Binding } from 'astal/binding';
// A mixin class must have a constructor with a single rest parameter of type 'any[]'
// eslint-disable-next-line "@typescript-eslint/no-explicit-any"
export type MixinParams = any[];
export type BindableChild = Gtk.Widget | Binding<Gtk.Widget>;
export type BindableProps<T> = {
[K in keyof T]: Binding<T[K]> | T[K];
};
export const noImplicitDestroy = Symbol('no no implicit destroy');
export const setChildren = Symbol('children setter method');
export const childType = Symbol('child type');
export const dummyBuilder = new Gtk.Builder();
export type GenericWidget = InstanceType<typeof Gtk.Widget> & {
[setChildren]: (children: Gtk.Widget[]) => void
};
export interface AstalifyProps {
css: string
child: Gtk.Widget
children: Gtk.Widget[]
}

View file

@ -0,0 +1,5 @@
import astalify from './astalify';
export default astalify;
export { type AstalifyProps, type BindableProps, childType } from './generics';

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Astal, type ConstructProps, Gtk } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type BoxProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
type ButtonSignals = Record<`on${string}`, unknown[]> & {

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
type CalendarSignals = Record<`on${string}`, unknown[]> & {

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type CenterBoxProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
type EntrySignals = Record<`on${string}`, unknown[]> & {

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type ImageProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type LabelProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type LevelBarProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type MenuButtonProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type, type AstalifyProps } from './astalify';
import astalify, { childType, type AstalifyProps } from '../astalify';
export type OverlayProps = ConstructProps<
@ -29,8 +29,8 @@ export class OverlayClass extends astalify(Gtk.Overlay) {
setChildren(self: OverlayClass, children: Gtk.Widget[]) {
for (const child of children) {
const types = type in child ?
(child[type] as string).split(/\s+/) :
const types = childType in child ?
(child[childType] as string).split(/\s+/) :
[];
if (types.includes('overlay')) {

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type PopoverProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type RevealerProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Astal, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
type SliderSignals = Record<`on${string}`, unknown[]> & {

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type StackProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Gtk, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type SwitchProps = ConstructProps<

View file

@ -1,7 +1,7 @@
import { register } from 'astal';
import { Astal, type ConstructProps } from 'astal/gtk4';
import astalify, { type AstalifyProps } from './astalify';
import astalify, { type AstalifyProps } from '../astalify';
export type WindowProps = ConstructProps<