diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-01-14 00:22:59 +1100 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-01-14 00:22:59 +1100 |
| commit | e923d39cdeed4b42e747afc01b3420d6d89af6e6 (patch) | |
| tree | a6e1c4fd3af32d2c8bcaf9d4f49226ced4c9c8c0 /utils | |
| parent | less border (diff) | |
| download | caelestia-shell-e923d39cdeed4b42e747afc01b3420d6d89af6e6.tar.gz caelestia-shell-e923d39cdeed4b42e747afc01b3420d6d89af6e6.tar.bz2 caelestia-shell-e923d39cdeed4b42e747afc01b3420d6d89af6e6.zip | |
app launcher
Diffstat (limited to 'utils')
| -rw-r--r-- | utils/icons.ts | 9 | ||||
| -rw-r--r-- | utils/system.ts | 11 | ||||
| -rw-r--r-- | utils/widgets.tsx | 113 |
3 files changed, 127 insertions, 6 deletions
diff --git a/utils/icons.ts b/utils/icons.ts index dff47f3..f12aee0 100644 --- a/utils/icons.ts +++ b/utils/icons.ts @@ -1,4 +1,5 @@ import { Gio } from "astal"; +import type AstalApps from "gi://AstalApps"; import { Apps } from "../services/apps"; // Code points from https://www.github.com/lukas-w/font-logos @@ -73,10 +74,12 @@ const categoryIcons: Record<string, string> = { System: "host", }; -export const getAppCategoryIcon = (name: string) => { +export const getAppCategoryIcon = (nameOrApp: string | AstalApps.Application) => { const categories = - Gio.DesktopAppInfo.new(`${name}.desktop`)?.get_categories()?.split(";") ?? - Apps.fuzzy_query(name)[0]?.categories; + typeof nameOrApp === "string" + ? Gio.DesktopAppInfo.new(`${nameOrApp}.desktop`)?.get_categories()?.split(";") ?? + Apps.fuzzy_query(nameOrApp)[0]?.categories + : nameOrApp.categories; if (categories) for (const [key, value] of Object.entries(categoryIcons)) if (categories.includes(key)) return value; return "terminal"; diff --git a/utils/system.ts b/utils/system.ts index 9a328d5..99e9d7c 100644 --- a/utils/system.ts +++ b/utils/system.ts @@ -1,4 +1,5 @@ -import { exec, GLib } from "astal"; +import { exec, execAsync, GLib } from "astal"; +import type AstalApps from "gi://AstalApps"; import { osIcons } from "./icons"; export const inPath = (bin: string) => { @@ -10,6 +11,14 @@ export const inPath = (bin: string) => { return true; }; +export const launch = (app: AstalApps.Application) => { + execAsync(["uwsm", "app", "--", app.entry]).catch(() => { + app.frequency--; // Decrement frequency cause launch also increments it + app.launch(); + }); + app.frequency++; +}; + export const osId = GLib.get_os_info("ID") ?? "unknown"; export const osIdLike = GLib.get_os_info("ID_LIKE"); export const osIcon = String.fromCodePoint( 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(); + } +} |