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; entry: Widget.Entry }) => ( {entry} ); const ModeSwitcher = ({ mode, modes }: { mode: Variable; modes: Mode[] }) => ( {modes.map(m => ( ))} ); @register() export default class Launcher extends PopupWindow { readonly mode: Variable; constructor() { const entry = ( `Type "${p}" for subcommands`)} /> ) as Widget.Entry; const mode = Variable("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: ( className.drop()} > (isAction(t) ? "actions" : "content"))} > {Object.values(content)} {actions} ), }); 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(); } }