1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
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,
}),
})
|