From dcd914dbae039560a4ad4db4edd0264845f7024b Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:22:22 +1100 Subject: better popup window Not manual using css, instead use a combination of css opacity and Hyprland animations --- utils/widgets.tsx | 145 +++++++++++------------------------------------------- 1 file changed, 28 insertions(+), 117 deletions(-) (limited to 'utils') diff --git a/utils/widgets.tsx b/utils/widgets.tsx index 033a4e2..1065fae 100644 --- a/utils/widgets.tsx +++ b/utils/widgets.tsx @@ -1,5 +1,5 @@ -import { Binding, property, register, timeout } from "astal"; -import { App, Astal, Gdk, Gtk, Widget } from "astal/gtk3"; +import { Binding } from "astal"; +import { App, Astal, Gdk, Widget } from "astal/gtk3"; import AstalHyprland from "gi://AstalHyprland"; export const setupCustomTooltip = (self: any, text: string | Binding) => { @@ -47,118 +47,29 @@ export const setupCustomTooltip = (self: any, text: string | Binding) => 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 transitionInDuration: number; - readonly transitionOutDuration: 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; - transitionInDuration?: number; - transitionOutDuration?: number; - transitionAmount?: number; - } - ) { - const { - clickThrough, - child, - halign = Gtk.Align.START, - valign = Gtk.Align.START, - transitionType = TransitionType.FADE, - transitionInDuration = 300, - transitionOutDuration = 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.exclusivity = Astal.Exclusivity.IGNORE; - if (!sProps.keymode) sProps.keymode = Astal.Keymode.ON_DEMAND; - sProps.onKeyPressEvent = (self, event) => { - // Close window on escape - if (event.get_keyval()[1] === Gdk.KEY_Escape) self.hide(); - - return props.onKeyPressEvent?.(self, event); - }; - super(sProps); - - this.transitionType = transitionType; - this.transitionInDuration = transitionInDuration; - this.transitionOutDuration = transitionOutDuration; - this.transitionAmount = transitionAmount; - - // Wrapper box for animations - this.#content = ( - - {clickThrough ? {child} : child} - - ) as Widget.Box; - this.#content.css = this.#getTransitionCss(false); - this.add(this.#content); - - if (clickThrough) setupChildClickthrough(this); - } - - #getTransitionCss(visible: boolean) { - return ( - `transition-duration: ${visible ? this.transitionInDuration : this.transitionOutDuration}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.toggleClassName("visible", true); - this.#content.css = this.#getTransitionCss(true); - } - - hide() { - this.#visible = false; - this.notify("real-visible"); - - this.#content.toggleClassName("visible", false); - this.#content.css = this.#getTransitionCss(false); - timeout(this.transitionOutDuration, () => !this.#visible && super.hide()); - } - - toggle() { - if (this.#visible) this.hide(); - else this.show(); - } -} +const overrideProp = ( + prop: T | Binding | undefined, + override: (prop: T | undefined) => T | undefined +) => prop && (prop instanceof Binding ? prop.as(override) : override(prop)); + +export const convertPopupWindowProps = (props: Widget.WindowProps): Widget.WindowProps => ({ + keymode: Astal.Keymode.ON_DEMAND, + exclusivity: Astal.Exclusivity.IGNORE, + ...props, + visible: false, + application: App, + namespace: overrideProp(props.name, n => `caelestia-${n}`), + className: overrideProp(props.className, c => `popup ${c}`), + onKeyPressEvent: (self, event) => { + // Close window on escape + if (event.get_keyval()[1] === Gdk.KEY_Escape) self.hide(); + + return props.onKeyPressEvent?.(self, event); + }, + setup: self => { + self.connect("notify::visible", () => self.toggleClassName("visible", self.visible)); + props.setup?.(self); + }, +}); + +export const PopupWindow = (props: Widget.WindowProps) => ; -- cgit v1.2.3-freya