parent
8bd0b732e7
commit
557e4c7a52
32 changed files with 281 additions and 12 deletions
modules/ags/gtk4/widgets
179
modules/ags/gtk4/widgets/astalify/astalify.ts
Normal file
179
modules/ags/gtk4/widgets/astalify/astalify.ts
Normal file
|
@ -0,0 +1,179 @@
|
|||
import { property, register } from 'astal';
|
||||
import { Gtk, hook } from 'astal/gtk4';
|
||||
import { type Connectable, type Subscribable } from 'astal/binding';
|
||||
|
||||
import construct from './construct';
|
||||
import setupControllers from './controller';
|
||||
|
||||
import {
|
||||
type BindableProps,
|
||||
childType,
|
||||
type Cursor,
|
||||
dummyBuilder,
|
||||
type MixinParams,
|
||||
noImplicitDestroy,
|
||||
setChildren,
|
||||
} from './generics';
|
||||
|
||||
|
||||
export default <
|
||||
C extends new (...props: MixinParams) => Gtk.Widget,
|
||||
ConstructorProps,
|
||||
>(
|
||||
cls: C,
|
||||
clsName = cls.name,
|
||||
) => {
|
||||
@register({ GTypeName: `RealClass_${clsName}` })
|
||||
class Widget extends cls {
|
||||
declare private _css: string | undefined;
|
||||
declare private _provider: Gtk.CssProvider | undefined;
|
||||
|
||||
@property(String)
|
||||
get css(): string | undefined {
|
||||
return this._css;
|
||||
}
|
||||
|
||||
set css(value: string) {
|
||||
if (!this._provider) {
|
||||
this._provider = new Gtk.CssProvider();
|
||||
|
||||
this.get_style_context().add_provider(
|
||||
this._provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER,
|
||||
);
|
||||
}
|
||||
|
||||
this._css = value;
|
||||
this._provider.load_from_string(value);
|
||||
}
|
||||
|
||||
|
||||
declare private [childType]: string;
|
||||
|
||||
@property(String)
|
||||
get type(): string { return this[childType]; }
|
||||
|
||||
set type(value: string) { this[childType] = value; }
|
||||
|
||||
|
||||
@property(Object)
|
||||
get children(): Gtk.Widget[] { return this.getChildren(this); }
|
||||
|
||||
set children(value: Gtk.Widget[]) { this.setChildren(this, value); }
|
||||
|
||||
|
||||
declare private [noImplicitDestroy]: boolean;
|
||||
|
||||
@property(String)
|
||||
get noImplicitDestroy(): boolean { return this[noImplicitDestroy]; }
|
||||
|
||||
set noImplicitDestroy(value: boolean) { this[noImplicitDestroy] = value; }
|
||||
|
||||
|
||||
protected getChildren(widget: Gtk.Widget): Gtk.Widget[] {
|
||||
if ('get_child' in widget && typeof widget.get_child == 'function') {
|
||||
return widget.get_child() ? [widget.get_child()] : [];
|
||||
}
|
||||
|
||||
const children: Gtk.Widget[] = [];
|
||||
let ch = widget.get_first_child();
|
||||
|
||||
while (ch !== null) {
|
||||
children.push(ch);
|
||||
ch = ch.get_next_sibling();
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
protected setChildren(widget: Gtk.Widget, children: Gtk.Widget[]) {
|
||||
for (const child of children) {
|
||||
widget.vfunc_add_child(
|
||||
dummyBuilder,
|
||||
child,
|
||||
childType in widget ? widget[childType] as string : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[setChildren](children: Gtk.Widget[]) {
|
||||
for (const child of (this.getChildren(this))) {
|
||||
child?.unparent();
|
||||
|
||||
if (!children.includes(child) && noImplicitDestroy in this) {
|
||||
child.run_dispose();
|
||||
}
|
||||
}
|
||||
|
||||
this.setChildren(this, children);
|
||||
}
|
||||
|
||||
|
||||
private _cursorName: Cursor = 'default';
|
||||
|
||||
@property(String)
|
||||
get cursorName(): Cursor {
|
||||
return this._cursorName;
|
||||
}
|
||||
|
||||
set cursorName(val: Cursor) {
|
||||
this._cursorName = val;
|
||||
this.set_cursor_from_name(val);
|
||||
}
|
||||
|
||||
|
||||
hook(
|
||||
object: Connectable,
|
||||
signal: string,
|
||||
callback: (self: this, ...args: unknown[]) => void,
|
||||
): this;
|
||||
|
||||
hook(
|
||||
object: Subscribable,
|
||||
callback: (self: this, ...args: unknown[]) => void,
|
||||
): this;
|
||||
|
||||
hook(
|
||||
object: Connectable | Subscribable,
|
||||
signalOrCallback: string | ((self: this, ...args: unknown[]) => void),
|
||||
callback?: (self: this, ...args: unknown[]) => void,
|
||||
) {
|
||||
hook(this, object, signalOrCallback, callback);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
constructor(...params: MixinParams) {
|
||||
const props = params[0] || {};
|
||||
|
||||
super('cssName' in props ? { cssName: props.cssName } : {});
|
||||
|
||||
if ('cssName' in props) {
|
||||
delete props.cssName;
|
||||
}
|
||||
|
||||
if (props.noImplicitDestroy) {
|
||||
this.noImplicitDestroy = true;
|
||||
delete props.noImplicitDestroy;
|
||||
}
|
||||
|
||||
if (props.type) {
|
||||
this.type = props.type;
|
||||
delete props.type;
|
||||
}
|
||||
|
||||
construct(this, setupControllers(this, props));
|
||||
}
|
||||
}
|
||||
|
||||
type Constructor<Instance, Props> = new (...args: Props[]) => Instance;
|
||||
|
||||
type WidgetClass = Constructor<
|
||||
Widget & Gtk.Widget & InstanceType<C>,
|
||||
Partial<BindableProps<ConstructorProps>>
|
||||
>;
|
||||
|
||||
// override the parameters of the `super` constructor
|
||||
return Widget as unknown as WidgetClass;
|
||||
};
|
27
modules/ags/gtk4/widgets/astalify/bindings.ts
Normal file
27
modules/ags/gtk4/widgets/astalify/bindings.ts
Normal 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)();
|
||||
};
|
131
modules/ags/gtk4/widgets/astalify/construct.ts
Normal file
131
modules/ags/gtk4/widgets/astalify/construct.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
import { execAsync } from 'astal';
|
||||
import { type Gtk, type ConstructProps } from 'astal/gtk4';
|
||||
import { Binding, kebabify, snakeify } from 'astal/binding';
|
||||
|
||||
import { mergeBindings } from './bindings';
|
||||
import { type EventController } from './controller';
|
||||
import { type AstalifyProps, type BindableProps, type GenericWidget, setChildren } from './generics';
|
||||
|
||||
|
||||
export default <
|
||||
Self extends GenericWidget,
|
||||
Props extends Gtk.Widget.ConstructorProps,
|
||||
>(
|
||||
widget: Self,
|
||||
props: Omit<
|
||||
ConstructProps<Self, Props> & Partial<BindableProps<AstalifyProps>>,
|
||||
keyof EventController<Self>
|
||||
>,
|
||||
) => {
|
||||
type Key = keyof typeof props;
|
||||
const keys = Object.keys(props) as Key[];
|
||||
const entries = Object.entries(props) as [Key, unknown][];
|
||||
|
||||
const setProp = (prop: Key, value: Self[keyof Self]) => {
|
||||
try {
|
||||
const setter = `set_${snakeify(prop.toString())}` as keyof Self;
|
||||
|
||||
if (typeof widget[setter] === 'function') {
|
||||
return widget[setter](value);
|
||||
}
|
||||
|
||||
return (widget[prop as keyof Self] = value);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`could not set property "${prop.toString()}" on ${widget}:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
const children = props.children ?
|
||||
props.children instanceof Binding ?
|
||||
[props.children] as (Binding<Gtk.Widget[]> | Binding<Gtk.Widget> | Gtk.Widget)[] :
|
||||
props.children as Gtk.Widget[] :
|
||||
[];
|
||||
|
||||
if (props.child) {
|
||||
children.unshift(props.child);
|
||||
}
|
||||
|
||||
// remove undefined values
|
||||
for (const [key, value] of entries) {
|
||||
if (typeof value === 'undefined') {
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
|
||||
// collect bindings
|
||||
const bindings: [Key, Binding<unknown>][] = [];
|
||||
|
||||
for (const key of keys) {
|
||||
if (props[key] instanceof Binding) {
|
||||
bindings.push([key, props[key]]);
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
|
||||
// collect signal handlers
|
||||
const onHandlers: [string, string | (() => void)][] = [];
|
||||
|
||||
for (const key of keys) {
|
||||
if (key.toString().startsWith('on')) {
|
||||
const sig = kebabify(key.toString()).split('-').slice(1).join('-');
|
||||
|
||||
onHandlers.push([sig, props[key] as string | (() => void)]);
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
|
||||
// set children
|
||||
const mergedChildren = mergeBindings<Gtk.Widget>(children.flat(Infinity));
|
||||
|
||||
if (mergedChildren instanceof Binding) {
|
||||
widget[setChildren](mergedChildren.get());
|
||||
|
||||
widget.connect('destroy', mergedChildren.subscribe((v) => {
|
||||
widget[setChildren](v);
|
||||
}));
|
||||
}
|
||||
else if (mergedChildren.length > 0) {
|
||||
widget[setChildren](mergedChildren);
|
||||
}
|
||||
|
||||
// setup signal handlers
|
||||
for (const [signal, callback] of onHandlers) {
|
||||
const sig = signal.startsWith('notify') ?
|
||||
signal.replace('-', '::') :
|
||||
signal;
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
widget.connect(sig, callback);
|
||||
}
|
||||
else {
|
||||
widget.connect(sig, () => execAsync(callback)
|
||||
.then(print).catch(console.error));
|
||||
}
|
||||
}
|
||||
|
||||
// setup bindings handlers
|
||||
for (const [prop, binding] of bindings) {
|
||||
if (prop === 'child' || prop === 'children') {
|
||||
widget.connect('destroy', (binding as Binding<Gtk.Widget[]>).subscribe((v: Gtk.Widget[]) => {
|
||||
widget[setChildren](v);
|
||||
}));
|
||||
}
|
||||
widget.connect('destroy', binding.subscribe((v: unknown) => {
|
||||
setProp(prop, v as Self[keyof Self]);
|
||||
}));
|
||||
setProp(prop, binding.get() as Self[keyof Self]);
|
||||
}
|
||||
|
||||
// filter undefined values
|
||||
for (const [key, value] of entries) {
|
||||
if (typeof value === 'undefined') {
|
||||
delete props[key];
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(widget, props);
|
||||
props.setup?.(widget);
|
||||
|
||||
return widget;
|
||||
};
|
120
modules/ags/gtk4/widgets/astalify/controller.ts
Normal file
120
modules/ags/gtk4/widgets/astalify/controller.ts
Normal 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;
|
||||
};
|
91
modules/ags/gtk4/widgets/astalify/generics.ts
Normal file
91
modules/ags/gtk4/widgets/astalify/generics.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { Gtk } from 'astal/gtk4';
|
||||
import { Binding } from 'astal/binding';
|
||||
|
||||
import { EventController } from './controller';
|
||||
|
||||
// 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[]
|
||||
cursorName: Cursor
|
||||
}
|
||||
|
||||
type SigHandler<
|
||||
W extends InstanceType<typeof Gtk.Widget>,
|
||||
Args extends 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}`, unknown[]> = Record<`on${string}`, unknown[]>,
|
||||
> = Partial<{
|
||||
// @ts-expect-error can't assign to unknown, but it works as expected though
|
||||
[S in keyof Signals]: SigHandler<Self, Signals[S]>
|
||||
}> & Partial<Record<`on${string}`, SigHandler<Self, unknown[]>>> & Partial<BindableProps<Omit<
|
||||
Props,
|
||||
'cssName' | 'css_name' | 'cursor'
|
||||
>>> & {
|
||||
noImplicitDestroy?: true
|
||||
type?: string
|
||||
cssName?: string
|
||||
} & EventController<Self> & {
|
||||
onDestroy?: (self: Self) => unknown
|
||||
setup?: (self: Self) => void
|
||||
};
|
||||
|
||||
export 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';
|
10
modules/ags/gtk4/widgets/astalify/index.ts
Normal file
10
modules/ags/gtk4/widgets/astalify/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import astalify from './astalify';
|
||||
|
||||
export default astalify;
|
||||
|
||||
export {
|
||||
type AstalifyProps,
|
||||
type BindableProps,
|
||||
type ConstructProps,
|
||||
childType,
|
||||
} from './generics';
|
49
modules/ags/gtk4/widgets/bar.ts
Normal file
49
modules/ags/gtk4/widgets/bar.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { App, Astal, Gdk, Gtk } from 'astal/gtk4';
|
||||
import { Variable } from 'astal';
|
||||
|
||||
import Kompass from 'gi://Kompass';
|
||||
|
||||
import { Box, Calendar, CenterBox, Label, MenuButton, Popover, Window } from './subclasses';
|
||||
|
||||
const { EXCLUSIVE } = Astal.Exclusivity;
|
||||
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||
const { CENTER } = Gtk.Align;
|
||||
|
||||
const time = Variable(0);
|
||||
|
||||
setInterval(() => {
|
||||
time.set(time.get() + 1);
|
||||
}, 1000);
|
||||
|
||||
export default () => {
|
||||
const styledBox = Box({
|
||||
css: time().as((t) => `* { background: red; min-height: 10px; min-width: ${t}px; }`),
|
||||
});
|
||||
|
||||
return Window({
|
||||
visible: true,
|
||||
cssClasses: ['Bar'],
|
||||
exclusivity: EXCLUSIVE,
|
||||
anchor: TOP | LEFT | RIGHT,
|
||||
application: App,
|
||||
|
||||
child: CenterBox({
|
||||
startWidget: new Kompass.Tray({
|
||||
cursor: Gdk.Cursor.new_from_name('pointer', null),
|
||||
}),
|
||||
|
||||
centerWidget: styledBox,
|
||||
|
||||
endWidget: MenuButton({
|
||||
cursorName: 'pointer',
|
||||
hexpand: true,
|
||||
halign: CENTER,
|
||||
|
||||
children: [
|
||||
Label({ label: time().as(String) }),
|
||||
Popover({ child: Calendar() }),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
9
modules/ags/gtk4/widgets/lockscreen/_index.scss
Normal file
9
modules/ags/gtk4/widgets/lockscreen/_index.scss
Normal file
|
@ -0,0 +1,9 @@
|
|||
window,
|
||||
viewport {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
.lock-clock {
|
||||
font-size: 80pt;
|
||||
font-family: 'Ubuntu Mono';
|
||||
}
|
253
modules/ags/gtk4/widgets/lockscreen/index.ts
Normal file
253
modules/ags/gtk4/widgets/lockscreen/index.ts
Normal file
|
@ -0,0 +1,253 @@
|
|||
import { idle, timeout, Variable } from 'astal';
|
||||
import { App, Astal, Gdk, Gtk } from 'astal/gtk4';
|
||||
import { register } from 'astal/gobject';
|
||||
|
||||
import AstalAuth from 'gi://AstalAuth';
|
||||
import Gtk4SessionLock from 'gi://Gtk4SessionLock';
|
||||
|
||||
import { Box, BoxClass, Entry, Label, Window } from '../subclasses';
|
||||
import Separator from '../misc/separator';
|
||||
import { get_hyprland_monitor_desc } from '../../lib';
|
||||
|
||||
// This file is generated by Nix
|
||||
import Vars from './vars';
|
||||
|
||||
/* Types */
|
||||
declare global {
|
||||
function authFinger(): void;
|
||||
}
|
||||
@register()
|
||||
class BlurredBox extends BoxClass {
|
||||
geometry = {} as { w: number, h: number };
|
||||
}
|
||||
|
||||
|
||||
export default () => {
|
||||
const windows = new Map<Gdk.Monitor, Gtk.Window>();
|
||||
const blurBGs: BlurredBox[] = [];
|
||||
|
||||
const transition_duration = 1000;
|
||||
const WINDOW_MARGINS = -2;
|
||||
const ENTRY_SPACING = 20;
|
||||
const CLOCK_SPACING = 60;
|
||||
|
||||
const bgCSS = ({ w = 1, h = 1 } = {}) => `* {
|
||||
border: 2px solid rgba(189, 147, 249, 0.8);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
min-height: ${h}px;
|
||||
min-width: ${w}px;
|
||||
transition: min-height ${transition_duration / 2}ms,
|
||||
min-width ${transition_duration / 2}ms;
|
||||
}`;
|
||||
|
||||
const lock = Gtk4SessionLock.get_singleton();
|
||||
|
||||
const unlock = () => {
|
||||
blurBGs.forEach((b) => {
|
||||
b.css = bgCSS({
|
||||
w: b.geometry.w,
|
||||
h: 1,
|
||||
});
|
||||
|
||||
timeout(transition_duration / 2, () => {
|
||||
b.css = bgCSS({
|
||||
w: 1,
|
||||
h: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
timeout(transition_duration, () => {
|
||||
Gtk4SessionLock.unlock();
|
||||
Gdk.Display.get_default()?.sync();
|
||||
App.quit();
|
||||
});
|
||||
};
|
||||
|
||||
const Clock = () => {
|
||||
const time = Variable<string>('').poll(1000, () => {
|
||||
return (new Date().toLocaleString([], {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
hour12: true,
|
||||
}) ?? '')
|
||||
.replace('a.m.', 'AM')
|
||||
.replace('p.m.', 'PM');
|
||||
});
|
||||
|
||||
return Label({
|
||||
cssClasses: ['lock-clock'],
|
||||
label: time(),
|
||||
});
|
||||
};
|
||||
|
||||
const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => {
|
||||
const rev = new BlurredBox({ css: bgCSS() });
|
||||
|
||||
idle(() => {
|
||||
rev.geometry = {
|
||||
w: monitor.get_geometry().width,
|
||||
h: monitor.get_geometry().height,
|
||||
};
|
||||
|
||||
rev.css = bgCSS({
|
||||
w: rev.geometry.w,
|
||||
h: 1,
|
||||
});
|
||||
|
||||
timeout(transition_duration / 2, () => {
|
||||
rev.css = bgCSS({
|
||||
w: rev.geometry.w,
|
||||
h: rev.geometry.h,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
blurBGs.push(rev);
|
||||
|
||||
Window({
|
||||
name: `blur-bg-${monitor.get_model()}`,
|
||||
namespace: `blur-bg-${monitor.get_model()}`,
|
||||
gdkmonitor: monitor,
|
||||
layer: Astal.Layer.OVERLAY,
|
||||
visible: true,
|
||||
|
||||
anchor: Astal.WindowAnchor.TOP |
|
||||
Astal.WindowAnchor.LEFT |
|
||||
Astal.WindowAnchor.RIGHT |
|
||||
Astal.WindowAnchor.BOTTOM,
|
||||
|
||||
margin: WINDOW_MARGINS,
|
||||
exclusivity: Astal.Exclusivity.IGNORE,
|
||||
|
||||
child: Box({
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.CENTER,
|
||||
children: [rev],
|
||||
}),
|
||||
});
|
||||
|
||||
const label = Label({ label: 'Enter password:' });
|
||||
|
||||
return new Gtk.Window({
|
||||
child: visible ?
|
||||
Box({
|
||||
vertical: true,
|
||||
halign: Gtk.Align.CENTER,
|
||||
valign: Gtk.Align.CENTER,
|
||||
spacing: 16,
|
||||
|
||||
children: [
|
||||
Clock(),
|
||||
|
||||
Separator({
|
||||
size: CLOCK_SPACING,
|
||||
vertical: true,
|
||||
}),
|
||||
|
||||
Box({
|
||||
halign: Gtk.Align.CENTER,
|
||||
cssClasses: ['avatar'],
|
||||
}),
|
||||
|
||||
Box({
|
||||
cssClasses: ['entry-box'],
|
||||
vertical: true,
|
||||
|
||||
children: [
|
||||
label,
|
||||
|
||||
Separator({
|
||||
size: ENTRY_SPACING,
|
||||
vertical: true,
|
||||
}),
|
||||
|
||||
Entry({
|
||||
halign: Gtk.Align.CENTER,
|
||||
xalign: 0.5,
|
||||
visibility: false,
|
||||
placeholder_text: 'password',
|
||||
|
||||
onRealize: (self) => self.grab_focus(),
|
||||
|
||||
onActivate: (self) => {
|
||||
self.set_sensitive(false);
|
||||
|
||||
AstalAuth.Pam.authenticate(self.get_text() ?? '', (_, task) => {
|
||||
try {
|
||||
AstalAuth.Pam.authenticate_finish(task);
|
||||
unlock();
|
||||
}
|
||||
catch (e) {
|
||||
self.set_text('');
|
||||
label.set_label((e as Error).message);
|
||||
self.set_sensitive(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}) :
|
||||
Box(),
|
||||
});
|
||||
};
|
||||
|
||||
const createWindow = (monitor: Gdk.Monitor) => {
|
||||
const hyprDesc = get_hyprland_monitor_desc(monitor);
|
||||
const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen;
|
||||
const win = PasswordPrompt(monitor, entryVisible);
|
||||
|
||||
windows.set(monitor, win);
|
||||
};
|
||||
|
||||
const lock_screen = () => {
|
||||
const display = Gdk.Display.get_default();
|
||||
|
||||
for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) {
|
||||
const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor;
|
||||
|
||||
if (monitor) {
|
||||
createWindow(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
display?.get_monitors()?.connect('items-changed', () => {
|
||||
for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) {
|
||||
const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor;
|
||||
|
||||
if (monitor && !windows.has(monitor)) {
|
||||
createWindow(monitor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Gtk4SessionLock.lock();
|
||||
|
||||
windows.forEach((win, monitor) => {
|
||||
Gtk4SessionLock.assign_window_to_monitor(win, monitor);
|
||||
win.show();
|
||||
});
|
||||
};
|
||||
|
||||
const on_finished = () => {
|
||||
Gdk.Display.get_default()?.sync();
|
||||
App.quit();
|
||||
};
|
||||
|
||||
lock.connect('finished', on_finished);
|
||||
|
||||
if (Vars.hasFprintd) {
|
||||
globalThis.authFinger = () => AstalAuth.Pam.authenticate('', (_, task) => {
|
||||
try {
|
||||
AstalAuth.Pam.authenticate_finish(task);
|
||||
unlock();
|
||||
}
|
||||
catch (e) {
|
||||
console.error((e as Error).message);
|
||||
}
|
||||
});
|
||||
globalThis.authFinger();
|
||||
}
|
||||
lock_screen();
|
||||
};
|
111
modules/ags/gtk4/widgets/misc/popup-window.ts
Normal file
111
modules/ags/gtk4/widgets/misc/popup-window.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { App, Astal, Gtk } from 'astal/gtk4';
|
||||
import { property, register } from 'astal/gobject';
|
||||
import { Binding, idle } from 'astal';
|
||||
|
||||
import { WindowClass, WindowProps } from '../subclasses';
|
||||
import { get_hyprland_monitor, hyprMessage } from '../../lib';
|
||||
|
||||
/* Types */
|
||||
type CloseType = 'none' | 'stay' | 'released' | 'clicked';
|
||||
type HyprTransition = 'slide' | 'slide top' | 'slide bottom' | 'slide left' |
|
||||
'slide right' | 'popin' | 'fade';
|
||||
type PopupCallback = (self?: WindowClass) => void;
|
||||
|
||||
export type PopupWindowProps = WindowProps & {
|
||||
transition?: HyprTransition | Binding<HyprTransition>
|
||||
close_on_unfocus?: CloseType | Binding<CloseType>
|
||||
on_open?: PopupCallback
|
||||
on_close?: PopupCallback
|
||||
};
|
||||
|
||||
|
||||
@register()
|
||||
export class PopupWindow extends WindowClass {
|
||||
@property(String)
|
||||
declare transition: HyprTransition | Binding<HyprTransition>;
|
||||
|
||||
@property(String)
|
||||
declare close_on_unfocus: CloseType | Binding<CloseType>;
|
||||
|
||||
on_open: PopupCallback;
|
||||
on_close: PopupCallback;
|
||||
|
||||
constructor({
|
||||
transition = 'slide top',
|
||||
close_on_unfocus = 'released',
|
||||
on_open = () => { /**/ },
|
||||
on_close = () => { /**/ },
|
||||
|
||||
name,
|
||||
visible = false,
|
||||
layer = Astal.Layer.OVERLAY,
|
||||
...rest
|
||||
}: PopupWindowProps) {
|
||||
super({
|
||||
...rest,
|
||||
name: `win-${name}`,
|
||||
namespace: `win-${name}`,
|
||||
visible: false,
|
||||
layer,
|
||||
setup: () => idle(() => {
|
||||
// Add way to make window open on startup
|
||||
if (visible) {
|
||||
this.visible = true;
|
||||
}
|
||||
}),
|
||||
} as WindowProps);
|
||||
|
||||
App.add_window(this);
|
||||
|
||||
const setTransition = (_: PopupWindow, t: HyprTransition | Binding<HyprTransition>) => {
|
||||
hyprMessage(`keyword layerrule animation ${t}, ${this.name}`).catch(console.log);
|
||||
};
|
||||
|
||||
this.connect('notify::transition', setTransition);
|
||||
|
||||
this.close_on_unfocus = close_on_unfocus;
|
||||
this.transition = transition;
|
||||
this.on_open = on_open;
|
||||
this.on_close = on_close;
|
||||
|
||||
this.connect('notify::visible', () => {
|
||||
// Make sure we have the right animation
|
||||
setTransition(this, this.transition);
|
||||
|
||||
if (this.visible) {
|
||||
this.on_open(this);
|
||||
}
|
||||
else {
|
||||
this.on_close(this);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async set_x_pos(
|
||||
alloc: Gtk.Allocation,
|
||||
side = 'right' as 'left' | 'right',
|
||||
) {
|
||||
const monitor = this.gdkmonitor ?? this.get_current_monitor();
|
||||
|
||||
const transform = get_hyprland_monitor(monitor)?.get_transform();
|
||||
|
||||
let width: number;
|
||||
|
||||
if (transform && (transform === 1 || transform === 3)) {
|
||||
width = monitor.get_geometry().height;
|
||||
}
|
||||
else {
|
||||
width = monitor.get_geometry().width;
|
||||
}
|
||||
|
||||
this.margin_right = side === 'right' ?
|
||||
(width - alloc.x - alloc.width) :
|
||||
this.margin_right;
|
||||
|
||||
this.margin_left = side === 'right' ?
|
||||
this.margin_left :
|
||||
(alloc.x - alloc.width);
|
||||
}
|
||||
}
|
||||
|
||||
export default PopupWindow;
|
12
modules/ags/gtk4/widgets/misc/separator.ts
Normal file
12
modules/ags/gtk4/widgets/misc/separator.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Box, BoxProps } from '../subclasses';
|
||||
|
||||
|
||||
export default ({
|
||||
size,
|
||||
vertical = false,
|
||||
css = '',
|
||||
...rest
|
||||
}: { size: number } & BoxProps) => Box({
|
||||
css: `* { ${vertical ? 'min-height' : 'min-width'}: ${size}px; ${css} }`,
|
||||
...rest,
|
||||
} as BoxProps);
|
27
modules/ags/gtk4/widgets/subclasses/box.ts
Normal file
27
modules/ags/gtk4/widgets/subclasses/box.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { register } from 'astal';
|
||||
import { Astal, Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type BoxProps = ConstructProps<
|
||||
BoxClass,
|
||||
Astal.Box.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Box' })
|
||||
export class BoxClass extends astalify(Astal.Box) {
|
||||
constructor({ cssName = 'box', ...props }: BoxProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren(self: BoxClass) {
|
||||
return self.get_children();
|
||||
}
|
||||
|
||||
setChildren(self: BoxClass, children: Gtk.Widget[]) {
|
||||
return self.set_children(children);
|
||||
}
|
||||
}
|
||||
|
||||
export const Box = (props?: BoxProps) => new BoxClass(props);
|
23
modules/ags/gtk4/widgets/subclasses/button.ts
Normal file
23
modules/ags/gtk4/widgets/subclasses/button.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
type ButtonSignals = Record<`on${string}`, unknown[]> & {
|
||||
onClicked: []
|
||||
};
|
||||
export type ButtonProps = ConstructProps<
|
||||
ButtonClass,
|
||||
Gtk.Button.ConstructorProps & AstalifyProps,
|
||||
ButtonSignals
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Button' })
|
||||
export class ButtonClass extends astalify(Gtk.Button) {
|
||||
constructor({ cssName = 'button', ...props }: ButtonProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
}
|
||||
|
||||
export const Button = (props?: ButtonProps) => new ButtonClass(props);
|
28
modules/ags/gtk4/widgets/subclasses/calendar.ts
Normal file
28
modules/ags/gtk4/widgets/subclasses/calendar.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
type CalendarSignals = Record<`on${string}`, unknown[]> & {
|
||||
onDaySelected: []
|
||||
onNextMonth: []
|
||||
onNextYear: []
|
||||
onPrevMonth: []
|
||||
onPrevYear: []
|
||||
|
||||
};
|
||||
export type CalendarProps = ConstructProps<
|
||||
CalendarClass,
|
||||
Gtk.Calendar.ConstructorProps & AstalifyProps,
|
||||
CalendarSignals
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Calendar' })
|
||||
export class CalendarClass extends astalify(Gtk.Calendar) {
|
||||
constructor({ cssName = 'calendar', ...props }: CalendarProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
}
|
||||
|
||||
export const Calendar = (props?: CalendarProps) => new CalendarClass(props);
|
33
modules/ags/gtk4/widgets/subclasses/centerbox.ts
Normal file
33
modules/ags/gtk4/widgets/subclasses/centerbox.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type CenterBoxProps = ConstructProps<
|
||||
CenterBoxClass,
|
||||
Gtk.CenterBox.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'CenterBox' })
|
||||
export class CenterBoxClass extends astalify(Gtk.CenterBox) {
|
||||
constructor({ cssName = 'centerbox', ...props }: CenterBoxProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren(box: CenterBoxClass) {
|
||||
return [box.startWidget, box.centerWidget, box.endWidget];
|
||||
}
|
||||
|
||||
setChildren(box: CenterBoxClass, children: (Gtk.Widget | null)[]) {
|
||||
if (children.length > 3) {
|
||||
throw new Error('Cannot have more than 3 children in a CenterBox');
|
||||
}
|
||||
|
||||
box.startWidget = children[0] || new Gtk.Box();
|
||||
box.centerWidget = children[1] || new Gtk.Box();
|
||||
box.endWidget = children[2] || new Gtk.Box();
|
||||
}
|
||||
}
|
||||
|
||||
export const CenterBox = (props?: CenterBoxProps) => new CenterBoxClass(props);
|
26
modules/ags/gtk4/widgets/subclasses/entry.ts
Normal file
26
modules/ags/gtk4/widgets/subclasses/entry.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
type EntrySignals = Record<`on${string}`, unknown[]> & {
|
||||
onActivate: []
|
||||
onNotifyText: []
|
||||
};
|
||||
export type EntryProps = ConstructProps<
|
||||
EntryClass,
|
||||
Gtk.Entry.ConstructorProps & AstalifyProps,
|
||||
EntrySignals
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Entry' })
|
||||
export class EntryClass extends astalify(Gtk.Entry) {
|
||||
constructor({ cssName = 'entry', ...props }: EntryProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const Entry = (props?: EntryProps) => new EntryClass(props);
|
21
modules/ags/gtk4/widgets/subclasses/image.ts
Normal file
21
modules/ags/gtk4/widgets/subclasses/image.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type ImageProps = ConstructProps<
|
||||
ImageClass,
|
||||
Gtk.Image.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Image' })
|
||||
export class ImageClass extends astalify(Gtk.Image) {
|
||||
constructor({ cssName = 'image', ...props }: ImageProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const Image = (props?: ImageProps) => new ImageClass(props);
|
16
modules/ags/gtk4/widgets/subclasses/index.ts
Normal file
16
modules/ags/gtk4/widgets/subclasses/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export * from './box';
|
||||
export * from './button';
|
||||
export * from './calendar';
|
||||
export * from './centerbox';
|
||||
export * from './entry';
|
||||
export * from './image';
|
||||
export * from './label';
|
||||
export * from './levelbar';
|
||||
export * from './menubutton';
|
||||
export * from './overlay';
|
||||
export * from './popover';
|
||||
export * from './revealer';
|
||||
export * from './slider';
|
||||
export * from './stack';
|
||||
export * from './switch';
|
||||
export * from './window';
|
21
modules/ags/gtk4/widgets/subclasses/label.ts
Normal file
21
modules/ags/gtk4/widgets/subclasses/label.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type LabelProps = ConstructProps<
|
||||
LabelClass,
|
||||
Gtk.Label.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Label' })
|
||||
export class LabelClass extends astalify(Gtk.Label) {
|
||||
constructor({ cssName = 'label', ...props }: LabelProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const Label = (props?: LabelProps) => new LabelClass(props);
|
21
modules/ags/gtk4/widgets/subclasses/levelbar.ts
Normal file
21
modules/ags/gtk4/widgets/subclasses/levelbar.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type LevelBarProps = ConstructProps<
|
||||
LevelBarClass,
|
||||
Gtk.LevelBar.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'LevelBar' })
|
||||
export class LevelBarClass extends astalify(Gtk.LevelBar) {
|
||||
constructor({ cssName = 'levelbar', ...props }: LevelBarProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const LevelBar = (props?: LevelBarProps) => new LevelBarClass(props);
|
34
modules/ags/gtk4/widgets/subclasses/menubutton.ts
Normal file
34
modules/ags/gtk4/widgets/subclasses/menubutton.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type MenuButtonProps = ConstructProps<
|
||||
MenuButtonClass,
|
||||
Gtk.MenuButton.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'MenuButton' })
|
||||
export class MenuButtonClass extends astalify(Gtk.MenuButton) {
|
||||
constructor({ cssName = 'menubutton', ...props }: MenuButtonProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren(self: MenuButtonClass) {
|
||||
return [self.popover, self.child];
|
||||
}
|
||||
|
||||
setChildren(self: MenuButtonClass, children: Gtk.Widget[]) {
|
||||
for (const child of children) {
|
||||
if (child instanceof Gtk.Popover) {
|
||||
self.set_popover(child);
|
||||
}
|
||||
else {
|
||||
self.set_child(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuButton = (props?: MenuButtonProps) => new MenuButtonClass(props);
|
49
modules/ags/gtk4/widgets/subclasses/overlay.ts
Normal file
49
modules/ags/gtk4/widgets/subclasses/overlay.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { childType, type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type OverlayProps = ConstructProps<
|
||||
OverlayClass,
|
||||
Gtk.Overlay.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Overlay' })
|
||||
export class OverlayClass extends astalify(Gtk.Overlay) {
|
||||
constructor({ cssName = 'overlay', ...props }: OverlayProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren(self: OverlayClass) {
|
||||
const children: Gtk.Widget[] = [];
|
||||
let ch = self.get_first_child();
|
||||
|
||||
while (ch !== null) {
|
||||
children.push(ch);
|
||||
ch = ch.get_next_sibling();
|
||||
}
|
||||
|
||||
return children.filter((child) => child !== self.child);
|
||||
}
|
||||
|
||||
setChildren(self: OverlayClass, children: Gtk.Widget[]) {
|
||||
for (const child of children) {
|
||||
const types = childType in child ?
|
||||
(child[childType] as string).split(/\s+/) :
|
||||
[];
|
||||
|
||||
if (types.includes('overlay')) {
|
||||
self.add_overlay(child);
|
||||
}
|
||||
else {
|
||||
self.set_child(child);
|
||||
}
|
||||
|
||||
self.set_measure_overlay(child, types.includes('measure'));
|
||||
self.set_clip_overlay(child, types.includes('clip'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Overlay = (props?: OverlayProps) => new OverlayClass(props);
|
19
modules/ags/gtk4/widgets/subclasses/popover.ts
Normal file
19
modules/ags/gtk4/widgets/subclasses/popover.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type PopoverProps = ConstructProps<
|
||||
PopoverClass,
|
||||
Gtk.Popover.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Popover' })
|
||||
export class PopoverClass extends astalify(Gtk.Popover) {
|
||||
constructor({ cssName = 'popover', ...props }: PopoverProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
}
|
||||
|
||||
export const Popover = (props?: PopoverProps) => new PopoverClass(props);
|
19
modules/ags/gtk4/widgets/subclasses/revealer.ts
Normal file
19
modules/ags/gtk4/widgets/subclasses/revealer.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type RevealerProps = ConstructProps<
|
||||
RevealerClass,
|
||||
Gtk.Revealer.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Revealer' })
|
||||
export class RevealerClass extends astalify(Gtk.Revealer) {
|
||||
constructor({ cssName = 'revealer', ...props }: RevealerProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
}
|
||||
|
||||
export const Revealer = (props?: RevealerProps) => new RevealerClass(props);
|
25
modules/ags/gtk4/widgets/subclasses/slider.ts
Normal file
25
modules/ags/gtk4/widgets/subclasses/slider.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { register } from 'astal';
|
||||
import { Astal } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
type SliderSignals = Record<`on${string}`, unknown[]> & {
|
||||
onClicked: []
|
||||
};
|
||||
export type SliderProps = ConstructProps<
|
||||
SliderClass,
|
||||
Astal.Slider.ConstructorProps & AstalifyProps,
|
||||
SliderSignals
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Slider' })
|
||||
export class SliderClass extends astalify(Astal.Slider) {
|
||||
constructor({ cssName = 'slider', ...props }: SliderProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const Slider = (props?: SliderProps) => new SliderClass(props);
|
30
modules/ags/gtk4/widgets/subclasses/stack.ts
Normal file
30
modules/ags/gtk4/widgets/subclasses/stack.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type StackProps = ConstructProps<
|
||||
StackClass,
|
||||
Gtk.Stack.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Stack' })
|
||||
export class StackClass extends astalify(Gtk.Stack) {
|
||||
constructor({ cssName = 'stack', ...props }: StackProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
setChildren(self: StackClass, children: Gtk.Widget[]) {
|
||||
for (const child of children) {
|
||||
if (child.name !== '' && child.name !== null) {
|
||||
self.add_named(child, child.name);
|
||||
}
|
||||
else {
|
||||
self.add_child(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Stack = (props?: StackProps) => new StackClass(props);
|
21
modules/ags/gtk4/widgets/subclasses/switch.ts
Normal file
21
modules/ags/gtk4/widgets/subclasses/switch.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { register } from 'astal';
|
||||
import { Gtk } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type SwitchProps = ConstructProps<
|
||||
SwitchClass,
|
||||
Gtk.Switch.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Switch' })
|
||||
export class SwitchClass extends astalify(Gtk.Switch) {
|
||||
constructor({ cssName = 'switch', ...props }: SwitchProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
|
||||
getChildren() { return []; }
|
||||
}
|
||||
|
||||
export const Switch = (props?: SwitchProps) => new SwitchClass(props);
|
19
modules/ags/gtk4/widgets/subclasses/window.ts
Normal file
19
modules/ags/gtk4/widgets/subclasses/window.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { register } from 'astal';
|
||||
import { Astal } from 'astal/gtk4';
|
||||
|
||||
import astalify, { type AstalifyProps, type ConstructProps } from '../astalify';
|
||||
|
||||
|
||||
export type WindowProps = ConstructProps<
|
||||
WindowClass,
|
||||
Astal.Window.ConstructorProps & AstalifyProps
|
||||
>;
|
||||
|
||||
@register({ GTypeName: 'Window' })
|
||||
export class WindowClass extends astalify(Astal.Window) {
|
||||
constructor({ cssName = 'window', ...props }: WindowProps = {}) {
|
||||
super({ cssName, ...props });
|
||||
}
|
||||
}
|
||||
|
||||
export const Window = (props?: WindowProps) => new WindowClass(props);
|
Loading…
Add table
Add a link
Reference in a new issue