From 91de0cf5b54bd0fcf574221dbef0686b5e98888a Mon Sep 17 00:00:00 2001
From: matt1432 <matt@nelim.org>
Date: Thu, 21 Sep 2023 20:01:14 -0400
Subject: [PATCH] feat(ags): make my own popup widget and switch windows to it

---
 config/ags/config.js                  |   7 ++
 config/ags/js/bar/clock.js            |  10 +--
 config/ags/js/bar/notif-button.js     |  10 +--
 config/ags/js/bar/quick-settings.js   |  10 +--
 config/ags/js/date.js                 |  20 +++--
 config/ags/js/misc/popup.js           |  19 +++++
 config/ags/js/notifications/center.js |  57 +++++++------
 config/ags/js/powermenu.js            |  59 ++++++++------
 config/ags/js/quick-settings/main.js  | 112 ++++++++++++++------------
 config/hypr/main.conf                 |   2 +-
 10 files changed, 169 insertions(+), 137 deletions(-)
 create mode 100644 config/ags/js/misc/popup.js

diff --git a/config/ags/config.js b/config/ags/config.js
index 76f2b779..3dbae275 100644
--- a/config/ags/config.js
+++ b/config/ags/config.js
@@ -20,6 +20,13 @@ exec(`bash -c "$AGS_PATH/startup.sh"`);
 export default {
   style: css,
   notificationPopupTimeout: 5000,
+  cacheNotificationActions: true,
+  closeWindowDelay: {
+    'quick-settings': 500,
+    'notification-center': 500,
+    'calendar': 500,
+    'powermenu': 500,
+  },
   windows: [
     Powermenu,
     Bar,
diff --git a/config/ags/js/bar/clock.js b/config/ags/js/bar/clock.js
index 1da870b6..b62ea686 100644
--- a/config/ags/js/bar/clock.js
+++ b/config/ags/js/bar/clock.js
@@ -1,5 +1,5 @@
 const { Box, Label } = ags.Widget;
-const { toggleWindow, openWindow } = ags.App;
+const { toggleWindow } = ags.App;
 const { DateTime } = imports.gi.GLib;
 
 import { EventBox } from '../misc/cursorbox.js';
@@ -24,13 +24,7 @@ export const Clock = EventBox({
   connections: [
     [ags.App, (box, windowName, visible) => {
       if (windowName == 'calendar') {
-        if (visible) {
-          Clock.toggleClassName('toggle-on', true);
-          openWindow('closer');
-        }
-        else {
-          Clock.toggleClassName('toggle-on', false);
-        }
+        box.toggleClassName('toggle-on', visible);
       }
     }],
   ],
diff --git a/config/ags/js/bar/notif-button.js b/config/ags/js/bar/notif-button.js
index 90cd09db..4647e540 100644
--- a/config/ags/js/bar/notif-button.js
+++ b/config/ags/js/bar/notif-button.js
@@ -1,5 +1,5 @@
 const { Box, Label, Icon } = ags.Widget;
-const { toggleWindow, openWindow } = ags.App;
+const { toggleWindow } = ags.App;
 const { Notifications } = ags.Service;
 
 import { Separator } from '../misc/separator.js';
@@ -11,13 +11,7 @@ export const NotifButton = EventBox({
   connections: [
     [ags.App, (box, windowName, visible) => {
       if (windowName == 'notification-center') {
-        if (visible) {
-          NotifButton.toggleClassName('toggle-on', true);
-          openWindow('closer');
-        }
-        else {
-          NotifButton.toggleClassName('toggle-on', false);
-        }
+        box.toggleClassName('toggle-on', visible);
       }
     }],
   ],
diff --git a/config/ags/js/bar/quick-settings.js b/config/ags/js/bar/quick-settings.js
index de19f8fe..3b0e8b26 100644
--- a/config/ags/js/bar/quick-settings.js
+++ b/config/ags/js/bar/quick-settings.js
@@ -1,5 +1,5 @@
 const { Box, Label } = ags.Widget;
-const { toggleWindow, openWindow } = ags.App;
+const { toggleWindow } = ags.App;
 
 import { EventBox } from '../misc/cursorbox.js';
 
@@ -9,13 +9,7 @@ export const QsToggle = EventBox({
   connections: [
     [ags.App, (box, windowName, visible) => {
       if (windowName == 'quick-settings') {
-        if (visible) {
-          QsToggle.toggleClassName('toggle-on', true);
-          openWindow('closer');
-        }
-        else {
-          QsToggle.toggleClassName('toggle-on', false);
-        }
+        box.toggleClassName('toggle-on', visible);
       }
     }],
   ],
diff --git a/config/ags/js/date.js b/config/ags/js/date.js
index 10ad8611..1b623e9f 100644
--- a/config/ags/js/date.js
+++ b/config/ags/js/date.js
@@ -2,6 +2,8 @@ const { Box, Label, Window } = ags.Widget;
 const { Gtk } = imports.gi;
 const { DateTime } = imports.gi.GLib;
 
+import { PopUp } from './misc/popup.js';
+
 const Divider = () => Box({
   className: 'divider',
   vertical: true,
@@ -68,16 +70,18 @@ const CalendarWidget = () => Box({
 
 export const Calendar = Window({
   name: 'calendar',
-  popup: true,
   layer: 'overlay',
   anchor: 'top right',
   margin: [ 8, 182, 0, 0],
-  child: Box({
-    className: 'date',
-    vertical: true,
-    children: [
-      Time(),
-      CalendarWidget(),
-    ],
+  child: PopUp({
+    name: 'calendar',
+    child: Box({
+      className: 'date',
+      vertical: true,
+      children: [
+        Time(),
+        CalendarWidget(),
+      ],
+    }),
   }),
 });
diff --git a/config/ags/js/misc/popup.js b/config/ags/js/misc/popup.js
new file mode 100644
index 00000000..33a511d7
--- /dev/null
+++ b/config/ags/js/misc/popup.js
@@ -0,0 +1,19 @@
+const { Revealer } = ags.Widget;
+const { closeWindow, openWindow } = ags.App;
+
+export const PopUp = ({name, child, transition = 'slide_down', ...params}) => Revealer({
+  ...params,
+  // FIXME: popups don't work with revealers
+  setup: () => setTimeout(() => closeWindow(name), 100),
+  transition,
+  transitionDuration: 500,
+  connections: [[ags.App, (revealer, currentName, visible) => {
+    if (currentName === name) {
+      revealer.reveal_child = visible;
+
+      if (visible)
+        openWindow('closer');
+    }
+  }]],
+  child: child,
+});
diff --git a/config/ags/js/notifications/center.js b/config/ags/js/notifications/center.js
index d2e9bd9d..9e09d80a 100644
--- a/config/ags/js/notifications/center.js
+++ b/config/ags/js/notifications/center.js
@@ -4,7 +4,8 @@ const { timeout } = ags.Utils;
 const { getWindow } = ags.App;
 
 import Notification from './base.js';
-import { EventBox } from '../misc/cursorbox.js'
+import { EventBox } from '../misc/cursorbox.js';
+import { PopUp } from '../misc/popup.js';
 
 const ClearButton = () => EventBox({
   child: Button({
@@ -21,7 +22,7 @@ const ClearButton = () => EventBox({
     properties: [['notifList'], ['popups']],
     connections: [[Notifications, button => {
       if (!button._notifList)
-        button._notifList = getWindow('notification-center').child.children[1].children[0].child.child.children[0];
+        button._notifList = NotificationList;
 
       if (!button._popups)
         button._popups = getWindow('notifications').child.children[0].child;
@@ -50,7 +51,7 @@ const Header = () => Box({
   ],
 });
 
-const NotificationList = () => Box({
+const NotificationList = Box({
   vertical: true,
   vexpand: true,
   connections: [
@@ -107,33 +108,37 @@ const Placeholder = () => Revealer({
   }),
 });
 
+const NotificationCenterWidget = Box({
+  className: 'notification-center',
+  vertical: true,
+  children: [
+    Header(),
+    Box({
+      className: 'notification-wallpaper-box',
+      children: [Scrollable({
+        className: 'notification-list-box',
+        hscroll: 'never',
+        vscroll: 'automatic',
+        child: Box({
+          className: 'notification-list',
+          vertical: true,
+          children: [
+            NotificationList,
+            Placeholder(),
+          ],
+        }),
+      })],
+    }),
+  ],
+});
+
 export const NotificationCenter = Window({
   name: 'notification-center',
-  popup: true,
   layer: 'overlay',
   anchor: 'top right',
   margin: [ 8, 60, 0, 0 ],
-  child: Box({
-    className: 'notification-center',
-    vertical: true,
-    children: [
-      Header(),
-      Box({
-        className: 'notification-wallpaper-box',
-        children: [Scrollable({
-          className: 'notification-list-box',
-          hscroll: 'never',
-          vscroll: 'automatic',
-          child: Box({
-            className: 'notification-list',
-            vertical: true,
-            children: [
-              NotificationList(),
-              Placeholder(),
-            ],
-          }),
-        })],
-      }),
-    ],
+  child: PopUp({
+    name: 'notification-center',
+    child: NotificationCenterWidget,
   }),
 });
diff --git a/config/ags/js/powermenu.js b/config/ags/js/powermenu.js
index f43f2ed2..5a2b0411 100644
--- a/config/ags/js/powermenu.js
+++ b/config/ags/js/powermenu.js
@@ -1,37 +1,46 @@
-export const Powermenu = ags.Widget.Window({
-  name: 'powermenu',
-  popup: true,
-  layer: 'overlay',
+const { Window, CenterBox, Label } = ags.Widget;
 
-  child: ags.Widget.CenterBox({
-    className: 'powermenu',
-    vertical: false,
+import { PopUp } from './misc/popup.js';
+import { Button } from './misc/cursorbox.js'
 
-    startWidget: ags.Widget.Button({
-      className: 'shutdown',
-      onPrimaryClickRelease: 'systemctl poweroff',
+const PowermenuWidget = CenterBox({
+  className: 'powermenu',
+  vertical: false,
 
-      child: ags.Widget.Label({
-        label: '襤',
-      }),
+  startWidget: Button({
+    className: 'shutdown',
+    onPrimaryClickRelease: 'systemctl poweroff',
+
+    child: Label({
+      label: '襤',
     }),
+  }),
 
-    centerWidget: ags.Widget.Button({
-      className: 'reboot',
-      onPrimaryClickRelease: 'systemctl reboot',
+  centerWidget: Button({
+    className: 'reboot',
+    onPrimaryClickRelease: 'systemctl reboot',
 
-      child: ags.Widget.Label({
-        label: '勒',
-      }),
+    child: Label({
+      label: '勒',
     }),
+  }),
 
-    endWidget: ags.Widget.Button({
-      className: 'logout',
-      onPrimaryClickRelease: 'hyprctl dispatch exit',
+  endWidget: Button({
+    className: 'logout',
+    onPrimaryClickRelease: 'hyprctl dispatch exit',
 
-      child: ags.Widget.Label({
-        label: '',
-      }),
+    child: Label({
+      label: '',
     }),
   }),
 });
+
+export const Powermenu = Window({
+  name: 'powermenu',
+  layer: 'overlay',
+  child: PopUp({
+    name: 'powermenu',
+    transition: 'crossfade',
+    child: PowermenuWidget,
+  }),
+});
diff --git a/config/ags/js/quick-settings/main.js b/config/ags/js/quick-settings/main.js
index 545aa972..cef19033 100644
--- a/config/ags/js/quick-settings/main.js
+++ b/config/ags/js/quick-settings/main.js
@@ -1,67 +1,73 @@
 const { Window, CenterBox, Box, Label, Revealer, Icon } = ags.Widget;
+const { closeWindow } = ags.App;
 const { ToggleButton } = imports.gi.Gtk;
 
 import { ButtonGrid } from './button-grid.js';
 import { SliderBox } from './slider-box.js';
 import Player from '../media-player/player.js';
 import { EventBox } from '../misc/cursorbox.js';
+import { PopUp } from '../misc/popup.js';
+
+const QuickSettingsWidget = Box({
+  className: 'qs-container',
+  vertical: true,
+  children: [
+
+    Box({
+      className: 'quick-settings',
+      vertical: true,
+      children: [
+
+        Label({
+          label: 'Control Center',
+          className: 'title',
+          halign: 'start',
+          style: 'margin-left: 20px'
+        }),
+
+        ButtonGrid,
+
+        SliderBox,
+
+        EventBox({
+          child: ags.Widget({
+            type: ToggleButton,
+            connections: [['toggled', button => {
+              if (button.get_active()) {
+                button.child.setStyle("-gtk-icon-transform: rotate(0deg);");
+                button.get_parent().get_parent().get_parent().children[1].revealChild = true;
+              }
+              else {
+                button.child.setStyle('-gtk-icon-transform: rotate(180deg);');
+                button.get_parent().get_parent().get_parent().children[1].revealChild = false;
+              }
+            }]],
+            child: Icon({
+              icon: 'folder-download-symbolic',
+              className: 'arrow',
+              style: `-gtk-icon-transform: rotate(180deg);`,
+            }),
+          }),
+        }),
+
+      ],
+    }),
+
+    Revealer({
+      transition: 'slide_down',
+      child: Player(),
+    })
+
+  ],
+});
 
 export const QuickSettings = Window({
   name: 'quick-settings',
   layer: 'overlay',
-  popup: true,
   anchor: 'top right',
-  margin: [ 8, 5, 0,  ],
-  child: Box({
-    className: 'qs-container',
-    vertical: true,
-    children: [
-      
-      Box({
-        className: 'quick-settings',
-        vertical: true,
-        children: [
-
-          Label({
-            label: 'Control Center',
-            className: 'title',
-            halign: 'start',
-            style: 'margin-left: 20px'
-          }),
-
-          ButtonGrid,
-
-          SliderBox,
-
-          EventBox({
-            child: ags.Widget({
-              type: ToggleButton,
-              connections: [['toggled', button => {
-                if (button.get_active()) {
-                  button.child.setStyle("-gtk-icon-transform: rotate(0deg);");
-                  button.get_parent().get_parent().get_parent().children[1].revealChild = true;
-                }
-                else {
-                  button.child.setStyle('-gtk-icon-transform: rotate(180deg);');
-                  button.get_parent().get_parent().get_parent().children[1].revealChild = false;
-                }
-              }]],
-              child: Icon({
-                icon: 'folder-download-symbolic',
-                className: 'arrow',
-                style: `-gtk-icon-transform: rotate(180deg);`,
-              }),
-            }),
-          }),
-
-        ],
-      }),
-
-      Revealer({
-        transition: 'slide_down',
-        child: Player(),
-      })
-
-    ],
+  margin: [ 8, 5, 0, ],
+  child: PopUp({
+    name: 'quick-settings',
+    child: QuickSettingsWidget,
   }),
 });
diff --git a/config/hypr/main.conf b/config/hypr/main.conf
index a478a964..4b7f47aa 100644
--- a/config/hypr/main.conf
+++ b/config/hypr/main.conf
@@ -170,7 +170,7 @@ bind = $mainMod, T, togglespecialworkspace, thunder
 bind = $mainMod, C, killactive, 
 
 bind = $mainMod, L, exec, $LOCK_PATH/lock.sh
-bind = $mainMod SHIFT, E, exec, ags run-js 'ags.App.openWindow("closer")'; ags run-js 'ags.App.openWindow("powermenu")'
+bind = $mainMod SHIFT, E, exec, ags run-js 'ags.App.openWindow("powermenu")'
 bindn =, Escape, exec, ags run-js 'ags.App.closeAll()'
 bind = $mainMod SHIFT, SPACE, togglefloating, 
 bind = $mainMod, D, exec, wofi --show drun