From e923d39cdeed4b42e747afc01b3420d6d89af6e6 Mon Sep 17 00:00:00 2001
From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>
Date: Tue, 14 Jan 2025 00:22:59 +1100
Subject: app launcher

---
 utils/widgets.tsx | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 111 insertions(+), 2 deletions(-)

(limited to 'utils/widgets.tsx')

diff --git a/utils/widgets.tsx b/utils/widgets.tsx
index 7c40184..a0a96cb 100644
--- a/utils/widgets.tsx
+++ b/utils/widgets.tsx
@@ -1,5 +1,5 @@
-import { Binding } from "astal";
-import { Astal, type Widget } from "astal/gtk3";
+import { Binding, property, register, timeout } from "astal";
+import { App, Astal, Gdk, Gtk, Widget } from "astal/gtk3";
 import AstalHyprland from "gi://AstalHyprland";
 
 export const setupCustomTooltip = (self: any, text: string | Binding<string>) => {
@@ -46,3 +46,112 @@ export const setupCustomTooltip = (self: any, text: string | Binding<string>) =>
 
 export const setupChildClickthrough = (self: any) =>
     self.connect("size-allocate", () => self.get_window()?.set_child_input_shapes());
+
+export enum TransitionType {
+    FADE = "",
+    SLIDE_DOWN = "margin-top: -${height}px; margin-bottom: ${height}px;",
+    SLIDE_UP = "margin-top: ${height}px; margin-bottom: -${height}px;",
+    SLIDE_RIGHT = "margin-left: -${width}px; margin-right: ${width}px;",
+    SLIDE_LEFT = "margin-left: ${width}px; margin-right: -${width}px;",
+}
+
+@register()
+export class PopupWindow extends Widget.Window {
+    readonly transitionType: TransitionType;
+    readonly transitionDuration: number;
+    readonly transitionAmount: number;
+
+    readonly #content: Widget.Box;
+    #visible: boolean = false;
+
+    @property(Boolean)
+    get realVisible() {
+        return this.#visible;
+    }
+
+    set realVisible(v: boolean) {
+        if (v) this.show();
+        else this.hide();
+    }
+
+    constructor(
+        props: Widget.WindowProps & {
+            transitionType?: TransitionType;
+            transitionDuration?: number;
+            transitionAmount?: number;
+        }
+    ) {
+        const {
+            onKeyPressEvent,
+            clickThrough,
+            child,
+            halign = Gtk.Align.START,
+            valign = Gtk.Align.START,
+            transitionType = TransitionType.FADE,
+            transitionDuration = 200,
+            transitionAmount = 0.2,
+            ...sProps
+        } = props;
+
+        sProps.visible = false;
+        sProps.application = App;
+        sProps.namespace = `caelestia-${props.name}`;
+        sProps.anchor =
+            Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.RIGHT;
+        sProps.onKeyPressEvent = (self, event) => {
+            // Close window on escape
+            if (event.get_keyval()[1] === Gdk.KEY_Escape) self.hide();
+
+            return onKeyPressEvent?.(self, event);
+        };
+        super(sProps);
+
+        this.transitionType = transitionType;
+        this.transitionDuration = transitionDuration;
+        this.transitionAmount = transitionAmount;
+
+        // Wrapper box for animations
+        this.#content = (
+            <box halign={halign} valign={valign} className={`${props.name}-wrapper`}>
+                {clickThrough ? <eventbox>{child}</eventbox> : child}
+            </box>
+        ) as Widget.Box;
+        this.#content.css = this.#getTransitionCss(false);
+        this.add(this.#content);
+
+        if (clickThrough) setupChildClickthrough(this);
+    }
+
+    #getTransitionCss(visible: boolean) {
+        return (
+            `transition-duration: ${this.transitionDuration}ms;` +
+            (visible
+                ? "opacity: 1;" + this.transitionType.replaceAll("${width}", "0").replaceAll("${height}", "0")
+                : "opacity: 0;" +
+                  this.transitionType
+                      .replaceAll("${width}", String(this.#content.get_preferred_width()[1] * this.transitionAmount))
+                      .replaceAll("${height}", String(this.#content.get_preferred_height()[1] * this.transitionAmount)))
+        );
+    }
+
+    show() {
+        this.#visible = true;
+        this.notify("real-visible");
+
+        super.show();
+        this.#content.css = this.#getTransitionCss(true);
+    }
+
+    hide() {
+        this.#visible = false;
+        this.notify("real-visible");
+
+        this.#content.css = this.#getTransitionCss(false);
+        timeout(this.transitionDuration, () => !this.#visible && super.hide());
+    }
+
+    toggle() {
+        if (this.#visible) this.hide();
+        else this.show();
+    }
+}
-- 
cgit v1.2.3-freya

