summaryrefslogtreecommitdiff
path: root/src/modules/launcher/modes.tsx
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-01 16:35:18 +1100
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-01 16:35:18 +1100
commit62602465ced5f65d4626f3cdf54b5fa30ba7c9dd (patch)
tree50223190f6d9c5e6dfc72f6d6492e26290521a16 /src/modules/launcher/modes.tsx
parentlauncher: better files (diff)
downloadcaelestia-shell-62602465ced5f65d4626f3cdf54b5fa30ba7c9dd.tar.gz
caelestia-shell-62602465ced5f65d4626f3cdf54b5fa30ba7c9dd.tar.bz2
caelestia-shell-62602465ced5f65d4626f3cdf54b5fa30ba7c9dd.zip
launcher: actions + refactor into multi file
Action prefix is configurable
Diffstat (limited to 'src/modules/launcher/modes.tsx')
-rw-r--r--src/modules/launcher/modes.tsx169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/modules/launcher/modes.tsx b/src/modules/launcher/modes.tsx
new file mode 100644
index 0000000..4d54ad2
--- /dev/null
+++ b/src/modules/launcher/modes.tsx
@@ -0,0 +1,169 @@
+import { Apps as AppsService } from "@/services/apps";
+import { getAppCategoryIcon } from "@/utils/icons";
+import { launch } from "@/utils/system";
+import { type FlowBox, setupCustomTooltip } from "@/utils/widgets";
+import { execAsync, Gio, register } 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:"" || xdg-open "${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>
+);
+
+@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 {
+ constructor() {
+ super({ name: "math", className: "math" });
+ }
+
+ updateContent(search: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ handleActivate(search: string): void {
+ throw new Error("Method not implemented.");
+ }
+}
+
+@register()
+class Windows extends Widget.Box implements LauncherContent {
+ constructor() {
+ super({ name: "windows", className: "windows" });
+ }
+
+ updateContent(search: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ handleActivate(search: string): void {
+ throw new Error("Method not implemented.");
+ }
+}
+
+export default () => ({
+ apps: new Apps(),
+ files: new Files(),
+ math: new Math(),
+ windows: new Windows(),
+});