dots/ags/widget/launcher/shortcuts.tsx
2025-05-03 17:06:04 +02:00

83 lines
2.6 KiB
TypeScript

import { generateModmaskMap, hideWindow, limit, skip } from "@lib/utils";
import { App, Astal, Gtk } from "astal/gtk4";
import { Variable, bind } from "astal";
import Hyprland from "gi://AstalHyprland";
const modmasks = generateModmaskMap();
// Incomplete Astal types?
type Bind = Hyprland.Bind & { description: string };
function ShortcutEntry({ entry }: { entry: Bind }) {
return <box cssClasses={["entry"]} spacing={4}>
{entry.modmask !== 0 && <label
cssClasses={["kbd"]}
label={modmasks[entry.modmask]?.toString()} />
}
<label cssClasses={["kbd"]} label={entry.key.toString()} />
<label label={entry.description} />
</box>
}
export default async function Shortcuts(_monitor_id: number) {
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
const hypr = Hyprland.get_default();
const shortcuts: Variable<Bind[]> = Variable([]);
const toSkip = Variable(0);
const list = Variable.derive([
bind(shortcuts),
bind(toSkip)
], (page, count) => limit(
skip(page, count),
10
));
const isEmpty = bind(list).as(list => list.length === 0);
const onScroll = (dy: number) => {
const value = toSkip.get();
if (dy < 0) {
if ((value - 10) < 0) return;
toSkip.set(value - 10)
} else {
if ((value + 10) > shortcuts.get().length) return;
toSkip.set(value + 10)
}
};
const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => {
if (self.is_visible()) shortcuts.set(hypr.get_binds() as Bind[])
})
return <window
visible={false}
monitor={bind(hypr, "focusedMonitor").as(monitor => monitor.id)}
name={"shortcuts"}
cssClasses={["shortcuts"]}
keymode={Astal.Keymode.EXCLUSIVE}
anchor={TOP | BOTTOM | LEFT | RIGHT}
onKeyPressed={hideWindow}
application={App}
setup={setup}>
<box hexpand={false} cssClasses={["shortcuts"]} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
<box spacing={6} vertical onScroll={(_, __, dy) => onScroll(dy)}>
{bind(list).as(list => list.map(entry => (
<ShortcutEntry entry={entry} />
)))}
</box>
<box
halign={Gtk.Align.CENTER}
cssClasses={["not-found"]}
vertical
visible={isEmpty}>
<image iconName="system-search-symbolic" />
<label label="No match found" />
</box>
</box>
</window>
}