diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-04-26 22:36:23 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-04-26 22:36:23 +1000 |
| commit | 3c579d0e275cdaf6f2c9589abade94bde7905c82 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/modules/launcher | |
| parent | schemes: fix (diff) | |
| download | caelestia-shell-3c579d0e275cdaf6f2c9589abade94bde7905c82.tar.gz caelestia-shell-3c579d0e275cdaf6f2c9589abade94bde7905c82.tar.bz2 caelestia-shell-3c579d0e275cdaf6f2c9589abade94bde7905c82.zip | |
clean
Remove everything
Diffstat (limited to 'src/modules/launcher')
| -rw-r--r-- | src/modules/launcher/actions.tsx | 522 | ||||
| -rw-r--r-- | src/modules/launcher/index.tsx | 144 | ||||
| -rw-r--r-- | src/modules/launcher/modes.tsx | 225 | ||||
| -rw-r--r-- | src/modules/launcher/util.tsx | 19 |
4 files changed, 0 insertions, 910 deletions
diff --git a/src/modules/launcher/actions.tsx b/src/modules/launcher/actions.tsx deleted file mode 100644 index 40d37b5..0000000 --- a/src/modules/launcher/actions.tsx +++ /dev/null @@ -1,522 +0,0 @@ -import { Apps } from "@/services/apps"; -import Palette from "@/services/palette"; -import Schemes, { type Colours } from "@/services/schemes"; -import Wallpapers, { type ICategory, type IWallpaper } from "@/services/wallpapers"; -import { basename } from "@/utils/strings"; -import { notify } from "@/utils/system"; -import { setupCustomTooltip, type FlowBox } from "@/utils/widgets"; -import { bind, execAsync, GLib, readFile, register, type Variable } from "astal"; -import { Gtk, Widget } from "astal/gtk3"; -import { launcher as config } from "config"; -import { setConfig } from "config/funcs"; -import fuzzysort from "fuzzysort"; -import AstalHyprland from "gi://AstalHyprland"; -import { close, ContentBox, type LauncherContent, type Mode } from "./util"; - -interface IAction { - icon: string; - name: string; - description: string; - action: (...args: string[]) => void; - available?: () => boolean; -} - -interface ActionMap { - [k: string]: IAction; -} - -const variantActions = { - vibrant: { - icon: "sentiment_very_dissatisfied", - name: "Vibrant", - description: "A high chroma palette. The primary palette's chroma is at maximum.", - }, - tonalspot: { - icon: "android", - name: "Tonal Spot", - description: "Default for Material theme colours. A pastel palette with a low chroma.", - }, - expressive: { - icon: "compare_arrows", - name: "Expressive", - description: - "A medium chroma palette. The primary palette's hue is different from the seed colour, for variety.", - }, - fidelity: { - icon: "compare", - name: "Fidelity", - description: "Matches the seed colour, even if the seed colour is very bright (high chroma).", - }, - content: { - icon: "sentiment_calm", - name: "Content", - description: "Almost identical to fidelity.", - }, - fruitsalad: { - icon: "nutrition", - name: "Fruit Salad", - description: "A playful theme - the seed colour's hue does not appear in the theme.", - }, - rainbow: { - icon: "looks", - name: "Rainbow", - description: "A playful theme - the seed colour's hue does not appear in the theme.", - }, - neutral: { - icon: "contrast", - name: "Neutral", - description: "Close to grayscale, a hint of chroma.", - }, - monochrome: { - icon: "filter_b_and_w", - name: "Monochrome", - description: "All colours are grayscale, no chroma.", - }, -}; - -const transparencyActions = { - off: { - icon: "blur_off", - name: "Off", - description: "Completely opaque", - }, - low: { - icon: "blur_circular", - name: "Low", - description: "Less transparent", - }, - normal: { - icon: "blur_linear", - name: "Normal", - description: "Somewhat transparent", - }, - high: { - icon: "blur_on", - name: "High", - description: "Extremely transparent", - }, -}; - -const autocomplete = (entry: Widget.Entry, action: string) => { - entry.set_text(`${config.actionPrefix.get()}${action} `); - entry.set_position(-1); -}; - -const actions = (mode: Variable<Mode>, entry: Widget.Entry): ActionMap => ({ - apps: { - icon: "apps", - name: "Apps", - description: "Search for apps", - action: () => { - mode.set("apps"); - entry.set_text(""); - }, - }, - files: { - icon: "folder", - name: "Files", - description: "Search for files", - action: () => { - mode.set("files"); - entry.set_text(""); - }, - }, - math: { - icon: "calculate", - name: "Math", - description: "Do math calculations", - action: () => { - mode.set("math"); - entry.set_text(""); - }, - }, - light: { - icon: "light_mode", - name: "Light", - description: "Change scheme to light mode", - action: () => { - Palette.get_default().switchMode("light"); - close(); - }, - available: () => Palette.get_default().hasMode("light"), - }, - dark: { - icon: "dark_mode", - name: "Dark", - description: "Change scheme to dark mode", - action: () => { - Palette.get_default().switchMode("dark"); - close(); - }, - available: () => Palette.get_default().hasMode("dark"), - }, - scheme: { - icon: "palette", - name: "Scheme", - description: "Change the current colour scheme", - action: () => autocomplete(entry, "scheme"), - }, - variant: { - icon: "colors", - name: "Variant", - description: "Change the current scheme variant", - action: () => autocomplete(entry, "variant"), - available: () => Palette.get_default().scheme === "dynamic", - }, - wallpaper: { - icon: "image", - name: "Wallpaper", - description: "Change the current wallpaper", - action: () => autocomplete(entry, "wallpaper"), - }, - transparency: { - icon: "opacity", - name: "Transparency", - description: "Change shell transparency", - action: () => autocomplete(entry, "transparency"), - }, - todo: { - icon: "checklist", - name: "Todo", - description: "Create a todo in Todoist", - action: (...args) => { - // If no args, autocomplete cmd - if (args.length === 0) return autocomplete(entry, "todo"); - - // If tod not configured, notify - let token = null; - try { - token = JSON.parse(readFile(GLib.get_user_config_dir() + "/tod.cfg")).token; - } catch {} // Ignore - if (!token) { - notify({ - summary: "Tod not configured", - body: "You need to configure tod first. Run any tod command to do this.", - icon: "dialog-warning-symbolic", - urgency: "critical", - }); - } else { - // Create todo and notify if configured - execAsync(`tod t q -c ${args.join(" ")}`).catch(console.error); - if (config.todo.notify.get()) - notify({ - summary: "Todo created", - body: `Created todo with content: ${args.join(" ")}`, - icon: "view-list-bullet-symbolic", - urgency: "low", - transient: true, - actions: { - "Copy content": () => execAsync(`wl-copy -- ${args.join(" ")}`).catch(console.error), - View: () => { - const client = AstalHyprland.get_default().clients.find(c => c.class === "Todoist"); - if (client) client.focus(); - else execAsync("app2unit -- todoist").catch(console.error); - }, - }, - }); - } - - close(); - }, - available: () => !!GLib.find_program_in_path("tod"), - }, - reload: { - icon: "refresh", - name: "Reload", - description: "Reload app list", - action: () => { - Apps.reload(); - entry.set_text(""); - }, - }, - lock: { - icon: "lock", - name: "Lock", - description: "Lock the current session", - action: () => { - execAsync("loginctl lock-session").catch(console.error); - close(); - }, - }, - logout: { - icon: "logout", - name: "Logout", - description: "End the current session", - action: () => { - execAsync("uwsm stop").catch(console.error); - close(); - }, - }, - sleep: { - icon: "bedtime", - name: "Sleep", - description: "Suspend then hibernate", - action: () => { - execAsync("systemctl suspend-then-hibernate").catch(console.error); - close(); - }, - }, - reboot: { - icon: "cached", - name: "Reboot", - description: "Restart the machine", - action: () => { - execAsync("systemctl reboot").catch(console.error); - close(); - }, - }, - hibernate: { - icon: "downloading", - name: "Hibernate", - description: "Suspend to RAM", - action: () => { - execAsync("systemctl hibernate").catch(console.error); - close(); - }, - }, - shutdown: { - icon: "power_settings_new", - name: "Shutdown", - description: "Suspend to disk", - action: () => { - execAsync("systemctl poweroff").catch(console.error); - close(); - }, - }, -}); - -const Action = ({ args, icon, name, description, action }: IAction & { args: string[] }) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result" - cursor="pointer" - onClicked={() => action(...args)} - setup={self => setupCustomTooltip(self, description)} - > - <box> - <label className="icon" label={icon} /> - <box vertical className="has-sublabel"> - <label truncate xalign={0} label={name} /> - <label truncate xalign={0} label={description} className="sublabel" /> - </box> - </box> - </button> - </Gtk.FlowBoxChild> -); - -const Swatch = ({ colour }: { colour: string }) => <box className="swatch" css={"background-color: " + colour + ";"} />; - -const Scheme = ({ scheme, name, colours }: { scheme?: string; name: string; colours?: Colours }) => { - const palette = colours![Palette.get_default().mode] ?? colours!.light ?? colours!.dark!; - return ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result" - cursor="pointer" - onClicked={() => { - execAsync(`caelestia scheme ${scheme ?? ""} ${name}`).catch(console.error); - close(); - }} - > - <box> - <box valign={Gtk.Align.CENTER}> - <box className="swatch big left" css={"background-color: " + palette.base + ";"} /> - <box className="swatch big right" css={"background-color: " + palette.primary + ";"} /> - </box> - <box vertical className="has-sublabel"> - <label truncate xalign={0} label={scheme ? `${scheme} (${name})` : name} /> - <box className="swatches"> - <Swatch colour={palette.rosewater} /> - <Swatch colour={palette.flamingo} /> - <Swatch colour={palette.pink} /> - <Swatch colour={palette.mauve} /> - <Swatch colour={palette.red} /> - <Swatch colour={palette.maroon} /> - <Swatch colour={palette.peach} /> - <Swatch colour={palette.yellow} /> - <Swatch colour={palette.green} /> - <Swatch colour={palette.teal} /> - <Swatch colour={palette.sky} /> - <Swatch colour={palette.sapphire} /> - <Swatch colour={palette.blue} /> - <Swatch colour={palette.lavender} /> - </box> - </box> - </box> - </button> - </Gtk.FlowBoxChild> - ); -}; - -const Variant = ({ name }: { name: keyof typeof variantActions }) => ( - <Action - {...variantActions[name]} - args={[]} - action={() => { - execAsync(`caelestia variant ${name}`).catch(console.error); - close(); - }} - /> -); - -const Wallpaper = ({ path, thumbnails }: IWallpaper) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result wallpaper-container" - cursor="pointer" - onClicked={() => { - execAsync(`caelestia wallpaper -f ${path}`).catch(console.error); - close(); - }} - setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))} - > - <box - vertical={config.wallpaper.style.get() !== "compact"} - className={`wallpaper ${config.wallpaper.style.get()}`} - > - <box - className="thumbnail" - css={bind(config.wallpaper.style).as( - s => "background-image: url('" + thumbnails[s as keyof typeof thumbnails] + "');" - )} - /> - <label truncate label={basename(path)} /> - </box> - </button> - </Gtk.FlowBoxChild> -); - -const CategoryThumbnail = ({ style, wallpapers }: { style: string; wallpapers: IWallpaper[] }) => ( - <box className="thumbnail"> - {wallpapers.slice(0, 3).map(w => ( - <box hexpand css={"background-image: url('" + w.thumbnails[style as keyof typeof w.thumbnails] + "');"} /> - ))} - </box> -); - -const Category = ({ path, wallpapers }: ICategory) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result wallpaper-container" - cursor="pointer" - onClicked={() => { - execAsync(`caelestia wallpaper -d ${path}`).catch(console.error); - close(); - }} - setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))} - > - <box - vertical={config.wallpaper.style.get() !== "compact"} - className={`wallpaper ${config.wallpaper.style.get()}`} - > - {bind(config.wallpaper.style).as(s => - s === "compact" ? ( - <box - className="thumbnail" - css={"background-image: url('" + wallpapers[0].thumbnails.compact + "');"} - /> - ) : ( - <CategoryThumbnail style={s} wallpapers={wallpapers} /> - ) - )} - <label truncate label={basename(path)} /> - </box> - </button> - </Gtk.FlowBoxChild> -); - -const Transparency = ({ amount }: { amount: keyof typeof transparencyActions }) => ( - <Action - {...transparencyActions[amount]} - args={[]} - action={() => { - setConfig("style.transparency", amount).catch(console.error); - close(); - }} - /> -); - -@register() -export default class Actions extends Widget.Box implements LauncherContent { - #map: ActionMap; - #list: string[]; - - #content: FlowBox; - - constructor(mode: Variable<Mode>, entry: Widget.Entry) { - super({ name: "actions", className: "actions" }); - - this.#map = actions(mode, entry); - this.#list = Object.keys(this.#map); - - this.#content = (<ContentBox />) as FlowBox; - - this.add( - <scrollable expand hscroll={Gtk.PolicyType.NEVER}> - {this.#content} - </scrollable> - ); - } - - updateContent(search: string): void { - this.#content.foreach(c => c.destroy()); - const args = search.split(" "); - const action = args[0].slice(1).toLowerCase(); - - if (action === "scheme") { - const scheme = args[1] ?? ""; - const schemes = Object.values(Schemes.get_default().map) - .flatMap(s => (s.colours ? s.name : Object.values(s.flavours!).map(f => `${f.scheme}-${f.name}`))) - .filter(s => s !== undefined) - .sort(); - for (const { target } of fuzzysort.go(scheme, schemes, { all: true })) { - if (Schemes.get_default().map.hasOwnProperty(target)) - this.#content.add(<Scheme {...Schemes.get_default().map[target]} />); - else { - const [scheme, flavour] = target.split("-"); - this.#content.add(<Scheme {...Schemes.get_default().map[scheme].flavours![flavour]} />); - } - } - } else if (action === "variant") { - const list = Object.keys(variantActions); - - for (const { target } of fuzzysort.go(args[1], list, { all: true })) - this.#content.add(<Variant name={target as keyof typeof variantActions} />); - } else if (action === "wallpaper") { - if (args[1]?.toLowerCase() === "random") { - const list = Wallpapers.get_default().categories; - for (const { obj } of fuzzysort.go(args[2] ?? "", list, { all: true, key: "path" })) - this.#content.add(<Category {...obj} />); - } else { - const list = Wallpapers.get_default().list; - let limit = undefined; - if ((args[1] || !config.wallpaper.showAllEmpty.get()) && config.wallpaper.maxResults.get() > 0) - limit = config.wallpaper.maxResults.get(); - - for (const { obj } of fuzzysort.go(args[1] ?? "", list, { all: true, key: "path", limit })) - this.#content.add(<Wallpaper {...obj} />); - } - } else if (action === "transparency") { - const list = Object.keys(transparencyActions); - - for (const { target } of fuzzysort.go(args[1], list, { all: true })) - this.#content.add(<Transparency amount={target as keyof typeof transparencyActions} />); - } else { - const list = this.#list.filter( - a => this.#map[a].available?.() ?? !config.disabledActions.get().includes(a) - ); - for (const { target } of fuzzysort.go(action, list, { all: true })) - this.#content.add(<Action {...this.#map[target]} args={args.slice(1)} />); - } - } - - handleActivate(search: string): void { - const args = search.split(" "); - const action = args[0].slice(1).toLowerCase(); - - if (action === "scheme" && args[1]?.toLowerCase() === "random") { - execAsync(`caelestia scheme`).catch(console.error); - close(); - } else this.#content.get_child_at_index(0)?.get_child()?.activate(); - } -} diff --git a/src/modules/launcher/index.tsx b/src/modules/launcher/index.tsx deleted file mode 100644 index b75ecce..0000000 --- a/src/modules/launcher/index.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import PopupWindow from "@/widgets/popupwindow"; -import { bind, register, Variable } from "astal"; -import { Astal, Gtk, Widget } from "astal/gtk3"; -import { launcher as config } from "config"; -import Actions from "./actions"; -import Modes from "./modes"; -import type { Mode } from "./util"; - -const getModeIcon = (mode: Mode) => { - if (mode === "apps") return "apps"; - if (mode === "files") return "folder"; - if (mode === "math") return "calculate"; - return "search"; -}; - -const getPrettyMode = (mode: Mode) => { - if (mode === "apps") return "Apps"; - if (mode === "files") return "Files"; - if (mode === "math") return "Math"; - return mode; -}; - -const isAction = (text: string, action: string = "") => text.startsWith(config.actionPrefix.get() + action); - -const SearchBar = ({ mode, entry }: { mode: Variable<Mode>; entry: Widget.Entry }) => ( - <box className="search-bar"> - <box className="mode"> - <label className="icon" label={bind(mode).as(getModeIcon)} /> - <label label={bind(mode).as(getPrettyMode)} /> - </box> - {entry} - </box> -); - -const ModeSwitcher = ({ mode, modes }: { mode: Variable<Mode>; modes: Mode[] }) => ( - <box homogeneous hexpand className="mode-switcher"> - {modes.map(m => ( - <button - className={bind(mode).as(c => `mode ${c === m ? "selected" : ""}`)} - cursor="pointer" - onClicked={() => mode.set(m)} - > - <box halign={Gtk.Align.CENTER}> - <label className="icon" label={getModeIcon(m)} /> - <label label={getPrettyMode(m)} /> - </box> - </button> - ))} - </box> -); - -@register() -export default class Launcher extends PopupWindow { - readonly mode: Variable<Mode>; - - constructor() { - const entry = ( - <entry - hexpand - className="entry" - placeholderText={bind(config.actionPrefix).as(p => `Type "${p}" for subcommands`)} - /> - ) as Widget.Entry; - const mode = Variable<Mode>("apps"); - const content = Modes(); - const actions = new Actions(mode, entry); - const className = Variable.derive([mode, config.style], (m, s) => `launcher ${m} ${s}`); - - super({ - name: "launcher", - anchor: - Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.RIGHT, - keymode: Astal.Keymode.EXCLUSIVE, - exclusivity: Astal.Exclusivity.IGNORE, - borderWidth: 0, - onKeyPressEvent(_, event) { - const keyval = event.get_keyval()[1]; - // Focus entry on typing - if (!entry.isFocus && keyval >= 32 && keyval <= 126) { - entry.text += String.fromCharCode(keyval); - entry.grab_focus(); - entry.set_position(-1); - - // Consume event, if not consumed it will duplicate character in entry - return true; - } - }, - child: ( - <box - vertical - halign={Gtk.Align.CENTER} - valign={Gtk.Align.CENTER} - className={bind(className)} - onDestroy={() => className.drop()} - > - <SearchBar mode={mode} entry={entry} /> - <stack - expand - transitionType={Gtk.StackTransitionType.CROSSFADE} - transitionDuration={100} - shown={bind(entry, "text").as(t => (isAction(t) ? "actions" : "content"))} - > - <stack - name="content" - transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT} - transitionDuration={200} - shown={bind(mode)} - > - {Object.values(content)} - </stack> - {actions} - </stack> - <ModeSwitcher mode={mode} modes={Object.keys(content) as Mode[]} /> - </box> - ), - }); - - this.mode = mode; - - content[mode.get()].updateContent(entry.get_text()); - this.hook(mode, (_, v: Mode) => { - entry.set_text(""); - content[v].updateContent(entry.get_text()); - }); - this.hook(entry, "changed", () => - (isAction(entry.get_text()) ? actions : content[mode.get()]).updateContent(entry.get_text()) - ); - this.hook(entry, "activate", () => { - (isAction(entry.get_text()) ? actions : content[mode.get()]).handleActivate(entry.get_text()); - if (mode.get() === "math" && !isAction(entry.get_text())) entry.set_text(""); // Cause math mode doesn't auto clear - }); - - // Clear search on hide if not in math mode or creating a todo - this.connect("hide", () => { - if ((mode.get() !== "math" || isAction(entry.get_text())) && !isAction(entry.get_text(), "todo")) - entry.set_text(""); - }); - } - - open(mode: Mode) { - this.mode.set(mode); - this.show(); - } -} diff --git a/src/modules/launcher/modes.tsx b/src/modules/launcher/modes.tsx deleted file mode 100644 index e278779..0000000 --- a/src/modules/launcher/modes.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { Apps as AppsService } from "@/services/apps"; -import MathService, { type HistoryItem } from "@/services/math"; -import { getAppCategoryIcon } from "@/utils/icons"; -import { launch } from "@/utils/system"; -import { type FlowBox, setupCustomTooltip } from "@/utils/widgets"; -import { bind, execAsync, Gio, register, Variable } from "astal"; -import { Astal, Gtk, Widget } from "astal/gtk3"; -import { launcher as config } from "config"; -import type AstalApps from "gi://AstalApps"; -import { close, ContentBox, type LauncherContent, limitLength } from "./util"; - -const AppResult = ({ app }: { app: AstalApps.Application }) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result" - cursor="pointer" - onClicked={() => { - launch(app); - close(); - }} - setup={self => setupCustomTooltip(self, app.description ? `${app.name}: ${app.description}` : app.name)} - > - <box> - {app.iconName && Astal.Icon.lookup_icon(app.iconName) ? ( - <icon className="icon" icon={app.iconName} /> - ) : ( - <label className="icon" label={getAppCategoryIcon(app)} /> - )} - <label truncate label={app.name} /> - </box> - </button> - </Gtk.FlowBoxChild> -); - -const FileResult = ({ path }: { path: string }) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result" - cursor="pointer" - onClicked={() => { - execAsync([ - "bash", - "-c", - `dbus-send --session --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:'file://${path}' string:'' || app2unit -O '${path}'`, - ]).catch(console.error); - close(); - }} - > - <box setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))}> - <icon - className="icon" - gicon={ - Gio.File.new_for_path(path) - .query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, null) - .get_icon()! - } - /> - <label - truncate - label={ - path.replace(HOME, "~").length > config.files.shortenThreshold.get() - ? path - .replace(HOME, "~") - .split("/") - .map((n, i, arr) => (i === 0 || i === arr.length - 1 ? n : n.slice(0, 1))) - .join("/") - : path.replace(HOME, "~") - } - /> - </box> - </button> - </Gtk.FlowBoxChild> -); - -const MathResult = ({ icon, equation, result }: HistoryItem) => ( - <Gtk.FlowBoxChild visible canFocus={false}> - <button - className="result" - cursor="pointer" - onClicked={() => { - execAsync(["wl-copy", "--", result]).catch(console.error); - close(); - }} - setup={self => setupCustomTooltip(self, `${equation} -> ${result}`)} - > - <box> - <label className="icon" label={icon} /> - <box vertical className="has-sublabel"> - <label truncate xalign={0} label={equation} /> - <label truncate xalign={0} label={result} className="sublabel" /> - </box> - </box> - </button> - </Gtk.FlowBoxChild> -); - -@register() -class Apps extends Widget.Box implements LauncherContent { - #content: FlowBox; - - constructor() { - super({ name: "apps", className: "apps" }); - - this.#content = (<ContentBox />) as FlowBox; - - this.add( - <scrollable expand hscroll={Gtk.PolicyType.NEVER}> - {this.#content} - </scrollable> - ); - } - - updateContent(search: string): void { - this.#content.foreach(c => c.destroy()); - for (const app of limitLength(AppsService.fuzzy_query(search), config.apps)) - this.#content.add(<AppResult app={app} />); - } - - handleActivate(): void { - this.#content.get_child_at_index(0)?.get_child()?.grab_focus(); - this.#content.get_child_at_index(0)?.get_child()?.activate(); - } -} - -@register() -class Files extends Widget.Box implements LauncherContent { - #content: FlowBox; - - constructor() { - super({ name: "files", className: "files" }); - - this.#content = (<ContentBox />) as FlowBox; - - this.add( - <scrollable expand hscroll={Gtk.PolicyType.NEVER}> - {this.#content} - </scrollable> - ); - } - - updateContent(search: string): void { - execAsync(["fd", ...config.files.fdOpts.get(), search, HOME]) - .then(out => { - this.#content.foreach(c => c.destroy()); - const paths = out.split("\n").filter(path => path); - for (const path of limitLength(paths, config.files)) this.#content.add(<FileResult path={path} />); - }) - .catch(() => {}); // Ignore errors - } - - handleActivate(): void { - this.#content.get_child_at_index(0)?.get_child()?.grab_focus(); - this.#content.get_child_at_index(0)?.get_child()?.activate(); - } -} - -@register() -class Math extends Widget.Box implements LauncherContent { - #showResult: Variable<boolean>; - #result: Variable<HistoryItem>; - #content: FlowBox; - - constructor() { - super({ name: "math", className: "math", vertical: true }); - - this.#showResult = Variable(false); - this.#result = Variable({ equation: "", result: "", icon: "" }); - this.#content = (<ContentBox />) as FlowBox; - - this.add( - <revealer - transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN} - transitionDuration={150} - revealChild={bind(this.#showResult)} - > - <box vertical className="preview"> - <box className="result"> - <label className="icon" label={bind(this.#result).as(r => r.icon)} /> - <box vertical> - <label xalign={0} label="Result" /> - <label - truncate - xalign={0} - className="sublabel" - label={bind(this.#result).as(r => r.result)} - /> - </box> - </box> - <box visible={bind(config.style).as(s => s === "lines")} className="separator" /> - </box> - </revealer> - ); - this.add( - <scrollable expand hscroll={Gtk.PolicyType.NEVER}> - {this.#content} - </scrollable> - ); - } - - updateContent(search: string): void { - this.#showResult.set(search.length > 0); - this.#result.set(MathService.get_default().evaluate(search)); - - this.#content.foreach(c => c.destroy()); - for (const item of limitLength(MathService.get_default().history, config.math)) - this.#content.add(<MathResult {...item} />); - } - - handleActivate(search: string): void { - if (!search) return; - MathService.get_default().commit(); - const res = this.#result.get(); - // Copy and close if not assignment, help or error - if (!["equal", "help", "error"].includes(res.icon)) { - execAsync(["wl-copy", "--", res.result]).catch(console.error); - close(); - } - } -} - -export default () => ({ - apps: new Apps(), - files: new Files(), - math: new Math(), -}); diff --git a/src/modules/launcher/util.tsx b/src/modules/launcher/util.tsx deleted file mode 100644 index 8288588..0000000 --- a/src/modules/launcher/util.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { FlowBox } from "@/utils/widgets"; -import type { Variable } from "astal"; -import { App, Gtk } from "astal/gtk3"; - -export type Mode = "apps" | "files" | "math"; - -export interface LauncherContent { - updateContent(search: string): void; - handleActivate(search: string): void; -} - -export const close = () => App.get_window("launcher")?.hide(); - -export const limitLength = <T,>(arr: T[], cfg: { maxResults: Variable<number> }) => - cfg.maxResults.get() > 0 && arr.length > cfg.maxResults.get() ? arr.slice(0, cfg.maxResults.get()) : arr; - -export const ContentBox = () => ( - <FlowBox homogeneous valign={Gtk.Align.START} minChildrenPerLine={2} maxChildrenPerLine={2} /> -); |