diff --git a/devices/wim/config/ags/config.js b/devices/wim/config/ags/config.js index f06c1d29..3d872e23 100644 --- a/devices/wim/config/ags/config.js +++ b/devices/wim/config/ags/config.js @@ -4,8 +4,8 @@ import { exec } from 'resource:///com/github/Aylur/ags/utils.js'; import Setup from './js/setup.js'; import Powermenu from './js/powermenu.js'; import * as Bar from './js/bar/main.js'; -//import NotifCenter from './js/notifications/center.js'; -//import NotifPopups from './js/notifications/popup.js'; +import NotifCenter from './js/notifications/center.js'; +import NotifPopups from './js/notifications/popup.js'; import Calendar from './js/date.js'; import QuickSettings from './js/quick-settings/main.js'; //import Overview from './js/overview/main.js'; @@ -18,7 +18,6 @@ exec(`sassc ${scss} ${css}`); Setup(); -// FIXME: notification and overview stuff are bugged as of this ags commit export default { style: css, notificationPopupTimeout: 5000, @@ -34,7 +33,7 @@ export default { windows: [ AppLauncher(), Calendar(), - //NotifCenter(), + NotifCenter(), //Overview(), Powermenu(), QuickSettings(), @@ -43,6 +42,6 @@ export default { Bar.BgGradient(), Corners.Bottomleft(), Corners.Bottomright(), - //NotifPopups(), + NotifPopups(), ], }; diff --git a/devices/wim/config/ags/js/notifications/base.js b/devices/wim/config/ags/js/notifications/base.js index d1c8fe10..5a81dabe 100644 --- a/devices/wim/config/ags/js/notifications/base.js +++ b/devices/wim/config/ags/js/notifications/base.js @@ -2,7 +2,7 @@ import Applications from 'resource:///com/github/Aylur/ags/service/applications. import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; import Variable from 'resource:///com/github/Aylur/ags/variable.js'; import { Box, Icon, Label, Button } from 'resource:///com/github/Aylur/ags/widget.js'; -import { lookUpIcon, execAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js'; +import { lookUpIcon, execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; import GLib from 'gi://GLib'; @@ -48,15 +48,17 @@ const NotificationIcon = notif => { return EventBox({ onPrimaryClickRelease: iconCmd, child: Box({ - valign: 'start', + vpack: 'start', hexpand: false, className: 'icon img', - style: `background-image: url("${notif.image}"); - background-size: contain; - background-repeat: no-repeat; - background-position: center; - min-width: 78px; - min-height: 78px;`, + css: ` + background-image: url("${notif.image}"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + min-width: 78px; + min-height: 78px; + `, }), }); } @@ -73,16 +75,16 @@ const NotificationIcon = notif => { return EventBox({ onPrimaryClickRelease: iconCmd, child: Box({ - valign: 'start', + vpack: 'start', hexpand: false, className: 'icon', - style: `min-width: 78px; - min-height: 78px;`, + css: `min-width: 78px; + min-height: 78px;`, children: [Icon({ icon, size: 58, - halign: 'center', + hpack: 'center', hexpand: true, - valign: 'center', + vpack: 'center', vexpand: true, })], }), @@ -114,37 +116,11 @@ export const Notification = ({ // Init notif const notifWidget = Gesture({ - maxOffset: 200, - command: () => command(), + command, slideIn, - properties: [ - ['hovered', false], - ['id', notif.id], - ], - onHover: w => { - if (!w._hovered) - w._hovered = true; - }, - onHoverLost: w => { - if (w._hovered) - w._hovered = false; - }, + id: notif.id, }); - // Notif methods - notifWidget.slideAway = side => { - notifWidget.child.setStyle(notifWidget.child[`_slide${side}`]); - notifWidget.sensitive = false; - timeout(400, () => { - notifWidget.child.setStyle(notifWidget.child[`_squeeze${side}`]); - timeout(500, () => { - HasNotifs.value = Notifications.notifications.length > 0; - notifWidget.get_parent().remove(notifWidget); - notifWidget.destroy(); - }); - }); - }; - // Add body to notif notifWidget.child.add(Box({ className: `notification ${notif.urgency}`, @@ -177,14 +153,14 @@ export const Notification = ({ }), Label({ className: 'time', - valign: 'start', + vpack: 'start', label: setTime(notif.time), }), EventBox({ reset: false, child: Button({ className: 'close-button', - valign: 'start', + vpack: 'start', onClicked: () => notif.close(), child: Icon('window-close-symbolic'), }), diff --git a/devices/wim/config/ags/js/notifications/center.js b/devices/wim/config/ags/js/notifications/center.js index d8ae4085..ddff1278 100644 --- a/devices/wim/config/ags/js/notifications/center.js +++ b/devices/wim/config/ags/js/notifications/center.js @@ -2,10 +2,56 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notification import { Button, Label, Box, Icon, Scrollable, Revealer } from 'resource:///com/github/Aylur/ags/widget.js'; import { Notification, HasNotifs } from './base.js'; -import PopupWindow from '../misc/popup.js'; -import EventBox from '../misc/cursorbox.js'; +import PopupWindow from '../misc/popup.js'; +import EventBox from '../misc/cursorbox.js'; +const addNotif = (box, notif) => { + if (!notif) + return; + + const NewNotif = Notification({ + notif, + slideIn: 'Right', + command: () => notif.close(), + }); + + if (NewNotif) { + box.pack_end(NewNotif, false, false, 0); + box.show_all(); + } +}; + +const NotificationList = () => Box({ + vertical: true, + vexpand: true, + vpack: 'start', + binds: [['visible', HasNotifs]], + setup: box => { + // Get cached notifications + const id = Notifications.connect('changed', () => { + Notifications.notifications.forEach(notif => { + addNotif(box, notif); + }); + Notifications.disconnect(id); + }); + }, + connections: [ + [Notifications, (box, id) => { + if (!id) + return; + + addNotif(box, Notifications.getNotification(id)); + }, 'notified'], + + [Notifications, (box, id) => { + const notif = box.children.find(ch => ch._id === id); + if (notif?.sensitive) + notif.slideAway('Right'); + }, 'closed'], + ], +}); + // Needs to be wrapped to still have onHover when disabled const ClearButton = () => EventBox({ child: Button({ @@ -37,65 +83,14 @@ const Header = () => Box({ ], }); -const NotificationList = () => Box({ - vertical: true, - vexpand: true, - valign: 'start', - binds: [['visible', HasNotifs]], - connections: [ - [Notifications, (box, id) => { - if (box.children.length == 0) { - for (const notif of Notifications.notifications) { - if (!notif) - return; - - const NewNotif = Notification({ - notif, - slideIn: 'Right', - command: () => notif.close(), - }); - - if (NewNotif) { - box.pack_end(NewNotif, false, false, 0); - box.show_all(); - } - } - } - else if (id) { - const notif = Notifications.getNotification(id); - - const NewNotif = Notification({ - notif, - slideIn: 'Right', - command: () => notif.close(), - }); - - if (NewNotif) { - box.pack_end(NewNotif, false, false, 0); - box.show_all(); - } - } - }, 'notified'], - - [Notifications, (box, id) => { - for (const ch of box.children) { - if (ch._id == id) { - ch.slideAway('Right'); - return; - } - } - }, 'closed'], - ], -}); - const Placeholder = () => Revealer({ transition: 'crossfade', binds: [['revealChild', HasNotifs, 'value', value => !value]], child: Box({ className: 'placeholder', vertical: true, - valign: 'center', - halign: 'center', + vpack: 'center', + hpack: 'center', vexpand: true, hexpand: true, children: [ @@ -133,6 +128,6 @@ const NotificationCenterWidget = () => Box({ export default () => PopupWindow({ name: 'notification-center', anchor: ['top', 'right'], - margin: [6, 60, 0, 0], + margins: [6, 60, 0, 0], child: NotificationCenterWidget(), }); diff --git a/devices/wim/config/ags/js/notifications/gesture.js b/devices/wim/config/ags/js/notifications/gesture.js index 1cb0f7dd..36f4c5e4 100644 --- a/devices/wim/config/ags/js/notifications/gesture.js +++ b/devices/wim/config/ags/js/notifications/gesture.js @@ -1,40 +1,43 @@ +import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; import { Box, EventBox } from 'resource:///com/github/Aylur/ags/widget.js'; import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; +import { HasNotifs } from './base.js'; + import Gtk from 'gi://Gtk'; import Gdk from 'gi://Gdk'; const display = Gdk.Display.get_default(); export default ({ + id, slideIn = 'Left', - maxOffset = 150, + maxOffset = 200, startMargin = 0, endMargin = 300, command = () => {}, - onHover = () => {}, - onHoverLost = () => {}, - child = '', - children = [], - properties = [[]], ...props }) => { const widget = EventBox({ ...props, - properties: [ - ['dragging', false], - ...properties, - ], onHover: self => { self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); - onHover(self); + if (!self._hovered) + self._hovered = true; }, onHoverLost: self => { self.window.set_cursor(null); - onHoverLost(self); + if (self._hovered) + self._hovered = false; }, }); + // Properties + widget._dragging = false; + widget._hovered = false; + widget._id = id; + widget.ready = false; + const gesture = Gtk.GestureDrag.new(widget); const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;'; @@ -48,32 +51,45 @@ export default ({ margin-right: -${Number(maxOffset + endMargin)}px; `; - const slideLeft = `${TRANSITION} ${MAX_LEFT} opacity: 0;`; + const slideLeft = `${TRANSITION} ${MAX_LEFT} margin-top: 0px; margin-bottom: 0px; opacity: 0;`; const squeezeLeft = `${TRANSITION} ${MAX_LEFT} ${SQUEEZED} opacity: 0;`; - const slideRight = `${TRANSITION} ${MAX_RIGHT} opacity: 0;`; + const slideRight = `${TRANSITION} ${MAX_RIGHT} margin-top: 0px; margin-bottom: 0px; opacity: 0;`; const squeezeRight = `${TRANSITION} ${MAX_RIGHT} ${SQUEEZED} opacity: 0;`; + const defaultStyle = `${TRANSITION} margin: unset; opacity: 1;`; + + // Notif methods + widget.slideAway = side => { + // Slide away + widget.child.setCss(side === 'Left' ? slideLeft : slideRight); + + // Makie it uninteractable + widget.sensitive = false; + + timeout(400, () => { + // Reduce height after sliding away + widget.child?.setCss(side === 'Left' ? squeezeLeft : squeezeRight); + + timeout(500, () => { + // Kill notif and update HasNotifs after anim is done + command(); + HasNotifs.value = Notifications.notifications.length > 0; + widget.get_parent()?.remove(widget); + }); + }); + }; widget.add(Box({ - properties: [ - ['slideLeft', slideLeft], - ['squeezeLeft', squeezeLeft], - ['slideRight', slideRight], - ['squeezeRight', squeezeRight], - ['ready', false], - ], - children: [ - ...children, - child, - ], - style: squeezeLeft, + css: squeezeLeft, connections: [ + // When dragging [gesture, self => { var offset = gesture.get_offset()[1]; // Slide right if (offset >= 0) { - self.setStyle(` + self.setCss(` + margin-top: 0px; margin-bottom: 0px; opacity: 1; transition: none; margin-left: ${Number(offset + startMargin)}px; margin-right: -${Number(offset + startMargin)}px; `); @@ -82,26 +98,31 @@ export default ({ // Slide left else { offset = Math.abs(offset); - self.setStyle(` + self.setCss(` + margin-top: 0px; margin-bottom: 0px; opacity: 1; transition: none; margin-right: ${Number(offset + startMargin)}px; margin-left: -${Number(offset + startMargin)}px; `); } // Put a threshold on if a click is actually dragging - self.get_parent()._dragging = Math.abs(offset) > 10; - - if (widget.window) - widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing')); + widget._dragging = Math.abs(offset) > 10; + widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing')); }, 'drag-update'], + + // On drag end [gesture, self => { // Make it slide in on init - if (!self._ready) { - self.setStyle(slideIn === 'Left' ? slideLeft : slideRight); + if (!widget.ready) { + // Reverse of slideAway, so it started at squeeze, then we go to slide + self.setCss(slideIn === 'Left' ? slideLeft : slideRight); - timeout(500, () => self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`)); - timeout(1000, () => self._ready = true); + timeout(500, () => { + // Then we got to center + self.setCss(defaultStyle); + timeout(500, () => widget.ready = true); + }); return; } @@ -109,35 +130,16 @@ export default ({ // If crosses threshold after letting go, slide away if (Math.abs(offset) > maxOffset) { - // Slide away right - if (offset > 0) { - // Disable inputs during animation - widget.sensitive = false; - - self.setStyle(slideRight); - timeout(500, () => self.setStyle(squeezeRight)); - } - - // Slide away left - else { - // Disable inputs during animation - widget.sensitive = false; - - self.setStyle(slideLeft); - timeout(500, () => self.setStyle(squeezeLeft)); - } - - timeout(1000, () => { - command(); - self.destroy(); - }); + if (offset > 0) + widget.slideAway('Right'); + else + widget.slideAway('Left'); } else { - self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`); - if (widget.window) - widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); + self.setCss(defaultStyle); + widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); - self.get_parent()._dragging = false; + widget._dragging = false; } }, 'drag-end'], diff --git a/devices/wim/config/ags/js/notifications/popup.js b/devices/wim/config/ags/js/notifications/popup.js index 00f0a293..36300992 100644 --- a/devices/wim/config/ags/js/notifications/popup.js +++ b/devices/wim/config/ags/js/notifications/popup.js @@ -1,71 +1,68 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; -import { Box, Window } from 'resource:///com/github/Aylur/ags/widget.js'; +import { Box } from 'resource:///com/github/Aylur/ags/widget.js'; import { interval } from 'resource:///com/github/Aylur/ags/utils.js'; import GLib from 'gi://GLib'; import { Notification } from './base.js'; +import PopupWindow from '../misc/popup.js'; -const Popups = () => Box({ - vertical: true, - properties: [ - ['notify', (box, id) => { - if (id) { - const notif = Notifications.getNotification(id); +const addPopup = (box, id) => { + if (!id) + return; - const NewNotif = Notification({ - notif, - command: () => notif.dismiss(), - }); + const notif = Notifications.getNotification(id); - if (NewNotif) { - // use this instead of add to put it at the top - box.pack_end(NewNotif, false, false, 0); - box.show_all(); - } + const NewNotif = Notification({ + notif, + command: () => notif.dismiss(), + }); + + if (NewNotif) { + // use this instead of add to put it at the top + box.pack_end(NewNotif, false, false, 0); + box.show_all(); + } +}; + +const handleDismiss = (box, id, force = false) => { + const notif = box.children.find(ch => ch._id === id); + if (!notif) + return; + + // If notif isn't hovered or was closed, slide away + if (!notif._hovered || force) { + notif.slideAway('Left'); + return; + } + + // If notif is hovered, delay close + else if (notif._hovered) { + notif.interval = interval(2000, () => { + if (!notif._hovered && notif.interval) { + notif.slideAway('Left'); + + GLib.source_remove(notif.interval); + notif.interval = undefined; + return; } - }], + }); + } +}; - ['dismiss', (box, id, force = false) => { - for (const ch of box.children) { - if (ch._id == id) { - // If notif isn't hovered or was closed, slide away - if (!ch._hovered || force) { - ch.slideAway('Left'); - return; - } - - // If notif is hovered, delay close - else if (ch._hovered) { - ch.interval = interval(2000, () => { - if (!ch._hovered && ch.interval) { - ch.slideAway('Left'); - - GLib.source_remove(ch.interval); - ch.interval = undefined; - return; - } - }); - } - } - } - }], - ], - connections: [ - [Notifications, (box, id) => box._notify(box, id), 'notified'], - [Notifications, (box, id) => box._dismiss(box, id), 'dismissed'], - [Notifications, (box, id) => box._dismiss(box, id, true), 'closed'], - ], -}); - -export default () => Window({ +export default () => PopupWindow({ name: 'notifications', anchor: ['top', 'left'], + visible: true, + transition: 'none', + closeOnUnfocus: 'none', child: Box({ - style: `min-height:1px; - min-width:1px; - padding: 1px;`, - children: [Popups()], + vertical: true, + connections: [ + [Notifications, (box, id) => addPopup(box, id), 'notified'], + [Notifications, (box, id) => handleDismiss(box, id), 'dismissed'], + [Notifications, (box, id) => handleDismiss(box, id, true), 'closed'], + ], }), });