nix/home/ags/bar/applauncher.ts

108 lines
2.9 KiB
TypeScript
Raw Normal View History

2024-08-21 21:30:11 +02:00
const { query } = await Service.import("applications")
const WINDOW_NAME = "applauncher"
/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */
const AppItem = app => Widget.Button({
on_clicked: () => {
App.closeWindow(WINDOW_NAME)
app.launch()
},
attribute: { app },
child: Widget.Box({
children: [
Widget.Icon({
icon: app.icon_name || "",
size: 42,
}),
Widget.Label({
class_name: "title",
label: app.name,
xalign: 0,
vpack: "center",
truncate: "end",
}),
],
}),
})
const Applauncher = ({ width = 500, height = 500, spacing = 12 }) => {
// list of application buttons
let applications = query("").map(AppItem)
// container holding the buttons
const list = Widget.Box({
vertical: true,
children: applications,
spacing,
})
// repopulate the box, so the most frequent apps are on top of the list
function repopulate() {
applications = query("").map(AppItem)
list.children = applications
}
// search entry
const entry = Widget.Entry({
hexpand: true,
css: `margin-bottom: ${spacing}px;`,
// to launch the first item on Enter
on_accept: () => {
// make sure we only consider visible (searched for) applications
const results = applications.filter((item) => item.visible);
if (results[0]) {
App.toggleWindow(WINDOW_NAME)
results[0].attribute.app.launch()
}
},
// filter out the list
on_change: ({ text }) => applications.forEach(item => {
item.visible = item.attribute.app.match(text ?? "")
}),
})
return Widget.Box({
vertical: true,
css: `margin: ${spacing * 2}px;`,
children: [
entry,
// wrap the list in a scrollable
Widget.Scrollable({
hscroll: "never",
css: `min-width: ${width}px;`
+ `min-height: ${height}px;`,
child: list,
}),
],
setup: self => self.hook(App, (_, windowName, visible) => {
if (windowName !== WINDOW_NAME)
return
// when the applauncher shows up
if (visible) {
repopulate()
entry.text = ""
entry.grab_focus()
}
}),
})
}
// there needs to be only one instance
export const applauncher = Widget.Window({
name: WINDOW_NAME,
setup: self => self.keybind("Escape", () => {
App.closeWindow(WINDOW_NAME)
}),
visible: false,
keymode: "exclusive",
child: Applauncher({
width: 500,
height: 500,
spacing: 12,
}),
})