aboutsummaryrefslogtreecommitdiff
path: root/ags/modules/applauncher.js
diff options
context:
space:
mode:
authoreric.marin <maarin.eric@gmail.com>2024-12-18 21:06:41 +0100
committereric.marin <maarin.eric@gmail.com>2024-12-18 21:06:41 +0100
commit000a7c2a4c4bda36f655f6489b5bac3211515a5d (patch)
tree8a5fe85f0632fcca678f0204a4288e6b3423f772 /ags/modules/applauncher.js
parentb0cfdab1e93f660fda8f9398e30c9c996a1760f3 (diff)
downloaddotfiles-000a7c2a4c4bda36f655f6489b5bac3211515a5d.tar.gz
dotfiles-000a7c2a4c4bda36f655f6489b5bac3211515a5d.zip
niri and fuzzel
Diffstat (limited to 'ags/modules/applauncher.js')
-rw-r--r--ags/modules/applauncher.js107
1 files changed, 107 insertions, 0 deletions
diff --git a/ags/modules/applauncher.js b/ags/modules/applauncher.js
new file mode 100644
index 0000000..f380185
--- /dev/null
+++ b/ags/modules/applauncher.js
@@ -0,0 +1,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,
+ }),
+})