import { Binding, register } from "astal"; import { App, Astal, Gdk, Widget } from "astal/gtk3"; import AstalHyprland from "gi://AstalHyprland?version=0.1"; const extendProp = ( prop: T | Binding | undefined, override: (prop: T | undefined) => T | undefined ) => prop && (prop instanceof Binding ? prop.as(override) : override(prop)); @register() export default class PopupWindow extends Widget.Window { constructor(props: Widget.WindowProps) { super({ keymode: Astal.Keymode.ON_DEMAND, exclusivity: Astal.Exclusivity.IGNORE, borderWidth: 20, // To allow shadow, cause if not it gets cut off ...props, visible: false, application: App, name: props.monitor ? extendProp(props.name, n => (n ? n + props.monitor : undefined)) : props.name, namespace: extendProp(props.name, n => `caelestia-${n}`), onKeyPressEvent: (self, event) => { // Close window on escape if (event.get_keyval()[1] === Gdk.KEY_Escape) self.hide(); return props.onKeyPressEvent?.(self, event); }, }); } popup_at_widget(widget: JSX.Element, event: Gdk.Event | Astal.ClickEvent) { const { width, height } = widget.get_allocation(); const [, x, y] = event instanceof Gdk.Event ? event.get_coords() : [null, event.x, event.y]; const { x: cx, y: cy } = AstalHyprland.get_default().get_cursor_position(); this.anchor = Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT; this.marginLeft = cx + ((width - this.get_preferred_width()[1]) / 2 - x); this.marginTop = cy + (height - y); this.show(); } }